SAMPLE: MFCDISP: Replacing MFC IDispatch ImplementationLast reviewed: June 26, 1997Article ID: Q140616 |
The information in this article applies to:
SUMMARYMFCDISP demonstrates how to replace MFC's IDispatch implementation with a type-library-based IDispatch implementation that uses the OLE system API DispInvoke and DispGetIDsOfNames to implement IDispatch::Invoke and IDispatch::GetIDsOfNames. The following file is available for download from the Microsoft Software Library:
~ Mfcdisp.exe (size: 62436 bytes)For more information about downloading files from the Microsoft Software Library, please see the following article in the Microsoft Knowledge Base:
ARTICLE-ID: Q119591 TITLE : How to Obtain Microsoft Support Files from Online ServicesAfter downloading the file, use the following command to extract the sample and build the appropriate directory structure:
MFCDISP.EXE -d MORE INFORMATIONMFC's implementation of server OLE Automation currently has the following limitations. These limitations can be removed by replacing or modifying MFC's IDispatch implementation with a type-library-based implementation of IDispatch.
Steps to Replace MFC's IDispatch ImplementationIf you do not already know how to create a non-MFC automation server, please see the HELLO sample in the Win32 SDK and the OLE Automation documentation in the Win32 online documentation (\Ole\Ole Automation) before using the following steps. The following instructions use Visual C++ 4.0. However, a type-library- based IDispatch implementation can be added to any MFC application. The instructions build a sample called Test. You can use names appropriate for your project.
library Test { importlib("stdole32.tlb"); [ uuid(C04AADF1-2A82-11CF-84F5-00AA00C006CF), oleautomation, dual ] interface ITest : IDispatch { [id(1), propput] HRESULT TestProperty([in]short nNewValue); [id(1), propget] HRESULT TestProperty([out, retval] short *retval); [id(2)] HRESULT TestMethod([in] short n, [out,retval] short *retval); }; // Primary dispatch interface for CTestDoc /* // Replace this dispinterface with a interface [ uuid(C04AADF1-2A82-11CF-84F5-00AA00C006CF) ] dispinterface ITest { properties: // NOTE - ClassWizard will maintain property information here. // Use extreme caution when editing this section. //{{AFX_ODL_PROP(CTestDoc) //}}AFX_ODL_PROP methods: // NOTE - ClassWizard will maintain method information here. // Use extreme caution when editing this section. //{{AFX_ODL_METHOD(CTestDoc) //}}AFX_ODL_METHOD }; */ [ uuid(943B3F80-CD85-11CE-815A-00AA0060D733) ] coclass CTestDoc { [default] interface ITest; }; //{{AFX_APPEND_ODL}} }; A coclass must be provided if the object is a top-level object (that is, if the object can be created by CoCreateInstance). The UUID of the coclass must be the CLSID of the object. The GUID generated by AppWizard for the dispinterface can be used for the interface that replaces it. Add a locale id for the type library (the sample uses lcid(0x09) for English). New CCmdTarget-derived automation objects can be added using ClassWizard. Edit the .odl file to make the changes after each object is added. (The sample adds automation only to the CDocument- derived class, CTestDoc.) Space must be allocated for the GUIDs that are defined in Itest.h. This is done by creating a separate source file (Guids.cpp) that includes Ole2.h, Initguid.h, and Itest.h. The OLE header file Initguid.h will cause space to be allocated for the GUIDs defined in Itest.h. Make sure that Guids.cpp is not built with a pre-compiled header. To do this, add Guids.cpp to the project. Then change the project settings for the Guids.cpp file. First open the appropriate platform folder in the left pane of the Project Settings dialog box. Select Guids.cpp in the build folder in the left pane of the Project Settings dialog box, click the C/C++ tab, click the Precompiled headers category, and select Not using precompiled headers. Include Itest.h in each source file that uses the GUIDs or interfaces that it defines. The sample includes Itest.h in Test.cpp, Testdoc.h, and Testdoc.cpp.
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(MyDispatch, ITest) STDMETHOD(GetTypeInfoCount)(UINT FAR* pctinfo); STDMETHOD(GetTypeInfo)( UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); STDMETHOD(GetIDsOfNames)( REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames, LCID lcid, DISPID FAR* rgdispid); STDMETHOD(Invoke)( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr); /* ITest methods */ STDMETHOD(put_TestProperty)(short nNewValue); STDMETHOD(get_TestProperty)(short FAR* retval); STDMETHOD(TestMethod)(short n, short FAR* retval); END_INTERFACE_PART(MyDispatch) Add a private class member to the object's class to hold the typeinfo of the interface: private: LPTYPEINFO m_ptinfo; // ITest type information a. An interface map is declared as follows to hook into the object's IUnknown::QueryInterface implementation. When a client asks for IDispatch (late binding/id-binding) or ITest (vtbl-binding) using IUnknown::QueryInterface, the vtbl for the XMyDispatch nested class will be returned.
BEGIN_INTERFACE_MAP(CTestDoc, CDocument) INTERFACE_PART(CTestDoc, IID_IDispatch, MyDispatch) INTERFACE_PART(CTestDoc, IID_ITest, MyDispatch) END_INTERFACE_MAP() Remove the interface map inserted by AppWizard. For example, the sample removes the following interface map from Testdoc.cpp: BEGIN_INTERFACE_MAP(CTestDoc, CDocument) INTERFACE_PART(CTestDoc, IID_ITest, Dispatch) END_INTERFACE_MAP() b. Remove the declaration of the interface IID because Guids.cpp will allocate space for it. For example, the sample removes the following from Testdoc.cpp: static const IID IID_ITest = {0xc04aadf1, 0x2a82, 0x11cf, { 0x84, 0xf5, 0x0, 0xaa, 0x0, 0xc0, 0x6, 0xcf } }; c. Load the typeinfo of the interface from the type library when the object is created. BOOL CTestDoc::OnNewDocument() { HRESULT hr; LPTYPELIB ptlib; if (!CDocument::OnNewDocument()) return FALSE; hr = LoadRegTypeLib(LIBID_Test, 1, 0, 0x09, &ptlib); if (FAILED(hr)) { AfxMessageBox("Can't find type library test.tlb. Re-register \ by running test.exe"); return FALSE; } hr = ptlib->GetTypeInfoOfGuid(IID_ITest, &m_ptinfo); if (FAILED(hr)) { ptlib->Release(); return FALSE; } ptlib->Release(); return TRUE; } d. Release the typeinfo of the interface in the destructor of the object. CTestDoc::~CTestDoc() { m_ptinfo->Release(); AfxOleUnlockApp(); } e. The ITest interface is implemented as follows. Note the use of the METHOD_PROLOGUE macro to gain access to the member functions of the object by using the pThis variable. QueryInterface, AddRef, and Release are delegated to MFC's implementation. IDispatch is implemented using DispGetIDsOfNames and DispInvoke. DispInvoke will call the appropriate automation method or property function. ULONG FAR EXPORT CTestDoc::XMyDispatch::AddRef() { METHOD_PROLOGUE(CTestDoc, MyDispatch) return pThis->ExternalAddRef(); } ULONG FAR EXPORT CTestDoc::XMyDispatch::Release() { METHOD_PROLOGUE(CTestDoc, MyDispatch) return pThis->ExternalRelease(); } STDMETHODIMP CTestDoc::XMyDispatch::QueryInterface(REFIID riid, LPVOID FAR* ppvObj) { METHOD_PROLOGUE(CTestDoc, MyDispatch) return (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj); } STDMETHODIMP CTestDoc::XMyDispatch::GetTypeInfoCount(UINT FAR* pctinfo) { METHOD_PROLOGUE(CTestDoc, MyDispatch) *pctinfo = 1; return NOERROR; } STDMETHODIMP CTestDoc::XMyDispatch::GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) { METHOD_PROLOGUE(CTestDoc, MyDispatch) *pptinfo = NULL; if(itinfo != 0) return ResultFromScode(DISP_E_BADINDEX); pThis->m_ptinfo->AddRef(); *pptinfo = pThis->m_ptinfo; return NOERROR; } STDMETHODIMP CTestDoc::XMyDispatch::GetIDsOfNames( REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames, LCID lcid, DISPID FAR* rgdispid) { METHOD_PROLOGUE(CTestDoc, MyDispatch) return DispGetIDsOfNames(pThis->m_ptinfo, rgszNames, cNames, rgdispid); } STDMETHODIMP CTestDoc::XMyDispatch::Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr) { METHOD_PROLOGUE(CTestDoc, MyDispatch) return DispInvoke( &pThis->m_xMyDispatch, pThis->m_ptinfo, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } STDMETHODIMP CTestDoc::XMyDispatch::get_TestProperty(short *pnRetVal) { METHOD_PROLOGUE(CTestDoc, MyDispatch) *pnRetVal = pThis->m_nTestProperty; return NOERROR; } STDMETHODIMP CTestDoc::XMyDispatch::put_TestProperty(short nNewValue) { METHOD_PROLOGUE(CTestDoc, MyDispatch) pThis->m_nTestProperty = nNewValue; return NOERROR; } STDMETHODIMP CTestDoc::XMyDispatch::TestMethod(short n, short *pnRetVal) { *pnRetVal = n; return NOERROR; }
// Register type library and the interfaces in it AfxOleRegisterTypeLib(AfxGetInstanceHandle(), LIBID_Test, _T("test.TLB")); Testing the Sample ServerRun the sample server, Test.exe, so that it registers itself in the registration database. Then use the Visual Basic files in the vb directory in the sample to control the server. The server will be launched invisible and it doesn't have an automation method to make it visible. Vb.vbp and Vb.frm use late-binding (IDispatch) to control the server with code similar to the following:
Dim o As Object Set o = CreateObject("Test.Document") Value = o.TestProperty o.TestProperty = Value Value = o.TestMethod(99)vbvtbl.vbp, vbvtbl.frm uses vtbl-binding (ITest) to control the server using code similar to the following. Use the Tools/References menu in VB to select the server's type library (Test) before executing this code.
Dim o As ITest Set o = New CTestDoc 'Use the name of the coclass Value = o.TestProperty o.TestProperty = Value Value = o.TestMethod(99) Handling ErrorsAfxThrowOleDispatchException cannot be used to throw exceptions in the automation method or property implementation when a type-library-based IDispatch implementation is used. Instead SetErrorInfo can be used to return rich error information as described in the following article in the Microsoft Knowledge Base:
ARTICLE-ID: Q139073 TITLE : How To Fill EXCEPINFO in IDispatch Implementation |
Additional query words: override
© 1998 Microsoft Corporation. All rights reserved. Terms of Use. |