共用方式為


撰寫自定義代理

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

  • 自定義代理可能會提供系統代理中未存在的一些優化或語意。
  • 如果同進程 DLL 包含與用戶端位於相同進程中的程式碼,則如果 DLL 伺服器在系統代理中執行,將無法正常運作。 自定義代理可以針對特定 DLL 量身打造,以處理此專案。
  • 系統代理支援混合線程模型,使其可以同時載入免費和 Apartment 模型 DLL。 基於效率的原因,或接受允許載入 DLL 類型的命令行自變數,自定義代理可能會針對只載入 Apartment DLL 量身打造。
  • 自定義 Surrogate 可能會採用系統 Surrogate 未使用的額外命令行參數。
  • 系統代理會呼叫 CoInitializeSecurity,並告知其使用登錄中 AppID 機碼下找到的任何現有安全性設定。 自訂代理可以使用另一個安全性內容。
  • 無法遠端的介面(例如最近 OCX 的介面)將無法與系統代理搭配運作。 自定義代理可以使用自己的實作包裝 DLL 的介面,並使用 Proxy/存根 DLL 搭配可遠端 IDL 定義,以允許遠端存取介面。

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

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

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

以載入要求呼叫 COM 呼叫的 LoadDllServer 實作,必須先建立支援 IUnknownIClassFactory IMarshal 的類別 Factory 物件,然後呼叫 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;
}
 

Surrogate 的類別處理站也必須支援 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 Surrogates