根据 Don Box 在Essential Com (p. 228) 和他的 Microsoft Systems Journal 文章 (Jan 1999)中的说法,一旦创建了代理,PSOAInterface 的性能应该与 /Oicf 代理/存根库的性能相同。
但从 Windows 8.1 开始,PSOAInterface 代理的创建可能不是最理想的。Don Box 在上面的文章中声称combase!CreateProxyFromTypeInfo 和类型库封送器执行缓存。但是,在我的测试中,每次发布所有接口后,都会从文件中重新加载类型库。这是 UIAutomation 库的一个重大问题,它广泛使用 IGlobalInterfaceTable,导致大量额外的系统调用。
这是我的测试。一个公寓实例化一个 coclass,然后另一个公寓将其解组为代理 10000 次。使用 PSOAInterface 大约需要 5s。如果我改为创建和注册 MIDL 代理/存根,只需要大约 100 毫秒。
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <comdef.h>
// RemoteProxyFactory32 from oleacc.dll {53362c64-a296-4f2d-a2f8-fd984d08340b}
static IID CLSID_RemoteProxyFactory32 = GUID{ 0x53362c64, 0xa296, 0x4f2d, { 0xa2, 0xf8, 0xfd, 0x98, 0x4d, 0x08, 0x34, 0x0b } };
// IRemoteProxyFactory from oleacc.dll {8628f27d-64a2-4ed6-906b-e6155314c16a}
static IID IID_IRemoteProxyFactory = GUID{ 0x8628f27d, 0x64a2, 0x4ed6, { 0x90, 0x6b, 0xe6, 0x15, 0x53, 0x14, 0xc1, 0x6a } };
struct register_interface_thread {
HANDLE hInterfaceRegistered;
DWORD dwCookie;
HANDLE hShouldClose;
};
DWORD WINAPI RegisterInterfaceThread(_In_ LPVOID lpParameter) {
struct register_interface_thread& state = *(struct register_interface_thread*)(lpParameter);
HRESULT hr;
if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED))) {
fprintf(stderr, "Error CoInitializeEx: 0x%08x\n", hr);
}
else {
IUnknown *pv;
if (FAILED(hr = CoCreateInstance(CLSID_RemoteProxyFactory32, NULL, CLSCTX_LOCAL_SERVER, IID_IRemoteProxyFactory, (LPVOID*)&pv))) {
fprintf(stderr, "CocCreateInstance(RemoteProxyFactory32 of oleacc.dll) failed with hresult 0x%x\n", hr);
}
else {
IGlobalInterfaceTable *pIGlobalInterfaceTable;
if (FAILED(hr = CoCreateInstance
(
CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void **)&pIGlobalInterfaceTable
))) {
fprintf(stderr, "CocCreateInstance(StdGlobalInterfaceTable) failed with hresult 0x%x\n", hr);
}
else {
DWORD dwCookie;
if (FAILED(hr = pIGlobalInterfaceTable->RegisterInterfaceInGlobal(pv, IID_IRemoteProxyFactory, &dwCookie))) {
fprintf(stderr, "RegisterInterfaceInGlobal failed with hresult 0x%x\n", hr);
}
else {
fprintf(stdout, "Successfully registered interface; cookie=0x%x\n", dwCookie);
state.dwCookie = dwCookie;
if (!SetEvent(state.hInterfaceRegistered)) {
DWORD err = GetLastError();
fprintf(stderr, "Error SetEvent(hInterfaceRegistered): 0x%x\n", err);
}
else {
DWORD waitResult;
if (WAIT_OBJECT_0 != (waitResult = WaitForSingleObject(state.hShouldClose, INFINITE))) {
DWORD err = GetLastError();
fprintf(stderr, "Error WaitForSingleObject: returned 0x%x; error=0x%08x\n", waitResult, err);
hr = err;
}
else {
fprintf(stdout, "Successfully joined thread; dwCookie=0x%x\n", state.dwCookie);
}
}
}
pIGlobalInterfaceTable->Release();
}
if (pv != NULL)
pv->Release();
}
CoUninitialize();
fprintf(stdout, "Thread going away\n");
}
return 0;
}
int main(int argc, char* argv[]) {
HRESULT hr;
if (FAILED(hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) {
fprintf(stderr, "Error CoInitializeEx: 0x%08x\n", hr);
}
else {
struct register_interface_thread state;
state.dwCookie = 0;
state.hInterfaceRegistered = CreateEventEx(NULL, TEXT("hInterfaceRegistered"), CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
state.hShouldClose = CreateEventEx(NULL, TEXT("hShouldClose"), CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
HANDLE hThread = CreateThread(NULL, 0, RegisterInterfaceThread, &state, 0, NULL);
if (hThread == NULL) {
DWORD err = GetLastError();
fprintf(stderr, "Error CreateThread: 0x%08x\n", err);
hr = err;
}
else {
DWORD waitResult;
if (WAIT_OBJECT_0 != (waitResult = WaitForSingleObject(state.hInterfaceRegistered, INFINITE))) {
DWORD err = GetLastError();
fprintf(stderr, "Error WaitForSingleObject: returned 0x%x; error=0x%08x\n", waitResult, err);
hr = err;
}
else {
fprintf(stdout, "Successfully waited for hInterfaceRegistered; dwCookie=0x%x\n", state.dwCookie);
IGlobalInterfaceTable *pIGlobalInterfaceTable;
if (FAILED(hr = CoCreateInstance
(
CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void **)&pIGlobalInterfaceTable
))) {
fprintf(stderr, "CoCreateInstance(StdGlobalInterfaceTable) failed with hresult 0x%x\n", hr);
}
else {
IUnknown *pv = NULL;
DWORD start_time = GetTickCount();
DWORD i;
for (i = 0; i != 10000; i++) {
if (FAILED(hr = pIGlobalInterfaceTable->GetInterfaceFromGlobal(state.dwCookie, IID_IRemoteProxyFactory, (LPVOID*)&pv))) {
fprintf(stderr, "GetInterfaceFromGlobal failed with hresult 0x%x\n", hr);
break;
}
else {
pv->Release();
}
}
DWORD end_time = GetTickCount();
DWORD difference = end_time - start_time;
fprintf(stdout, "%u iterations completed in %ums\n", i, difference);
pIGlobalInterfaceTable->Release();
}
if (!SetEvent(state.hShouldClose)) {
DWORD err = GetLastError();
fprintf(stderr, "SetEvent(hShouldClose) failed; err=0x%x\n", err);
hr = err;
}
else {
if (WAIT_OBJECT_0 != (waitResult = WaitForSingleObject(hThread, INFINITE))) {
DWORD err = GetLastError();
fprintf(stderr, "Error WaitForSingleObject(hThread): returned 0x%x; error=0x%08x\n", waitResult, err);
hr = err;
}
else {
printf("successfully joined thread.\n");
}
}
}
}
}
return hr;
}
在 windbg 中运行它确认它会重新加载类型库 10000 次。
bp KERNELBASE!CreateFileW "r $t0 = @$t0 + 1; g"
bp OLEAUT32!LoadTypeLibEx "r $t1 = @$t1 + 1; g"
g
r $t0, $t1
$t0=000000000000c35c $t1=0000000000002712