天天看點

COM元件開發(三)——類廠對象

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,該函數通過判斷元件對象的引用計數來判斷是否可以釋放元件。