Essential Comの Don Box (p. 228) と彼の Microsoft Systems Journal 記事 (1999 年 1 月)によると、プロキシが作成されると、PSOAInterface のパフォーマンスは /Oicf プロキシ/スタブ ライブラリのパフォーマンスと同じになるはずです。
しかし、Windows 8.1 の時点では、PSOAInterface プロキシの作成は最適ではない可能性があります。Don Box は上記の記事で、combase!CreateProxyFromTypeInfo とタイプ ライブラリ マーシャラーがキャッシュを実行すると主張しています。ただし、私のテストでは、すべてのインターフェイスを解放するたびに、タイプ ライブラリがファイルから再読み込みされます。これは、IGlobalInterfaceTable を広範囲に使用する UIAutomation ライブラリの重大な問題であり、余分なシステム コールが大量に発生します。
これが私のテストです。1 つのアパートメントがコクラスをインスタンス化し、別のアパートメントがそれをプロキシに 10000 回アンマーシャリングします。PSOAInterface を使用する場合、約 5 秒かかります。代わりに 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