分类: com 2011-12-15 22:53 66人阅读 收藏 举报
第7章
相信有了前六章的知识积累,学些以后的章节将会很顺利。本章实现了一个真正的COM组件,并通过client客户端来使用这个组件。
本章将介绍类厂,类厂是能够创建其他组件的组件,CoCreateInstance函数也是按照一定方法通过类厂来创建组件的。
CoCreateInstance
CoCreateInstance函数是COM库的函数,函数原型如下
- HRESULT __stdcall CoCreateInstance(const CLSID &clsid,
- IUnknown *pIUnkownOuter,
- DWORD dwClsContext,
- const IID &iid,
- void **ppv);
其中前四个是输入参数,最后一个是输出参数。第一个参数clsid是所创建组件的CLSID,第二个参数pIUnkownOuter是聚合组件需要用的,将在第8章介绍,第三个参数dwClsContext是限定所创建组件的执行上下文,第四个参数iid是所创建组件的接口的IID,第五个参数ppv将返回该接口的指针。
dwClsContext
dwClsContext可以控制所创建是与客户在相同的进程中运行,还是在不同的进程中运行,或者是在另外一台机器上运行。此参数可以是如下值的组合
CLSCTX_INPROC_SERVER 客户希望创建在同一进程中运行的组件,因此组件必须是在DLL中实现。
CLSCTX_INPROC_HANDLER 客户希望创建进程中处理器。
CLSCTX_LOCAL_SERVER 客户希望创建一个在同一机器上的另外一个进程中运行的组件。组件是由EXE实现的。
CLSCTX_REMOTE_SERVER 客户希望创建一个在远程机器上运行的组件。分布式COM组件。
在OBJBASE.H中定义了一些上述值的组合。
CoCreateInstance的具有一定的不灵活性,解决问题的办法是使用专门用于创建所需组件的组件,这个组件就是类厂。
类厂
某个特定的类厂可以创建某个特定CLSID相对应的组件,客户可以通过类厂提供的接口来对组件的创建过程进行控制。客户使用CoCreateInstance所创建的组件实际上是通过类厂的IClassFactory创建的,使用类厂创建组件的步骤是首先创建类厂,然后使用IClassFactory创建所需的组件。
创建类厂
COM库函数CoGetClassObject创建同某个CLSID相应的类厂。函数原型如下
- HRESULT __stdcall CoGetClassObject(const CLSID &clsid,
- DWORD dwClsContext,
- COSERVERINFO *pServerInfo,
- const IID &iid,
- void **ppv);
同CoCreateInstance非常相似。第一个参数const CLSID&待创建组件的CLSID,第二个参数DWORD dwClsContext是待创建组件的执行上下文,第三个参数COSERVERINFO*用于远程组件的访问,将在第10章讨论,第四个参数const IID&是IClassFactory接口的IID,第五个参数返回IClassFactory接口的指针。
创建组件
IClassFactory
大多数组件是使用IClassFactory来创建的,原型如下
- interface IClassFactory:IUnknown
- {
- HRESULT __stdcall CreateInstance(IUnkown *pUnknownOuter, const IID &id, void **ppv);
- HRESULT __stdcall LockServer(BOOL bLock);
- };
IClassFactory::CreateInstance函数的第一个参数IUnknown*是组件聚合使用的,将在第8章介绍,后两个参数跟CoCreateInstance后两个参数作用相同,将在创建组件的
同时返回此组件的某个接口指针。可以看到IClassFactory::CreateInstance并没有接收一个CLSID参数,这意味着此函数将只能创建同某个CLSID——即传给CoGetClassObject的参数CLSID相应的组件。
在两种情况下使用创建类厂再创建组件的方法,而不是直接使用CoCreateInstance的方法直接创建组件
(1)想使用IClassFactory2来创建组件。IClassFactory2是Microsoft定义的另外一个接口,此接口在IClassFactory的基础上增加了获取组件接口的许可权限功能。
(2)需要创建一个组件的多个实例。这样只需创建相应的类厂一次,而CoCreateInstance需要为每一个组件实例分别创建并释放相应的类厂。
类厂的特性
(1)类厂将只能给你创建同某个CLSID相应的组件。
(2)与某个特定CLSID相应的类厂是由组件开发人员来实现的。大多数情况下,类厂组件包含在它所创建的组件的相同的DLL中。
类厂的创建
客户通过CoGetClassObject来创建类厂,这就需要在DLL中实现一个特定的函数,此函数名为DllGetClassObject,函数原型如下
- STDAPI DllGetClassObject(const CLSID &clsid, const IID &iid, void **ppv);
函数的三个参数同CoGetClassObject中参数的意义相同。
通过类厂来创建组件的示意图如下,COM库函数CoGetClassObject将根据传入参数CLSID查询注册表,装载组件所在的DLL库。
CFactory只不过是另外一个组件而已,它也跟其他组件一样实现了IUnknown接口。
IClassFactory::CreateInstance和DllGetClassObject的实现是相同的,这两个函数都将创建一个组件然后向它查询某个接口。
组件的注册
实现组件的DLL中输出四个函数,除了DllGetClassObject外,DllRegisterServer和DllUnregisterServer将用于组件在注册表中注册和取消注册(链接的时候需要链接Advapi32.lib),调用regsvr32来完成注册和取消。
类厂的复用
在设计类厂和组件的时候,可以做到只用一个类厂的实现来完成所有组件的创建。将在第9章中实现。但即使是这样,类厂CFactory的一个实例也仅能创建一个同某个CLSID相应的组件。
DLL的卸载
COM库中实现了一个CoFreeUnusedLibraries的函数,以释放那些不再需要使用的DLL库所占用的内存,由组件的客户进行调用。
DllCanUnloadNow函数
也是在实现组件的DLL中的输出函数,CoFreeUnusedLibraries函数将调用DllCanUnloadNow函数,以询问Dll是否可以被卸载。代码中g_lComponents的作用就是这个,可以看到IClassFactory::CreateInstance也就是说在创建组件的时候,组件的构造函数都可以将g_lComponents增大,组件的析构函数可以将g_lComponents的值减小。若g_lComponets值为0,CoFreeUnusedLibraries可以将组件的DLL卸载掉。
LockServer函数
使用g_lComponents只是对DLL中的组件进行了记数,另外一个组件CFactory并没有记数。对类厂的记数使用了IFactory::LockServer函数,组件Server内部设置了另外一个与g_IComponents不同的计数值进行计数。(将在第10章进行讨论,主要原因是因为第10章将会讲到的进程外组件(exe实现的)的类厂无法像进程中的组件(dll实现的)一样方便的进行记数。)
本章代码
组件端:
cmpnt.cpp
-
-
-
-
-
- #include <objbase.h>
- #include "iface.h" //interface declarations
- #include "Registry.h" //Registry helper function
- #include <iostream>
- #include <string>
- using namespace std;
-
-
- void trace(string msg)
- {
- cout<<msg<<endl;
- }
-
-
-
- static HMODULE g_hModule = NULL;
- static long g_lComponent = 0;
- static long g_lServerLocks = 0;
-
-
- const char g_szFriendlyName[] = "InsideCOM Chapter 7 Example";
-
-
- const char g_szVerIndProgID[] = "InsideCOM.Chap07";
-
-
- const char g_szProgID[] = "InsideCOM.Chap07.1";
-
-
- class CA:public IX, public IY
- {
- public:
-
- virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
- virtual ULONG __stdcall AddRef();
- virtual ULONG __stdcall Release();
- virtual void __stdcall Fx() {cout<<"Fx"<<endl;}
- virtual void __stdcall Fy() {cout<<"Fy"<<endl;}
- CA();
- ~CA();
- protected:
- long m_cRef;
- };
-
- CA::CA()
- {
- m_cRef = 1;
-
- InterlockedIncrement(&g_lComponent);
- }
-
- CA::~CA()
- {
- InterlockedDecrement(&g_lComponent);
- trace("Component:destroy self");
- }
-
-
- HRESULT __stdcall CA::QueryInterface(const IID &iid, void **ppv)
- {
- if(iid == IID_IUnknown)
- {
- *ppv = static_cast<IX*>(this);
- }
- else if(iid == IID_IX)
- {
- *ppv = static_cast<IX*>(this);
- trace("component: return pointer to ix");
- }
- else if(iid == IID_IY)
- {
- *ppv = static_cast<IY*>(this);
- trace("component: return pointer to iy");
- }
- else
- {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef();
- return S_OK;
- }
-
- ULONG __stdcall CA::AddRef()
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- ULONG __stdcall CA::Release()
- {
- if(InterlockedDecrement(&m_cRef)== 0)
- {
- delete this;
- return 0;
- }
- return m_cRef;
- }
-
-
-
-
- class CFactory:public IClassFactory
- {
- public:
-
- virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
- virtual ULONG __stdcall AddRef();
- virtual ULONG __stdcall Release();
-
- virtual HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv);
- virtual HRESULT __stdcall LockServer(BOOL bLock);
-
- CFactory():m_cRef(1){}
-
- ~CFactory()
- {
- trace("class factory :destory self");
- }
- private:
- long m_cRef;
- };
-
-
-
-
- HRESULT __stdcall CFactory::QueryInterface(const IID &iid, void **ppv)
- {
- if((iid == IID_IUnknown) || (iid == IID_IClassFactory))
- {
- *ppv = static_cast<IClassFactory*>(this);
- }
- else
- {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef();
- return S_OK;
- }
-
- ULONG __stdcall CFactory::AddRef()
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- ULONG __stdcall CFactory::Release()
- {
- if(InterlockedDecrement(&m_cRef) == 0)
- {
- delete this;
- return 0;
- }
- return m_cRef;
- }
-
-
-
-
- HRESULT __stdcall CFactory::CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv)
- {
- trace("class factory : create component");
-
- if(pUnknownOuter != NULL)
- {
- return CLASS_E_NOAGGREGATION;
- }
-
- CA *pA = new CA();
- if(pA == NULL)
- {
- return E_OUTOFMEMORY;
- }
-
- HRESULT hr = pA->QueryInterface(iid, ppv);
-
-
- pA->Release();
- return hr;
- }
-
-
- HRESULT __stdcall CFactory::LockServer(BOOL bLock)
- {
- if(bLock)
- {
- InterlockedIncrement(&g_lServerLocks);
- }
- else
- {
- InterlockedDecrement(&g_lServerLocks);
- }
- return S_OK;
- }
-
-
-
-
-
- STDAPI DllGetClassObject(const CLSID &clsid, const IID &iid, void **ppv)
- {
- trace("DllGetClassObeject: create class factory");
- if(clsid != CLSID_Component1)
- {
- return CLASS_E_CLASSNOTAVAILABLE;
- }
-
- CFactory *pFactory = new CFactory;
- if(pFactory == NULL)
- {
- return E_OUTOFMEMORY;
- }
- HRESULT hr = pFactory->QueryInterface(iid, ppv);
- pFactory->Release();
- return hr;
- }
-
- STDAPI DllCanUnloadNow()
- {
- if((g_lComponent == 0) && (g_lServerLocks == 0))
- {
- return S_OK;
- }
- else
- {
- return S_FALSE;
- }
- }
-
-
- STDAPI DllRegisterServer()
- {
- return RegisterServer(g_hModule,
- CLSID_Component1,
- g_szFriendlyName,
- g_szVerIndProgID,
- g_szProgID);
- }
-
-
- STDAPI DllUnregisterServer()
- {
- return UnregisterServer(CLSID_Component1,
- g_szVerIndProgID,
- g_szProgID);
- }
-
- BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, void *lpReserved)
- {
- if(dwReason == DLL_PROCESS_ATTACH)
- {
- g_hModule = hModule;
- }
- return TRUE;
- }
-
-
cmpnt.def
- LIBRARY Cmpnt.dll
- DESCRIPTION 'Chapter 7 Example COM Component'
-
- EXPORTS DllGetClassObject @2 PRIVATE
- DllRegisterServer @3 PRIVATE
- DllUnregisterServer @4 PRIVATE
- DllCanUnloadNow @5 PRIVATE
registry.h
- #ifndef _REGISTRY_H_
- #define _REGISTRY_H_
-
- HRESULT RegisterServer(HMODULE hModule,
- const CLSID &clsid,
- const char *szFriendlyName,
- const char *szVerIndProgID,
- const char *szProgID);
- HRESULT UnregisterServer(const CLSID &clsid,
- const char *szVerIndProgID,
- const char *szProgID);
- #endif
registry.cpp
-
-
-
- #include <objbase.h>
- #include <cassert>
- #include "registry.h"
-
-
-
-
-
- BOOL SetKeyAndValue(const char *szKey, const char *szSubKey, const char *szValue);
-
-
- void CLSIDtochar(const CLSID &clsid, char *szClsID, int nLength);
-
-
- LONG RecursiveDeleteKey(HKEY hKeyParent, const char *szKeyChild);
-
- const int CLSID_STRING_SIZE = 39 ;
-
-
-
-
- HRESULT RegisterServer(HMODULE hModule,
- const CLSID& clsid,
- const char* szFriendlyName,
- const char* szVerIndProgID,
- const char* szProgID)
- {
- char szModule[512] ;
- DWORD dwResult =::GetModuleFileName(hModule, szModule, sizeof(szModule)/sizeof(char));
-
- assert(dwResult != 0) ;
-
-
- char szCLSID[CLSID_STRING_SIZE] ;
- CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;
-
-
- char szKey[64] ;
- strcpy(szKey, "CLSID\\") ;
- strcat(szKey, szCLSID) ;
-
-
- SetKeyAndValue(szKey, NULL, szFriendlyName) ;
-
-
- SetKeyAndValue(szKey, "InprocServer32", szModule) ;
-
-
- SetKeyAndValue(szKey, "ProgID", szProgID) ;
-
-
- SetKeyAndValue(szKey, "VersionIndependentProgID",
- szVerIndProgID) ;
-
-
- SetKeyAndValue(szVerIndProgID, NULL, szFriendlyName) ;
- SetKeyAndValue(szVerIndProgID, "CLSID", szCLSID) ;
- SetKeyAndValue(szVerIndProgID, "CurVer", szProgID) ;
-
-
- SetKeyAndValue(szProgID, NULL, szFriendlyName) ;
- SetKeyAndValue(szProgID, "CLSID", szCLSID) ;
-
- return S_OK ;
- }
-
-
-
-
- LONG UnregisterServer(const CLSID& clsid,
- const char* szVerIndProgID,
- const char* szProgID)
- {
-
- char szCLSID[CLSID_STRING_SIZE] ;
- CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;
-
-
- char szKey[64] ;
- strcpy(szKey, "CLSID\\") ;
- strcat(szKey, szCLSID) ;
-
-
- LONG lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szKey) ;
- assert( (lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND) ) ;
-
-
- lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ;
-
-
- lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ;
-
- return S_OK ;
- }
-
-
- BOOL SetKeyAndValue(const char *szKey, const char *szSubKey, const char *szValue)
- {
- HKEY hKey;
- char szKeyBuf[1024] ;
-
-
- strcpy(szKeyBuf, szKey) ;
-
-
- if (szSubKey != NULL)
- {
- strcat(szKeyBuf, "\\") ;
- strcat(szKeyBuf, szSubKey ) ;
- }
-
-
- long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT ,
- szKeyBuf,
- 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_ALL_ACCESS, NULL,
- &hKey, NULL) ;
- if (lResult != ERROR_SUCCESS)
- {
- return FALSE ;
- }
-
-
- if (szValue != NULL)
- {
- RegSetValueEx(hKey, NULL, 0, REG_SZ,
- (BYTE *)szValue,
- strlen(szValue)+1) ;
- }
-
- RegCloseKey(hKey) ;
- return TRUE ;
-
- }
-
-
- LONG RecursiveDeleteKey(HKEY hKeyParent, const char *szKeyChild)
- {
-
- HKEY hKeyChild ;
- LONG lRes = RegOpenKeyEx(hKeyParent, szKeyChild, 0,
- KEY_ALL_ACCESS, &hKeyChild) ;
- if (lRes != ERROR_SUCCESS)
- {
- return lRes ;
- }
-
-
- FILETIME time ;
- char szBuffer[256] ;
- DWORD dwSize = 256 ;
- while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
- NULL, NULL, &time) == S_OK)
- {
-
- lRes = RecursiveDeleteKey(hKeyChild, szBuffer) ;
- if (lRes != ERROR_SUCCESS)
- {
-
- RegCloseKey(hKeyChild) ;
- return lRes;
- }
- dwSize = 256 ;
- }
-
-
- RegCloseKey(hKeyChild) ;
-
-
- return RegDeleteKey(hKeyParent, szKeyChild) ;
- }
-
- void CLSIDtochar(const CLSID &clsid, char *szClSID, int nLength)
- {
- assert(nLength >= CLSID_STRING_SIZE) ;
-
- LPOLESTR wszCLSID = NULL ;
- HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ;
- assert(SUCCEEDED(hr)) ;
-
-
- wcstombs(szClSID, wszCLSID, nLength);
-
-
- CoTaskMemFree(wszCLSID) ;
- }
iface.h
-
-
- #include <objbase.h>
- interface IX:IUnknown
- {
- virtual void __stdcall Fx() = 0;
- };
-
- interface IY:IUnknown
- {
- virtual void __stdcall Fy() = 0;
- };
-
- interface IZ:IUnknown
- {
- virtual void __stdcall Fz() = 0;
- };
-
- extern const IID IID_IX;
- extern const IID IID_IY;
- extern const IID IID_IZ;
- extern const CLSID CLSID_Component1;
-
guids.cpp
-
-
-
- #include <objbase.h>
-
-
- extern const IID IID_IX =
- {0x32bb8320, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
-
-
- extern const IID IID_IY =
- {0x32bb8321, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
-
-
- extern const IID IID_IZ =
- {0x32bb8322, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
-
-
- extern const CLSID CLSID_Component1 =
- {0x0c092c21, 0x882c, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
-
-
客户端
clients.cpp
-
-
-
-
- #include <objbase.h>
- #include "iface.h"
- #include <iostream>
- #include <string>
- using namespace std;
-
- void trace(string msg)
- {
- cout<<msg<<endl;
- }
-
- int main(void)
- {
- CoInitialize(NULL);
- trace("client:call CoCreateInstance to create component and get interface ix");
- IX *pIx = NULL;
- HRESULT hr = ::CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIx);
- if(SUCCEEDED(hr))
- {
- trace("client:Succeeded getting IX");
- pIx->Fx();
- trace("client:Ask for interface IY");
- IY *pIy = NULL;
- hr = pIx->QueryInterface(IID_IY, (void**)&pIy);
- if(SUCCEEDED(hr))
- {
- trace("client:Succeeded getting IY");
- pIy->Fy();
- pIy->Release();
- trace("client:Release IY interface");
- }
- else
- {
- trace("client:Could not get interface IY");
- }
- trace("client:Ask for interface IZ");
- IZ *pIz = NULL;
- hr = pIx->QueryInterface(IID_IZ, (void**)&pIz);
- if(SUCCEEDED(hr))
- {
- trace("client:Succeeded getting IZ");
- pIz->Fz();
- pIz->Release();
- trace("client:Release IZ interface");
- }
- else
- {
- trace("client:Could not get interface IZ");
- }
- trace("client:Release IX interface");
- pIx->Release();
- }
- else
- {
- cout<<"Client: Could not create component.hr ="<<hex<<hr<<endl;
- }
- CoUninitialize();
- return 0;
- }
运行结果
|