描述:
现在我有一个DLL形式的COM组件,是一个Apartment-Thread模型的组件。现在我在一个Worker线程中调用这个COM。按照《COM的原理与应用》里面说得,worker线程相对于COM机制中的Multi-Thread模型,所以COM库会另外生成一个UI线程来容纳这个COM组件,而那个Worker线程要用proxy-stub机制来访问这个COM组件。但是这样岂不是效率太低了,如果我只有这一个worker线程访问COM组件,根本不会造成COM组件的访问冲突,也要这样来实现吗?
谢谢!!
解决方案1:
调用CoInitializeEx指定ApartmentThread可以进入STA,但调用线程必须要有自己的消息循环,否则Apartment-Thread模型的组件就会没有响应
解决方案2:选择STA还是MTA主要取决于你实现的组件是否线程安全。
解决方案3:你如果只有这一个worker线程访问COM组件,可以让线程进入STA就行了,为什么要进入MTA?
解决方案4: 跨越套间访问的两种规则:汇集和全局接口表
To make an interface pointer available to all apartments in the process, the apartment that owns the interface pointer must register it with the GIT by calling the RegisterInterfaceInGlobal method. The GIT will return a DWORD to the caller that represents the pointer globally across all apartments in the process. This DWORD can be used from any apartment in the process to unmarshal a new proxy by calling the GetInterfaceFromGlobal method. This DWORD can be used to unmarshal proxies repeatedly until a call to RevokeInterfaceFromGlobal invalidates the global interface pointer. Applications that use the global interface table usually bind a single process-wide interface pointer at startup:
IGlobalInterfaceTable *g_pGIT = 0;
HRESULT InitOnce(void) {
assert(g_pGIT == 0);
return CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void**)&g_pGIT);
}
Once the global interface table is available, passing an interface pointer to another apartment simply means registering the pointer with the global interface table:
HRESULT WritePtrToGlobalVariable(IRacer *pRacer) {
// where to write the marshaled ptr
extern DWORD g_dwCookie;
// thread synchronization
extern HANDLE g_heventWritten;
// write marshaled object reference to global variable
HRESULT hr = g_pGIT->RegisterInterfaceInGlobal(
IID_IRacer, pRacer, &g_dwCookie);
// signal other thread that ptr is now available
SetEvent(g_heventWritten);
return hr;
}
The following code correctly unmarshals the object reference and can be called from any apartment in the same process:
HRESULT ReadPtrFromGlobalVariable(IRacer * &rpRacer,
bool bLastUnmarshal) {
// where to write the marshaled ptr
extern DWORD g_dwCookie;
// thread synchronization
extern HANDLE g_heventWritten;
// wait for other thread to signal that ptr is available
WaitForSingleObject(g_heventWritten, INFINITE);
// read marshaled object reference from global variable
HRESULT hr = g_pGIT->GetInterfaceFromGlobal(
g_dwCookie, IID_IRacer, (void**)&rpRacer);
// if we are the last to unmarshal, revoke the pointer
if (bLastUnmarshal)
g_pGIT->RevokeInterfaceFromGlobal(g_dwCookie);
return hr;
}
Note that a critical difference between these code fragments and the examples using CoMarshalInterThreadInterfaceInStream is that the GIT-based code supports unmarshaling more than one proxy.
7 It may seem odd that the global variable is an interface pointer that is initialized in the writer’s apartment and used from the reader’s apartment. This inconsistency is addressed by the documentation for CoMarshalInterThreadInterfaceInStream, which states that the resultant IStream interface pointer can be accessed from any apartment in the process.
你加个消息循环不就行了,不就是两三句话吗?