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為程序外元件註冊的。