撰寫自定義代理
雖然系統提供的代理在大部分情況下會比大部分情況更充分,但在某些情況下,撰寫自定義代理可能值得。 下列是一些範例:
- 自定義代理可能會提供系統代理中未存在的一些優化或語意。
- 如果同進程 DLL 包含與用戶端位於相同進程中的程式碼,則如果 DLL 伺服器在系統代理中執行,將無法正常運作。 自定義代理可以針對特定 DLL 量身打造,以處理此專案。
- 系統代理支援混合線程模型,使其可以同時載入免費和 Apartment 模型 DLL。 基於效率的原因,或接受允許載入 DLL 類型的命令行自變數,自定義代理可能會針對只載入 Apartment DLL 量身打造。
- 自定義 Surrogate 可能會採用系統 Surrogate 未使用的額外命令行參數。
- 系統代理會呼叫 CoInitializeSecurity,並告知其使用登錄中 AppID 機碼下找到的任何現有安全性設定。 自訂代理可以使用另一個安全性內容。
- 無法遠端的介面(例如最近 OCX 的介面)將無法與系統代理搭配運作。 自定義代理可以使用自己的實作包裝 DLL 的介面,並使用 Proxy/存根 DLL 搭配可遠端 IDL 定義,以允許遠端存取介面。
主要代理線程通常應該執行下列設定步驟:
- 呼叫 CoInitializeEx 以初始化線程並設定線程模型。
- 如果您想要要在伺服器中執行的 DLL 伺服器能夠使用 AppID 登錄機碼中的安全性設定,請使用 EOAC_APPID 功能呼叫 CoInitializeSecurity。 否則,將會使用舊版安全性設定。
- 呼叫 CoRegisterSurrogate 以向 COM 註冊 Surrogate 介面。
- 針對要求的 CLSID 呼叫 ISurrogate::LoadDllServer。
- 將主線程放在迴圈中,定期呼叫 CoFreeUnusedLibraries。
- 當 COM 呼叫 ISurrogate::FreeSurrogate 時,撤銷所有類別處理站並結束。
代理程式必須實作 ISurrogate 介面。 當新的 Surrogate 啟動時,以及呼叫 CoInitializeEx 之後,應該註冊此介面。 如上述步驟所示,ISurrogate 介面有兩種方法可呼叫 COM:LoadDllServer,以動態方式將新的 DLL 伺服器載入現有的代理;而 FreeSurrogate 則會釋放 Surrogate。
以載入要求呼叫 COM 呼叫的 LoadDllServer 實作,必須先建立支援 IUnknown、IClassFactory 和 IMarshal 的類別 Factory 物件,然後呼叫 CoRegisterClassObject,將物件註冊為所要求 CLSID 的類別處理站。
代理程式所註冊的類別處理站不是 DLL 伺服器實作的實際類別處理站,而是支援 IClassFactory 和 IMarshal 的代理程式所實作的泛型類別處理站。 因為它是 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。 此外,由於泛型類別處理站僅支援 IUnknown 和 IClassFactory,因此其他介面的要求必須導向至實際物件。 因此,應該會有 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 伺服器。
請遵循這些指導方針,以在需要這樣做時建立代理程式,應確保適當的行為。
相關主題