[ Home | Syllabus | Course Notes | Assignments | Search]
Chapter 4 of the COM+ book develops a slightly more complex COM object than the previous Student Object and uses a MFC client to access it. This example also customizes visual studio to assist in creating this COM object.
import "unknwn.idl"; [ object,uuid(646803F2-9C2C-4555-B8F4-CF6BE0FF5475) ] interface ISpellChecker : IUnknown{ HRESULT CheckSpelling([in,string] char *word, [out,retval] BOOL *isCorrect); HRESULT UseCustomDictionary([in,string] char *filename); } [ uuid(11792306-C6D6-45a6-9E2E-2C88A990F600) ] library SpellcheckerLib { [ uuid(18469367-346A-447b-9972-53E0F16DB0FB) ] coclass CSpellChecker { interface ISpellChecker; } };
Let's look a little bit at spellcheck_p.c - see definition of proxy/stub
functions prototyped in spellcheck.h
NOTE: the previous example also had a *_p.c file
/* this ALWAYS GENERATED file contains the proxy stub code */ /* File created by MIDL compiler version 5.01.0164 */ /* at Sun Feb 24 18:35:39 2002 */ #include "spellcheck.h" HRESULT STDMETHODCALLTYPE ISpellChecker_CheckSpelling_Proxy( ISpellChecker __RPC_FAR * This, /* [string][in] */ unsigned char __RPC_FAR *word, /* [retval][out] */ BOOL __RPC_FAR *isCorrect) { RPC_MESSAGE _RpcMessage; MIDL_STUB_MESSAGE _StubMsg; NdrProxyGetBuffer(This, &_StubMsg); NdrConformantStringMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, (unsigned char __RPC_FAR *)word, (PFORMAT_STRING) &__MIDL_TypeFormatString.Format[4] ); NdrProxySendReceive(This, &_StubMsg); *isCorrect = *(( BOOL __RPC_FAR * )_StubMsg.Buffer)++; _RetVal = *(( HRESULT __RPC_FAR * )_StubMsg.Buffer)++; } void __RPC_STUB ISpellChecker_CheckSpelling_Stub( IRpcStubBuffer *This, IRpcChannelBuffer *_pRpcChannelBuffer, PRPC_MESSAGE _pRpcMessage, DWORD *_pdwStubPhase) { NdrConformantStringUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, (unsigned char __RPC_FAR * __RPC_FAR *)&word, (PFORMAT_STRING) &__MIDL_TypeFormatString.Format[4], (unsigned char)0 ); _RetVal = (((ISpellChecker*) ((CStdStubBuffer *)This)->pvServerObject)->lpVtbl) -> CheckSpelling( (ISpellChecker *) ((CStdStubBuffer *)This)->pvServerObject, word, isCorrect); NdrStubGetBuffer(This, _pRpcChannelBuffer, &_StubMsg); *(( BOOL __RPC_FAR * )_StubMsg.Buffer)++ = *isCorrect; *(( HRESULT __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; const CINTERFACE_PROXY_VTABLE(5) _ISpellCheckerProxyVtbl = { &IID_ISpellChecker, IUnknown_QueryInterface_Proxy, IUnknown_AddRef_Proxy, IUnknown_Release_Proxy , ISpellChecker_CheckSpelling_Proxy , ISpellChecker_UseCustomDictionary_Proxy };
Implementing the Component CSpellCheckImpl/ISpellChecker
Remember that the ISpellCheckerinterface object is an abstract class because its five functions are pure virtual.
(What are the five functions?)
Therefore we need to derive a implementation class from it.
#include "spellcheck.h" // The file generated from the idl #include <vector> using namespace std; class CSpellCheckImpl : public ISpellChecker { public: CSpellCheckImpl(); ~CSpellCheckImpl(); STDMETHOD(QueryInterface) (REFIID,void ** ); STDMETHOD_(ULONG,AddRef) (void); STDMETHOD_(ULONG,Release) (void); STDMETHOD(CheckSpelling) (unsigned char *word, BOOL *isCorrect); STDMETHOD(UseCustomDictionary) (unsigned char *filename); private: ULONG m_ref; // for COM lifetime management vector<string> m_dictionary; // use STL vector template class };
STDMETHODIMP CSpellCheckImpl::CheckSpelling( unsigned char *word,BOOL *isCorrect) { string str; vector< string >::iterator iter; *isCorrect=FALSE; for (iter=m_dictionary.begin();iter!=m_dictionary.end();iter++) { str=*iter; if (stricmp(str.c_str(),(const char *)word)==0) { *isCorrect=TRUE; break; } } return NOERROR; } STDMETHODIMP CSpellCheckImpl::UseCustomDictionary( unsigned char *filename) { string str; char tempBuffer[255]; ifstream dictionaryFile; dictionaryFile.open((const char *)filename); if (!dictionaryFile.is_open()) return E_DICTIONARYFILENOTFOUND; while (!dictionaryFile.eof()) { dictionaryFile.getline(tempBuffer,255); str= tempBuffer; m_dictionary.push_back(str); } return NOERROR; }
Class Factory COM object
Used by CoCreateInstance, calls CreateInstance.
cclass CSpellFactoryImpl : public IClassFactory { public: CSpellFactoryImpl(); ~CSpellFactoryImpl(); STDMETHOD(QueryInterface) (REFIID, void**ppv); STDMETHOD_(ULONG,AddRef) (void); STDMETHOD_(ULONG,Release) (void); STDMETHOD(CreateInstance) (LPUNKNOWN, REFIID, void **ppv); STDMETHOD(LockServer) (BOOL); private: ULONG m_ref; };
STDMETHODIMP CSpellFactoryImpl::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void** ppv) { HRESULT hr; CSpellCheckImpl *pSpellChecker; pSpellChecker=new CSpellCheckImpl(); hr=pSpellChecker->QueryInterface(riid, ppv); if (SUCCEEDED(hr)) { gObjectCount++; // one more object created by this server (DLL) return NOERROR; } else { delete pSpellChecker; *ppv=NULL; return E_NOINTERFACE; } }
//// code for dealing with DLL management
// in "spelldllfuncts.cpp" LONG gObjectCount=0; LONG gLockCount=0; STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid,void** ppv) { HRESULT hr; CSpellFactoryImpl *pObj; pObj=new CSpellFactoryImpl(); hr=pObj->QueryInterface(riid, ppv); if (FAILED(hr)) { *ppv=NULL; delete pObj; } return hr; } STDAPI DllCanUnloadNow(void) { if (gObjectCount==0 && gLockCount==0) return S_OK; else return S_FALSE; }
//// in classfactory implementation file STDMETHODIMP CSpellFactoryImpl::LockServer(BOOL fLock) { if (fLock) gLockCount++; else gLockCount--; return NOERROR; }
Make the DLL self registering (in stead of using separate ".reg" file. Also supports unregistering.
Need to define the following functions:
HINSTANCE g_hinstDll; const char *g_RegTable[][2] = { { "CLSID\\{57B7A8A0-E4D7-11d0-818D-444553540000}","Spellchecker Object"}, { "CLSID\\{57B7A8A0-E4D7-11d0-818D-444553540000}\\InprocServer32",(const char*)-1}, { "CLSID\\{57B7A8A0-E4D7-11d0-818D-444553540000}\\ProgID","Spellchecker.Object.1"}, { "Spellchecker.Object.1","Spellchecker Object"}, { "Spellchecker.Object.1\\CLSID","{57B7A8A0-E4D7-11d0-818D-444553540000}"} }; STDAPI DllRegisterServer() { HRESULT hr = S_OK; char szFileName[MAX_PATH]; HKEY hkey; GetModuleFileName(g_hinstDll,szFileName,MAX_PATH); int nEntries = sizeof(g_RegTable)/sizeof(*g_RegTable); for (int i=0; SUCCEEDED(hr) && i < nEntries; i++ ) { const char *pszKeyName = g_RegTable[i][0]; const char *pszValue =g_RegTable[i][1]; if (pszValue==(const char *)-1) pszValue=szFileName; long err=RegCreateKey(HKEY_CLASSES_ROOT,pszKeyName,&hkey); if (err == ERROR_SUCCESS) { err = RegSetValueExA(hkey,0,0,REG_SZ,(const BYTE *)pszValue,(strlen(pszValue)+1)); RegCloseKey(hkey); } if (err != ERROR_SUCCESS) { DllUnregisterServer(); hr = SELFREG_E_CLASS; } } return hr; } STDAPI DllUnregisterServer() { HRESULT hr=S_OK; int nEntries = sizeof(g_RegTable)/sizeof(*g_RegTable); for (int i = nEntries-1; i >= 0 ; i-- ) { const char *pszKeyName = g_RegTable[i][0]; long err = RegDeleteKeyA(HKEY_CLASSES_ROOT,pszKeyName); if (err != ERROR_SUCCESS) hr = S_FALSE; } return hr; } BOOL APIENTRY DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH : g_hinstDll = hDLLInst ; // remember your own handle (used for registration) } return TRUE ; }
register the component using the command
regsvr32 spellcheck.dll
to unregister
regsvr32 spellcheck.dll /u
; spellcheck.def EXPORTS DllGetClassObject PRIVATE DllCanUnloadNow PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE
Now let's see how a client can access this COM object:
void CSimplecomclientDlg::OnBrowseButton() { CFileDialog dlg(TRUE); // display a File dialog box if (dlg.DoModal()==IDOK) { m_dictionaryFile=dlg.GetPathName(); UpdateData(FALSE); // force m_dictionaryFile to dialog - experiment } }
if (!AfxOleInit())
AfxMessageBox("Could not initialize COM");
// in simplecomclientDlg.h private: ISpellChecker* pSpeller;
HRESULT hRes; hRes=CoCreateInstance(CLSID_CSpellChecker,NULL,CLSCTX_ALL, IID_ISpellChecker,(void**)&pSpeller); if (FAILED(hRes)) { AfxMessageBox("Could not instantiate object.\n" \ "Check that it is registered"); }
void CSimplecomclientDlg::OnSpellcheckerButton() { BOOL isCorrect; HRESULT hRes; if (NULL != pSpeller)// make sure we have a COM object defined { UpdateData(TRUE); // forces data to move to data variable char *str=m_word.GetBuffer(80); // can't pass MFC objects - get C-style string hRes=pSpeller->CheckSpelling((unsigned char *)str,&isCorrect); if (FAILED(hRes)) AfxMessageBox("Could not invoke CheckSpelling method"); if (isCorrect) AfxMessageBox("Spellcheck complete"); else AfxMessageBox("The word is misspelled"); } else AfxMessageBox("The server failed to start."); } void CSimplecomclientDlg::OnLoadDictionaryButton() { HRESULT hr; if (NULL != pSpeller) { UpdateData(TRUE); hr=pSpeller->UseCustomDictionary( (unsigned char *)m_dictionaryFile.GetBuffer(80)); if (FAILED(hr)) AfxMessageBox("Cannot load dictionary"); } else AfxMessageBox("The server failed to start."); }