Aracılığıyla paylaş


TN065: OLE Otomasyon Sunucuları için Çift Arabirim Desteği

Dekont

Aşağıdaki teknik not, çevrimiçi belgelere ilk kez eklendiğinden beri güncelleştirilmemiştir. Sonuç olarak, bazı yordamlar ve konular güncel olmayabilir veya yanlış olabilir. En son bilgiler için, çevrimiçi belge dizininde ilgilendiğiniz konuyu aramanız önerilir.

Bu not, MFC tabanlı OLE Otomasyonu sunucu uygulamasına çift arabirim desteği eklemeyi açıklar. ACDUAL örneği çift arabirim desteğini gösterir ve bu nottaki örnek kod ACDUAL'dan alınmıştır. Bu notta açıklanan DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART ve IMPLEMENT_DUAL_ERRORINFO gibi makrolar ACDUAL örneğinin bir parçasıdır ve MFCDUAL.H dosyasında bulunabilir.

İkili Arabirimler

OLE Otomasyonu bir IDispatch arabirim, VTBL arabirimi veya çift arabirim (her ikisini de kapsayan) uygulamanıza olanak tanısa da, Microsoft kullanıma sunulan tüm OLE Otomasyonu nesneleri için çift arabirimler uygulamanızı kesinlikle önerir. Çift arabirimler yalnızca veya yalnızca VTBL arabirimlerine göre IDispatchönemli avantajlara sahiptir:

  • Bağlama, VTBL arabirimi aracılığıyla derleme zamanında veya üzerinden IDispatchçalışma zamanında gerçekleşebilir.

  • VTBL arabirimini kullanabilen OLE Otomasyonu denetleyicileri gelişmiş performanstan yararlanabilir.

  • Arabirimini kullanan IDispatch mevcut OLE Otomasyonu denetleyicileri çalışmaya devam edecektir.

  • VTBL arabirimini C++'tan çağırmak daha kolaydır.

  • Visual Basic nesne desteği özellikleriyle uyumluluk için çift arabirimler gereklidir.

CCmdTarget Tabanlı Sınıfa Çift Arabirim Desteği Ekleme

Çift arabirim, gerçekten yalnızca 'den IDispatchtüretilen özel bir arabirimdir. Tabanlı bir CCmdTargetsınıfta çift arabirim desteği uygulamanın en basit yolu, önce MFC ve ClassWizard kullanarak sınıfınızda normal dağıtım arabirimini uygulamak, ardından özel arabirimi daha sonra eklemektir. Çoğunlukla, özel arabirim uygulamanız MFC IDispatch uygulamasına geri döner.

İlk olarak, nesneleriniz için çift arabirim tanımlamak üzere sunucunuzun ODL dosyasını değiştirin. Çift arabirim tanımlamak için, Visual C++ sihirbazlarının oluşturduğu deyim yerine DISPINTERFACE bir arabirim deyimi kullanmanız gerekir. Var olan DISPINTERFACE deyimi kaldırmak yerine yeni bir arabirim deyimi ekleyin. Formu koruyarak DISPINTERFACE , nesnenize özellik ve yöntemler eklemek için ClassWizard'ı kullanmaya devam edebilirsiniz, ancak arabirim deyiminize eşdeğer özellikleri ve yöntemleri eklemeniz gerekir.

Çift arabirim için bir arabirim deyimi OLEAUTOMATION ve DUAL özniteliklerine sahip olmalı ve arabiriminden IDispatchtüretilmelidir. GUIDGEN örneğini kullanarak çift arabirim için bir IID oluşturabilirsiniz:

[ uuid(0BDD0E81-0DD7-11cf-BBA8-444553540000), // IID_IDualAClick
    oleautomation,
    dual
]
interface IDualAClick : IDispatch
    {
    };

Arabirim deyimini hazırladıktan sonra, yöntemler ve özellikler için girdiler eklemeye başlayın. çift arabirimler için, çift arabirimdeki yöntemlerinizin ve özellik erişimci işlevlerinizin bir HRESULT döndürmesi ve dönüş değerlerini öznitelikleri [retval,out]ile parametre olarak geçirmesi için parametre listelerini yeniden düzenlemeniz gerekir. Özellikler için aynı kimliğe sahip hem okuma (propget) hem de yazma (propput) erişim işlevi eklemeniz gerektiğini unutmayın. Örneğin:

[propput, id(1)] HRESULT text([in] BSTR newText);
[propget, id(1)] HRESULT text([out, retval] BSTR* retval);

Yöntemleriniz ve özellikleriniz tanımlandıktan sonra, coclass deyiminizdeki arabirim deyimine bir başvuru eklemeniz gerekir. Örnek:

[ uuid(4B115281-32F0-11cf-AC85-444553540000) ]
coclass Document
{
    dispinterface IAClick;
    [default] interface IDualAClick;
};

ODL dosyanız güncelleştirildikten sonra, nesne sınıfınızdaki çift arabirim için bir uygulama sınıfı tanımlamak ve MFC'nin mekanizmasında ilgili girişleri yapmak için MFC'nin QueryInterface arabirim eşleme mekanizmasını kullanın. ODL'nin INTERFACE_PART arabirim deyimindeki her giriş için blokta bir girdiye ve dağıtım arabirimine yönelik girdilere ihtiyacınız vardır. propput özniteliğine sahip her ODL girdisinin adlı put_propertynamebir işleve ihtiyacı vardır. propget özniteliğine sahip her girdinin adlı get_propertynamebir işleve ihtiyacı vardır.

İkili arabiriminiz için bir uygulama sınıfı tanımlamak için nesne sınıfı tanımınıza bir DUAL_INTERFACE_PART blok ekleyin. Örnek:

BEGIN_DUAL_INTERFACE_PART(DualAClick, IDualAClick)
    STDMETHOD(put_text)(THIS_ BSTR newText);
    STDMETHOD(get_text)(THIS_ BSTR FAR* retval);
    STDMETHOD(put_x)(THIS_ short newX);
    STDMETHOD(get_x)(THIS_ short FAR* retval);
    STDMETHOD(put_y)(THIS_ short newY);
    STDMETHOD(get_y)(THIS_ short FAR* retval);
    STDMETHOD(put_Position)(THIS_ IDualAutoClickPoint FAR* newPosition);
    STDMETHOD(get_Position)(THIS_ IDualAutoClickPoint FAR* FAR* retval);
    STDMETHOD(RefreshWindow)(THIS);
    STDMETHOD(SetAllProps)(THIS_ short x, short y, BSTR text);
    STDMETHOD(ShowWindow)(THIS);
END_DUAL_INTERFACE_PART(DualAClick)

MFC'nin QueryInterface mekanizmasına çift arabirimi bağlamak için arabirim eşlemesine bir INTERFACE_PART giriş ekleyin:

BEGIN_INTERFACE_MAP(CAutoClickDoc, CDocument)
    INTERFACE_PART(CAutoClickDoc, DIID_IAClick, Dispatch)
    INTERFACE_PART(CAutoClickDoc, IID_IDualAClick, DualAClick)
END_INTERFACE_MAP()

Ardından, arabiriminin uygulamasını doldurmanız gerekir. Çoğunlukla, mevcut MFC IDispatch uygulamasına temsilci seçebileceksiniz. Örnek:

STDMETHODIMP_(ULONG) CAutoClickDoc::XDualAClick::AddRef()
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) CAutoClickDoc::XDualAClick::Release()
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    return pThis->ExternalRelease();
}

STDMETHODIMP CAutoClickDoc::XDualAClick::QueryInterface(
    REFIID iid,
    LPVOID* ppvObj)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP CAutoClickDoc::XDualAClick::GetTypeInfoCount(
    UINT FAR* pctinfo)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
    ASSERT(lpDispatch != NULL);
    return lpDispatch->GetTypeInfoCount(pctinfo);
}

STDMETHODIMP CAutoClickDoc::XDualAClick::GetTypeInfo(
    UINT itinfo,
    LCID lcid,
    ITypeInfo FAR* FAR* pptinfo)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
    ASSERT(lpDispatch != NULL);

    return lpDispatch->GetTypeInfo(itinfo, lcid, pptinfo);
}

STDMETHODIMP CAutoClickDoc::XDualAClick::GetIDsOfNames(
    REFIID riid,
    OLECHAR FAR* FAR* rgszNames,
    UINT cNames,
    LCID lcid,
    DISPID FAR* rgdispid)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
    ASSERT(lpDispatch != NULL);

    return lpDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
}

STDMETHODIMP CAutoClickDoc::XDualAClick::Invoke(
    DISPID dispidMember,
    REFIID riid,
    LCID lcid,
    WORD wFlags,
    DISPPARAMS FAR* pdispparams,
    VARIANT FAR* pvarResult,
    EXCEPINFO FAR* pexcepinfo,
    UINT FAR* puArgErr)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
    ASSERT(lpDispatch != NULL);

    return lpDispatch->Invoke(dispidMember, riid, lcid,
        wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
}

Nesnenizin yöntemleri ve özellik erişimci işlevleri için uygulamayı doldurmanız gerekir. Yönteminiz ve özellik işlevleriniz genellikle ClassWizard kullanılarak oluşturulan yöntemlere geri temsilci atayabilir. Ancak, değişkenlere doğrudan erişmek için özellikleri ayarlarsanız, değeri değişkene almak/koymak için kodu yazmanız gerekir. Örnek:

STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    // MFC automatically converts from Unicode BSTR to
    // Ansi CString, if necessary...
    pThis->m_str = newText;
    return NOERROR;
}

STDMETHODIMP CAutoClickDoc::XDualAClick::get_text(BSTR* retval)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    // MFC automatically converts from Ansi CString to
    // Unicode BSTR, if necessary...
    pThis->m_str.SetSysString(retval);
    return NOERROR;
}

İkili Arabirim İşaretçilerini Geçirme

Çift arabirim işaretçinizi geçirmek, özellikle çağırmanız CCmdTarget::FromIDispatchgerekiyorsa basit değildir. FromIDispatch yalnızca MFC'nin IDispatch işaretçilerinde çalışır. Bu sorunu geçici olarak düzeltmenin bir yolu, MFC tarafından ayarlanan özgün IDispatch işaretçiyi sorgulamak ve bu işaretçiyi ihtiyacı olan işlevlere geçirmektir. Örnek:

STDMETHODIMP CAutoClickDoc::XDualAClick::put_Position(
    IDualAutoClickPoint FAR* newPosition)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    LPDISPATCH lpDisp = NULL;
    newPosition->QueryInterface(IID_IDispatch, (LPVOID*)&lpDisp);
    pThis->SetPosition(lpDisp);
    lpDisp->Release();
    return NOERROR;
}

bir işaretçiyi çift arabirim yönteminden geri geçirmeden önce, bunu MFC IDispatch işaretçisinden çift arabirim işaretçinize dönüştürmeniz gerekebilir. Örnek:

STDMETHODIMP CAutoClickDoc::XDualAClick::get_Position(
    IDualAutoClickPoint FAR* FAR* retval)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    LPDISPATCH lpDisp;
    lpDisp = pThis->GetPosition();
    lpDisp->QueryInterface(IID_IDualAutoClickPoint, (LPVOID*)retval);
    return NOERROR;
}

Uygulamanın Tür Kitaplığını Kaydetme

AppWizard, OLE Automation sunucu uygulamasının tür kitaplığını sisteme kaydetmek için kod oluşturmaz. Tür kitaplığını kaydetmenin başka yolları da olsa, uygulamanın OLE türü bilgilerini güncelleştirirken ( yani uygulama tek başına çalıştırıldığında) tür kitaplığını kaydetmesini sağlamak uygundur.

Uygulama tek başına çalıştırıldığında uygulamanın tür kitaplığını kaydetmek için:

  • AFXCTL'yi dahil edin. Standart dosyanızdaki H, STDAFX üst bilgi dosyasını içerir. H, işlevinin tanımına AfxOleRegisterTypeLib erişmek için.

  • Uygulamanızın InitInstance işlevinde çağrısı bulun COleObjectFactory::UpdateRegistryAll. Bu çağrının ardından, türü kitaplığınıza karşılık gelen LIBID değerini ve tür kitaplığınızın adını belirten öğesine bir çağrı AfxOleRegisterTypeLibekleyin:

    // When a server application is launched stand-alone, it is a good idea
    // to update the system registry in case it has been damaged.
    m_server.UpdateRegistry(OAT_DISPATCH_OBJECT);
    
    COleObjectFactory::UpdateRegistryAll();
    
    // DUAL_SUPPORT_START
        // Make sure the type library is registered or dual interface won't work.
        AfxOleRegisterTypeLib(AfxGetInstanceHandle(),
            LIBID_ACDual,
            _T("AutoClik.TLB"));
    // DUAL_SUPPORT_END
    

Proje Derleme Ayarlar Tür Kitaplığı Değişikliklerine Uyum Sağlamak için Değiştirme

Bir projenin derleme ayarlarını, tür kitaplığı yeniden oluşturulduğunda MkTypLib tarafından UUID tanımları içeren bir üst bilgi dosyası oluşturulacak şekilde değiştirmek için:

  1. Oluştur menüsünde Ayarlar'e tıklayın ve her yapılandırma için dosya listesinden ODL dosyasını seçin.

  2. OLE Türleri sekmesine tıklayın ve Çıktı üst bilgisi dosya adı alanında bir dosya adı belirtin. MkTypLib var olan tüm dosyaların üzerine yazacağından, projeniz tarafından henüz kullanılmayan bir dosya adı kullanın. Derleme Ayarlar iletişim kutusunu kapatmak için Tamam'a tıklayın.

MkTypLib tarafından oluşturulan üst bilgi dosyasından UUID tanımlarını projenize eklemek için:

  1. MkTypLib tarafından oluşturulan üst bilgi dosyasını standart ekleme üst bilgi dosyanıza ekleyin( stdafx.h).

  2. INITIIDS adlı yeni bir dosya oluşturun. CPP'yi seçin ve projenize ekleyin. Bu dosyaya, OLE2'yi ekledikten sonra MkTypLib tarafından oluşturulan üst bilgi dosyanızı ekleyin. H ve INITGUID. H:

    // initIIDs.c: defines IIDs for dual interfaces
    // This must not be built with precompiled header.
    #include <ole2.h>
    #include <initguid.h>
    #include "acdual.h"
    
  3. Oluştur menüsünde Ayarlar'e tıklayın ve INITIIDS'i seçin. Her yapılandırma için dosya listesinden CPP.

  4. C++ sekmesine tıklayın, Önceden Derlenmiş Üst Bilgiler kategorisine tıklayın ve Önceden derlenmiş üst bilgileri kullanma radyo düğmesini seçin. Derleme Ayarlar iletişim kutusunu kapatmak için Tamam'a tıklayın.

Tür Kitaplığında Doğru Nesne Sınıf Adını Belirtme

Visual C++ ile gönderilen sihirbazlar ole-creatable sınıfları için sunucunun ODL dosyasındaki ortak sınıfı belirtmek üzere uygulama sınıfı adını yanlış kullanıyor. Bu işe yarasa da, uygulama sınıfı adı büyük olasılıkla nesnenizin kullanıcılarının kullanmasını istediğiniz sınıf adı değildir. Doğru adı belirtmek için ODL dosyasını açın, her coclass deyimini bulun ve uygulama sınıfı adını doğru dış adla değiştirin.

Coclass deyimi değiştirildiğinde, MkTypLib tarafından oluşturulan üst bilgi dosyasındaki CLSID'nindeğişken adlarının buna göre değişeceğini unutmayın. Yeni değişken adlarını kullanmak için kodunuzu güncelleştirmeniz gerekir.

Özel Durumları ve Otomasyon Hata Arabirimlerini İşleme

Otomasyon nesnenizin yöntemleri ve özellik erişimci işlevleri özel durumlar oluşturabilir. Bu durumda, bunları çift arabirimli uygulamanızda işlemeniz ve özel durum hakkındaki bilgileri OLE Otomasyonu hata işleme arabirimi IErrorInfoaracılığıyla denetleyiciye geri iletmeniz gerekir. Bu arabirim hem hem de IDispatch VTBL arabirimleri aracılığıyla ayrıntılı, bağlamsal hata bilgileri sağlar. Bir hata işleyicisinin kullanılabilir olduğunu belirtmek için arabirimini ISupportErrorInfo uygulamanız gerekir.

Hata işleme mekanizmasını göstermek için, standart dağıtım desteğini uygulamak için kullanılan ClassWizard tarafından oluşturulan işlevlerin özel durumlar oluşturduğunu varsayın. MFC'nin uygulaması IDispatch::Invoke genellikle bu özel durumları yakalar ve bunları çağrı aracılığıyla döndürülen bir EXCEPTINFO yapısına Invoke dönüştürür. Ancak VTBL arabirimi kullanıldığında özel durumları kendiniz yakalamak sizin sorumluluğunuzdadır. Çift arabirimli yöntemlerinizi koruma örneği olarak:

STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
    METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
    TRY_DUAL(IID_IDualAClick)
    {
        // MFC automatically converts from Unicode BSTR to
        // Ansi CString, if necessary...
        pThis->m_str = newText;
        return NOERROR;
    }
    CATCH_ALL_DUAL
}

CATCH_ALL_DUAL bir özel durum oluştuğunda doğru hata kodunu döndürmeyi üstlenir. CATCH_ALL_DUAL arabirimini kullanarak bir MFC özel durumunu OLE Otomasyonu hata işleme bilgilerine ICreateErrorInfo dönüştürür. (Örnek CATCH_ALL_DUAL bir makro MFCDUAL dosyasındadır. ACDUAL örneğinde H. özel durumları işlemek için çağıran işlev, DualHandleExceptionMFCDUAL dosyasındadır. CPP.) CATCH_ALL_DUAL, oluşan özel durum türüne göre döndürülecek hata kodunu belirler:

  • COleDispatchException - Bu durumda, HRESULT aşağıdaki kod kullanılarak oluşturulur:

    hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, (e->m_wCode + 0x200));
    

    Bu, özel duruma neden olan arabirime özgü bir HRESULT oluşturur. Hata kodu, standart OLE arabirimleri için sistem tanımlı HRESULTs ile çakışmaları önlemek için 0x200 tarafından uzaktır.

  • CMemoryException - Bu durumda döndürülür E_OUTOFMEMORY .

  • Başka bir özel durum - Bu durumda döndürülür E_UNEXPECTED .

OLE Otomasyonu hata işleyicisinin kullanıldığını belirtmek için arabirimini de uygulamanız ISupportErrorInfo gerekir.

İlk olarak, otomasyon sınıfı tanımınıza desteklediğini ISupportErrorInfogöstermek için kod ekleyin.

İkinci olarak, uygulama sınıfını MFC'nin QueryInterface mekanizmasıyla ilişkilendirmek ISupportErrorInfo için otomasyon sınıfınızın arabirim eşlemesine kod ekleyin. deyimi INTERFACE_PART için ISupportErrorInfotanımlanan sınıfla eşleşir.

Son olarak, desteklemek ISupportErrorInfoiçin tanımlanan sınıfını uygulayın.

(ACDUAL örneği, bu üç adımı gerçekleştirmeye yardımcı olacak üç makro içerir: DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, ve IMPLEMENT_DUAL_ERRORINFO, tümü MFCDUAL.H içinde yer alır.)

Aşağıdaki örnek, desteklemek ISupportErrorInfoiçin tanımlanmış bir sınıf uygular. CAutoClickDocotomasyon sınıfınızın adıdır ve IID_IDualAClick OLE Otomasyonu hata nesnesi aracılığıyla bildirilen hataların kaynağı olan arabirimin IID değeridir:

STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::AddRef()
{
    METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
    return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::Release()
{
    METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
    return pThis->ExternalRelease();
}

STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::QueryInterface(
    REFIID iid,
    LPVOID* ppvObj)
{
    METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
    return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::InterfaceSupportsErrorInfo(
    REFIID iid)
{
    METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
    return (iid == IID_IDualAClick) S_OK : S_FALSE;
}

Ayrıca bkz.

Sayıya Göre Teknik Notlar
Kategoriye Göre Teknik Notlar