共用方式為


撰寫自定義替代物

雖然系統提供的代理在大部分情況下會比大部分情況更充分,但在某些情況下,撰寫自定義代理可能值得。 以下是一些範例:

  • 自定義代理可能會提供系統代理中未存在的一些優化或語意。
  • 如果同一進程的 DLL 包含依賴於與用戶端處於同一進程中的程式碼,則當 DLL 伺服器在系統代理程序中執行時,將無法正常運作。 自定義代理可以針對特定 DLL 量身打造,以處理此專案。
  • 系統代理支援混合線程模型,使其可以同時載入自由和單元模型的 DLL。 自定義代理可能會為只載入 Apartment 動態連結庫量身打造,以提高效率或接受命令列引數來指定允許載入的動態連結庫類型。
  • 自定義代理程式可能會接受系統代理程式未使用的額外命令列參數。
  • 系統代理會呼叫 CoInitializeSecurity,並告知其使用登錄中 AppID 機碼下找到的任何現有安全性設定。 自訂代理可使用不同的安全性環境。
  • 無法遠端使用的介面(例如最近 OCX 的介面)將無法與系統代理程式一同運作。 自定義代理程式可以使用自己的實作包裝 DLL 的介面,並使用具有可遠端訪問的 IDL 定義的代理介面(Proxy)/存根(Stub)DLL,以實現介面的遠端訪問。

主要代理線程通常應該執行下列設定步驟:

  1. 呼叫 CoInitializeEx,以初始化線程並設定線程模型。
  2. 如果您想要要在伺服器中執行的 DLL 伺服器能夠使用 AppID 登錄機碼中的安全性設定,請使用 EOAC_APPID 功能呼叫 CoInitializeSecurity。 否則,將會使用舊版安全性設定。
  3. 呼叫 CoRegisterSurrogate,向 COM 註冊 Surrogate 介面。
  4. 呼叫 ISurrogate::LoadDllServer 以載入要求的 CLSID。
  5. 將主線程放在迴圈中,定期呼叫 CoFreeUnusedLibraries
  6. 當 COM 呼叫 ISurrogate::FreeSurrogate時,撤銷所有類別工廠並退出。

代理程式必須實作 ISurrogate 介面。 當新的 Surrogate 啟動時以及在呼叫 CoInitializeEx 之後,應該註冊這個介面。 如上述步驟所示,ISurrogate 介面有兩個 COM 呼叫的方法:LoadDllServer,以動態方式將新的 DLL 伺服器載入現有的代理:和 FreeSurrogate,以釋放代理。

LoadDllServer的實作,當 COM 使用載入要求呼叫時,必須先建立支援 IUnknownIClassFactoryIMarshal的類別工廠物件,然後呼叫 CoRegisterClassObject,將物件註冊為所要求 CLSID 的類別工廠。

代理程式所註冊的類別處理站不是 DLL 伺服器實作的實際類別處理站,而是代理程序實作的泛型類別處理站,可支援 IClassFactoryIMarshal。 因為它是 Surrogate 的類別處理站,而不是註冊的 DLL 伺服器,Surrogate 的類別處理站將需要使用實際類別處理站來建立已註冊 CLSID 對象的實例。 Surrogate 的 IClassFactory::CreateInstance 的樣子應該類似以下範例:

STDMETHODIMP CSurrogateFactory::CreateInstance(
  IUnknown* pUnkOuter, 
  REFIID iid, 
  void** ppv)
{
    void* pcf;
    HRESULT hr;
 
    hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, &pcf);
    if ( FAILED(hr) )
        return hr;
    hr = ((IClassFactory*)pcf)->CreateInstance(pUnkOuter, iid, ppv);
    ((IClassFactory*)pcf)->Release();
    return hr;
}
 

代理者的類別工廠也必須支援 IMarshal,因為呼叫 CoGetClassObject 可以從已註冊的類別工廠要求任何介面,而不只是 IClassFactory。 此外,由於泛型類別處理站只支援 IUnknownIClassFactory,因此其他介面的要求必須導向至實際物件。 因此,應該有一個 MarshalInterface 方法,其應該如下所示:

STDMETHODIMP CSurrogateFactory::MarshalInterface(
  IStream *pStm,  
  REFIID riid, void *pv, 
  WORD dwDestContext, 
  void *pvDestContext, 
  DWORD mshlflags )
{   
    void * pCF = NULL;
    HRESULT hr;
 
    hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, riid, &pCF);
    if ( FAILED(hr) )
        return hr;   
    hr = CoMarshalInterface(pStm, riid, (IUnknown*)pCF, dwDestContext, pvDestContext,  mshlflags);
    ((IUnknown*)pCF)->Release();
    return S_OK;
 

承載 DLL 伺服器的代理必須註冊 DLL 伺服器的類別物件,並呼叫 CoRegisterClassObject。 DLL 代理的所有類別處理站都應該註冊為 REGCLS_SURROGATE。 REGCLS_SINGLUSE和REGCLS_MULTIPLEUSE不應該用於載入至代理的 DLL 伺服器。

請遵循這些指導方針,以在需要時建立代理程式,應確保正確的運行。

DLL代理程式