Bagikan melalui


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