COM类厂对象的实现
COM客户程序要使用COM对象是通过COM库创建而来的,而实际上COM库是调用COM对象的类厂来创建的。COM类厂对象也是一个COM对象,所以它也从IUnknow继承而来,而它又支持IClassFactory接口:
class IClassFactory:public IUnknow
{
public:
virtual HRESULT CreateInstance(IUnkonwn* pUnkOuter, REFIID iid, void** ppObject)=0;
virtual HRESULT LockServer(BOOL fLock)=0;
};
所以,一个普通的类厂应该是这样的:
class CSampleFactory: public IClassFactory
{
public:
CSampleFactory();
HRESULT __stdcall QueryInterface(REFIID riid, void** ppObject);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
HRESULT __stdcall CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppObject);
HRESULT __stdcall LockServer(BOOL fLock);
private:
ULONG m_dwRefCount;
};
CreateInstance是构造COM对象的函数,通过传入接口的IID,从ppObject输出COM接口指针,而pUnkOuter一般设为NULL,该参数在聚合时起作用。
LockServer是用来控制COM类厂的生命周期的函数,将fLock设为TRUE后,即使组件程序中所有COM对象已释放了,该类厂指针也会一直保存并且有效,当不再需要的时候设为FALSE即可。
若要使用类厂对象去创建COM对象,首先得创建类厂对象,可以使用库函数CoGetClassObject来创建COM类的类厂,若找到的COM对象是进程内组件,则使用DLL导出函数DllGetClassObject函数创建类厂,然后将对象指针传出。
STDAPI DLLGetClassObject(REFCLSID rclsid, REFIID riid, void** ppObject)
{
if(rclsid == CLSID_SAMPLE)
{
CSampleFactory* csf = new CSampleFactory();
if(FAILED(csf->QueryInterface(riid, ppObject)))
{
delete pFactory;
*ppObject = NULL;
return E_INVALIDARG;
}
}
return NO_ERROR;
}
若创建的是进程外组件,则要分别调用CoRegisterClassObject和CoRevokeClassObject去注册和反注册类厂对象才能正常地创建、销毁COM对象。
COM自动注册
在cmd窗体下,使用命令
regsvr32 d:\sample.dll
对sample.dll进行注册,这时会调用该dll导出的DllRegisterServer。
另外,使用
regsvr32 /u d:\sample.dll
对其进行反注册,同样会调用导出函数DllUnregisterServer。
以上的命令是对进程内组件有效,而对进程外组件不必使用以上命令,由于自身是可执行程序,所以在运行时一般会自动调用DllRegisterServer和DllUnregisterServer。
COM自动卸载
客户端程序调用CoFreeUnusedLibraries来释放组件,但是释放组件需要满足:组件中的对象数目为0和类厂的锁计数为0,要知道是否满足以上条件,需要导出函数DllCanUnloadNow,该函数通过判断组件对象的引用计数来判断是否可以释放组件。