撰寫自訂 Surrogate

雖然系統提供的 Surrogate 在大部分情況下都已足夠,但在某些情況下,撰寫自訂 Surrogate 可能值得。 下列是一些範例:

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

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

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

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

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

代理程式所註冊的類別處理站不是 DLL 伺服器實作的實體類別處理站,而是由支援 IClassFactoryIMarshal的 Surrogate 進程所實作的泛型類別處理站。 因為它是 Surrogate 的類別處理站,而不是所註冊之 DLL 伺服器的類別處理站,代理的類別處理站將需要使用實體類別處理站來建立已註冊 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