1. 程式人生 > >COM元件建立例子程式

COM元件建立例子程式

此例子程式既建立了程序內元件,又建立了經常外元件。包括4個工程專案:

DictCtrl為客戶程式工程

Dictionary為程序外元件工程

Dictionary2為程序內元件工程

DictPrxy為代理/存根dll工程

下面我們分別介紹各個工程(詳細的原始碼請下載原始碼自己看,這裡只是介紹一下重點要注意的地方):

DictPrxy工程(代理/存根dll):

首先這個工程定義了介面宣告檔案Dictionary.idl:


import "unknwn.idl";

#define MaxWordLength 32

[
    object, 
    uuid(54BF6568-1007-11D1-B0AA-444553540000),
    pointer_default(unique)
] 
interface IDictionary : IUnknown
{   
	HRESULT Initialize();
	HRESULT LoadLibrary([in, string] WCHAR *pFilename);
	HRESULT InsertWord([in, string] WCHAR *pWord, [in, string] WCHAR *pWordUsingOtherLang);
	HRESULT DeleteWord([in, string] WCHAR *pWord);
	HRESULT LookupWord([in, string] WCHAR *pWord, [out] WCHAR pWordOut[MaxWordLength]);
	HRESULT RestoreLibrary([in, string] WCHAR *pFilename);
	HRESULT FreeLibrary();
};

[
    object, 
    uuid(54BF6569-1007-11D1-B0AA-444553540000),
    pointer_default(unique)
] 
interface ISpellCheck : IUnknown
{   
	HRESULT CheckWord([in, string] WCHAR *pWord, [out] WCHAR pWordOut[MaxWordLength]);
};

根據這個檔案,我們可以生成Dictionary_h.h,Dictionary_i.c,Dictionary_p.c,dlldata.c檔案。

Dictionary_h.h:介面宣告都在這裡面

Dictionary_i.c GUID的定義都在這個檔案裡面

Dictionary_p.c,dlldata.c 生成代理/存根dll依賴這2個檔案

通過這幾個檔案和匯出函式檔案(DictPrxy.def),我們可以生成代理/存根dll。

DictPrxy.def檔案定義如下:

LIBRARY		DictPrxy
DESCRIPTION	'IDictionary Interface Proxy/Stub DLL'

EXPORTS
				DllGetClassObject		@1 PRIVATE
				DllCanUnloadNow			@2 PRIVATE
				GetProxyDllInfo			@3 PRIVATE
				DllRegisterServer		@4 PRIVATE
				DllUnregisterServer		@5 PRIVATE

後面幾個工程都有用到這個工程生成的檔案Dictionary_h.h,Dictionary_i.c

DictCtrl工程(客戶呼叫程式):

如果客戶程式想建立程序外元件,呼叫程式碼如下:

hResult = CoCreateInstance(dictionaryCLSID, NULL,
		CLSCTX_LOCAL_SERVER, IID_IUnknown, (void **)&pUnknown);

如果客戶程式想建立程序內元件,呼叫程式碼如下:

hResult = CoCreateInstance(dictionaryCLSID, NULL,
		CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&pUnknown);

Dictionary工程(程序外元件):

此工程不需要匯出函式,但是也需要註冊/登出,元件程式啟動以後還要註冊類廠物件,元件程式關閉之前還要登出類廠物件。

winmain函式如下:

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	// Initialize the COM Library.
	HRESULT hr = CoInitialize(NULL) ;
	if (FAILED(hr))
	{
		return 0 ;
	}
   
	int retVal = 0;
	// If TRUE, don't loop.
	BOOL bExit = FALSE ;

	// Read the command line.
	char szTokens[] = "-/" ;
	dwMainThreadID = ::GetCurrentThreadId() ;

	char* szToken = strtok(lpCmdLine, szTokens) ; 
	while (szToken != NULL)
	{
		if (_stricmp(szToken, "RegServer") == 0)
		{
			char szModule[1024];
			DWORD dwResult = ::GetModuleFileName((HMODULE)hInstance, szModule, 1024);
			if (dwResult == 0)
				retVal = SELFREG_E_CLASS;
			retVal = RegisterServer(CLSID_Dictionary,
								  szModule, 
								  "Dictionary.Object",
								  "Dictionary Component",
								  NULL);
			// We are done, so exit.
			bExit = TRUE ;
		}
		else if (_stricmp(szToken, "UnregServer") == 0)
		{
			retVal = UnregisterServer(CLSID_Dictionary,
	                        "Dictionary.Object",NULL);
			// We are done, so exit.
			bExit = TRUE ;
		}
		else if (_stricmp(szToken, "Embedding") == 0)
		{
			bExit = FALSE;
			break ;
		}
		szToken = strtok(NULL, szTokens) ;
	}

	if (!bExit)
	{
		// Register all of the class factory.
		CDictionaryFactory::RegisterFactory();

		// Wait for shutdown.
		MSG msg ;
		while (::GetMessage(&msg, 0, 0, 0))
		{
			::DispatchMessage(&msg);
		}

		// Unregister the class factory.
		CDictionaryFactory::UnregisterFactory() ;
	}

	// Uninitialize the COM Library.
	CoUninitialize() ;
	return 0 ;
}

此外還要注意註冊函式要修改一個地方,將註冊的鍵改為:LocalServer32

SetKeyAndValue(szKey, "LocalServer32", szFileName) ;

Dictionary2工程(程序內元件):

程序內元件需要匯出4個函式,主要程式碼如下:


HMODULE g_hModule;
extern ULONG    g_LockNumber;
extern ULONG    g_DictionaryNumber;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		g_hModule = hModule;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

extern "C" const GUID CLSID_Dictionary =
{ 0x54bf6567, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00 } };

extern "C" HRESULT __stdcall DllGetClassObject(const CLSID& clsid, const IID& iid, void **ppv)
{
	if (clsid == CLSID_Dictionary) {

		CAFactory *pFactory = new CAFactory;

		if (pFactory == NULL) {
			return E_OUTOFMEMORY;
		}

		HRESULT result = pFactory->QueryInterface(iid, ppv);

		return result;
	}
	else {
		return CLASS_E_CLASSNOTAVAILABLE;
	}
}

extern "C" HRESULT __stdcall DllCanUnloadNow(void)
{
	if ((g_DictionaryNumber == 0) && (g_LockNumber == 0))
		return S_OK;
	else
		return S_FALSE;
}

//
// Server registration
//
extern "C" HRESULT __stdcall DllRegisterServer()
{
	char szModule[1024];
	DWORD dwResult = ::GetModuleFileName((HMODULE)g_hModule, szModule, 1024);
	if (dwResult == 0)
		return E_FAIL;
	return RegisterServer(CLSID_Dictionary,
		szModule,
		"Dictionary.Object",
		"Dictionary Component",
		NULL);
}


//
// Server unregistration
//
extern "C" HRESULT __stdcall DllUnregisterServer()
{
	return UnregisterServer(CLSID_Dictionary,
		"Dictionary.Object", NULL);
}

此外還要注意註冊函式要修改一個地方,將註冊的鍵改為:InprocServer32

SetKeyAndValue(szKey, "InprocServer32", szFileName) ;

此例子程式可以支援程序內元件和程序外元件同時註冊,組成完以後,登錄檔如下:

InprocServer32為程序內元件註冊的。

LocalServer32為程序外元件註冊的。