TN039: Implementasi Otomatisasi MFC/OLE
Catatan
Catatan teknis berikut belum diperbarui sejak pertama kali disertakan dalam dokumentasi online. Akibatnya, beberapa prosedur dan topik mungkin kedaluarsa atau salah. Untuk informasi terbaru, disarankan agar Anda mencari topik yang menarik dalam indeks dokumentasi online.
Gambaran Umum Antarmuka OLE IDispatch
Antarmuka IDispatch
adalah sarana di mana aplikasi mengekspos metode dan properti sehingga aplikasi lain seperti Visual BASIC, atau bahasa lain, dapat menggunakan fitur aplikasi. Bagian terpenting dari antarmuka ini adalah IDispatch::Invoke
fungsi . MFC menggunakan "peta pengiriman" untuk mengimplementasikan IDispatch::Invoke
. Peta pengiriman menyediakan informasi implementasi MFC pada tata letak atau "bentuk" kelas turunan Anda CCmdTarget
, sehingga dapat langsung memanipulasi properti objek, atau memanggil fungsi anggota dalam objek Anda untuk memenuhi IDispatch::Invoke
permintaan.
Untuk sebagian besar, ClassWizard dan MFC bekerja sama untuk menyembunyikan sebagian besar detail otomatisasi OLE dari programmer aplikasi. Programmer berkonsentrasi pada fungsionalitas aktual untuk mengekspos dalam aplikasi dan tidak perlu khawatir tentang pipa yang mendasar.
Namun, ada kasus di mana perlu untuk memahami apa yang dilakukan MFC di belakang layar. Catatan ini akan membahas bagaimana kerangka kerja menetapkan DISPIDke fungsi dan properti anggota. Pengetahuan tentang algoritma yang digunakan MFC untuk menetapkan DISPIDhanya diperlukan ketika Anda perlu mengetahui ID, seperti ketika Anda membuat "pustaka jenis" untuk objek aplikasi Anda.
Penetapan MFC DISPID
Meskipun pengguna akhir otomatisasi (pengguna Visual Basic, misalnya), melihat nama aktual properti dan metode yang diaktifkan otomatisasi dalam kode mereka (seperti obj. ShowWindow), implementasi IDispatch::Invoke
tidak menerima nama aktual. Untuk alasan pengoptimalan, ia menerima DISPID, yang merupakan "cookie ajaib" 32-bit yang menjelaskan metode atau properti yang akan diakses. Nilai DISPID ini dikembalikan dari IDispatch
implementasi melalui metode lain, yang disebut IDispatch::GetIDsOfNames
. Aplikasi klien otomatisasi akan memanggil GetIDsOfNames
sekali untuk setiap anggota atau properti yang ingin diaksesnya, dan menyimpannya untuk panggilan nanti ke IDispatch::Invoke
. Dengan cara ini, pencarian string yang mahal hanya dilakukan sekali per penggunaan objek, bukan sekali per IDispatch::Invoke
panggilan.
MFC menentukan DISPIDuntuk setiap metode dan properti berdasarkan dua hal:
Jarak dari bagian atas peta pengiriman (1 relatif)
Jarak peta pengiriman dari kelas yang paling turunan (0 relatif)
DISPID dibagi menjadi dua bagian. LOWORD DISPID berisi komponen pertama, jarak dari bagian atas peta pengiriman. HIWORD berisi jarak dari kelas yang paling turunan. Contohnya:
class CDispPoint : public CCmdTarget
{
public:
short m_x, m_y;
// ...
DECLARE_DISPATCH_MAP()
// ...
};
class CDisp3DPoint : public CDispPoint
{
public:
short m_z;
// ...
DECLARE_DISPATCH_MAP()
// ...
};
BEGIN_DISPATCH_MAP(CDispPoint, CCmdTarget)
DISP_PROPERTY(CDispPoint, "x", m_x, VT_I2)
DISP_PROPERTY(CDispPoint, "y", m_y, VT_I2)
END_DISPATCH_MAP()
BEGIN_DISPATCH_MAP(CDisp3DPoint, CDispPoint)
DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
END_DISPATCH_MAP()
Seperti yang Anda lihat, ada dua kelas, yang keduanya mengekspos antarmuka otomatisasi OLE. Salah satu kelas ini berasal dari kelas lainnya dan dengan demikian memanfaatkan fungsionalitas kelas dasar, termasuk bagian otomatisasi OLE (properti "x" dan "y" dalam hal ini).
MFC akan menghasilkan DISPIDuntuk kelas CDispPoint sebagai berikut:
property X (DISPID)0x00000001
property Y (DISPID)0x00000002
Karena properti tidak berada di kelas dasar, HIWORD DISPID selalu nol (jarak dari kelas yang paling turunan untuk CDispPoint adalah nol).
MFC akan menghasilkan DISPIDuntuk kelas CDisp3DPoint sebagai berikut:
property Z (DISPID)0x00000001
property X (DISPID)0x00010001
property Y (DISPID)0x00010002
Properti Z diberikan DISPID dengan HIWORD nol karena didefinisikan di kelas yang mengekspos properti, CDisp3DPoint. Karena properti X dan Y didefinisikan dalam kelas dasar, HIWORD di DISPID adalah 1, karena kelas di mana properti ini didefinisikan berada pada jarak satu turunan dari kelas yang paling turunan.
Catatan
LOWORD selalu ditentukan oleh posisi di peta, bahkan jika ada entri di peta dengan DISPID eksplisit (lihat bagian berikutnya untuk informasi tentang versi DISP_PROPERTY
_ID makro dan DISP_FUNCTION
).
Fitur Peta Pengiriman MFC Tingkat Lanjut
Ada sejumlah fitur tambahan yang tidak didukung ClassWizard dengan rilis Visual C++ini. ClassWizard masing-masing mendukung DISP_FUNCTION
, DISP_PROPERTY
, dan DISP_PROPERTY_EX
yang menentukan metode, properti variabel anggota, dan mendapatkan/mengatur properti fungsi anggota. Kemampuan ini biasanya semua yang diperlukan untuk membuat sebagian besar server otomatisasi.
Makro tambahan berikut dapat digunakan ketika makro yang didukung ClassWizard tidak memadai: DISP_PROPERTY_NOTIFY
, dan DISP_PROPERTY_PARAM
.
DISP_PROPERTY_NOTIFY — Deskripsi Makro
DISP_PROPERTY_NOTIFY(
theClass,
pszName,
memberName,
pfnAfterSet,
vtPropType)
Parameter
theClass
Nama kelas.
pszName
Nama eksternal properti.
memberName
Nama variabel anggota tempat properti disimpan.
pfnAfterSet
Nama fungsi anggota untuk dipanggil saat properti diubah.
vtPropType
Nilai yang menentukan jenis properti.
Keterangan
Makro ini sama seperti DISP_PROPERTY, kecuali bahwa makro menerima argumen tambahan. Argumen tambahan, pfnAfterSet, harus menjadi fungsi anggota yang tidak mengembalikan apa pun dan tidak mengambil parameter, 'void OnPropertyNotify()'. Ini akan dipanggil setelah variabel anggota dimodifikasi.
DISP_PROPERTY_PARAM — Deskripsi Makro
DISP_PROPERTY_PARAM(
theClass,
pszName,
pfnGet,
pfnSet,
vtPropType,
vtsParams)
Parameter
theClass
Nama kelas.
pszName
Nama eksternal properti.
memberGet
Nama fungsi anggota yang digunakan untuk mendapatkan properti .
memberSet
Nama fungsi anggota yang digunakan untuk mengatur properti.
vtPropType
Nilai yang menentukan jenis properti.
vtsParams
String spasi yang dipisahkan VTS_ untuk setiap parameter.
Keterangan
Sama seperti makro DISP_PROPERTY_EX, makro ini menentukan properti yang diakses dengan fungsi anggota Get and Set terpisah. Namun, makro ini memungkinkan Anda menentukan daftar parameter untuk properti . Ini berguna untuk menerapkan properti yang diindeks atau diparameterkan dengan cara lain. Parameter akan selalu ditempatkan terlebih dahulu, diikuti oleh nilai baru untuk properti . Contohnya:
DISP_PROPERTY_PARAM(CMyObject, "item", GetItem, SetItem, VT_DISPATCH, VTS_I2 VTS_I2)
akan sesuai dengan mendapatkan dan mengatur fungsi anggota:
LPDISPATCH CMyObject::GetItem(short row, short col)
void CMyObject::SetItem(short row, short col, LPDISPATCH newValue)
DISP_XXXX_ID — Deskripsi Makro
DISP_FUNCTION_ID(
theClass,
pszName,
dispid,
pfnMember,
vtRetVal,
vtsParams)
DISP_PROPERTY_ID(
theClass,
pszName,
dispid,
memberName,
vtPropType)
DISP_PROPERTY_NOTIFY_ID(
theClass,
pszName,
dispid,
memberName,
pfnAfterSet,
vtPropType)
DISP_PROPERTY_EX_ID(
theClass,
pszName,
dispid,
pfnGet,
pfnSet,
vtPropType)
DISP_PROPERTY_PARAM_ID(
theClass,
pszName,
dispid,
pfnGet,
pfnSet,
vtPropType,
vtsParams)
Parameter
theClass
Nama kelas.
pszName
Nama eksternal properti.
dispid
DISPID tetap untuk properti atau metode.
pfnGet
Nama fungsi anggota yang digunakan untuk mendapatkan properti .
pfnSet
Nama fungsi anggota yang digunakan untuk mengatur properti.
memberName
Nama variabel anggota untuk dipetakan ke properti
vtPropType
Nilai yang menentukan jenis properti.
vtsParams
String spasi yang dipisahkan VTS_ untuk setiap parameter.
Keterangan
Makro ini memungkinkan Anda menentukan DISPID alih-alih membiarkan MFC secara otomatis menetapkannya. Makro tingkat lanjut ini memiliki nama yang sama kecuali ID ditambahkan ke nama makro (misalnya DISP_PROPERTY_ID) dan ID ditentukan oleh parameter yang ditentukan tepat setelah parameter pszName . Lihat AFXDISP. H untuk informasi selengkapnya tentang makro ini. Entri _ID harus ditempatkan di akhir peta pengiriman. Mereka akan memengaruhi pembuatan DISPID otomatis dengan cara yang sama seperti versi makro yang tidak _ID akan (DISPIDditentukan oleh posisi). Contohnya:
BEGIN_DISPATCH_MAP(CDisp3DPoint, CCmdTarget)
DISP_PROPERTY(CDisp3DPoint, "y", m_y, VT_I2)
DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
DISP_PROPERTY_ID(CDisp3DPoint, "x", 0x00020003, m_x, VT_I2)
END_DISPATCH_MAP()
MFC akan menghasilkan DISPID untuk kelas CDisp3DPoint sebagai berikut:
property X (DISPID)0x00020003
property Y (DISPID)0x00000002
property Z (DISPID)0x00000001
Menentukan DISPID tetap berguna untuk mempertahankan kompatibilitas mundur ke antarmuka pengiriman yang ada sebelumnya, atau untuk menerapkan metode atau properti tertentu yang ditentukan sistem (biasanya ditunjukkan oleh DISPID negatif, seperti koleksi DISPID_NEWENUM).
Mengambil Antarmuka IDispatch untuk COleClientItem
Banyak server akan mendukung otomatisasi dalam objek dokumen mereka, bersama dengan fungsionalitas server OLE. Untuk mendapatkan akses ke antarmuka otomatisasi ini, perlu untuk langsung mengakses COleClientItem::m_lpObject
variabel anggota. Kode di bawah ini akan mengambil IDispatch
antarmuka untuk objek yang berasal dari COleClientItem
. Anda dapat menyertakan kode di bawah ini dalam aplikasi Jika Anda menemukan fungsionalitas ini yang diperlukan:
LPDISPATCH CMyClientItem::GetIDispatch()
{
ASSERT_VALID(this);
ASSERT(m_lpObject != NULL);
LPUNKNOWN lpUnk = m_lpObject;
Run(); // must be running
LPOLELINK lpOleLink = NULL;
if (m_lpObject->QueryInterface(IID_IOleLink,
(LPVOID FAR*)&lpOleLink) == NOERROR)
{
ASSERT(lpOleLink != NULL);
lpUnk = NULL;
if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
{
TRACE0("Warning: Link is not connected!\n");
lpOleLink->Release();
return NULL;
}
ASSERT(lpUnk != NULL);
}
LPDISPATCH lpDispatch = NULL;
if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch) != NOERROR)
{
TRACE0("Warning: does not support IDispatch!\n");
return NULL;
}
ASSERT(lpDispatch != NULL);
return lpDispatch;
}
Antarmuka pengiriman yang dikembalikan dari fungsi ini kemudian dapat digunakan secara langsung atau dilampirkan ke COleDispatchDriver
untuk akses jenis aman. Jika Anda menggunakannya secara langsung, pastikan Anda memanggil anggotanya Release
ketika melalui dengan penunjuk ( COleDispatchDriver
destruktor melakukan ini secara default).
Baca juga
Catatan Teknis menurut Angka
Catatan Teknis menurut Kategori