Windows Systems Programming: Spring 2002

[ Home | Syllabus | Course Notes | Assignments | Search]


Spell Checker (chapter 4)

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.

Source Code here


IDL File

rainbow.gif (2243 bytes)

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;
	}
};
rainbow.gif (2243 bytes)

 


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

rainbow.gif (2243 bytes)

/* 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
};



rainbow.gif (2243 bytes)

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.

rainbow.gif (2243 bytes)

#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
};

rainbow.gif (2243 bytes)


CheckSpelling/UseCustomDictionary implementation

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

IClassFactory:
A COM object for creating other COM objects of a given type.

Used by CoCreateInstance, calls CreateInstance.

rainbow.gif (2243 bytes)

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;
};

rainbow.gif (2243 bytes)

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;
}

rainbow.gif (2243 bytes)


Registering the Component

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 ; 
}
rainbow.gif (2243 bytes)

 

; spellcheck.def
EXPORTS      
     DllGetClassObject  	PRIVATE
     DllCanUnloadNow    	PRIVATE
     DllRegisterServer  	PRIVATE
     DllUnregisterServer	PRIVATE

 


dll2.gif (20140 bytes)

 

Now let's see how a client can access this  COM object:

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.");
}
 

rainbow.gif (2243 bytes)

 


Copyright chris wild 1999-2002.
For problems or questions regarding this web contact [Dr. Wild].
Last updated: February 24, 2002.