Once the proxy is created, the performance of PSOAInterface should be identical to that of a /Oicf proxy/stub library, according to Don Box in Essential Com (p. 228) and his Microsoft Systems Journal article (Jan 1999).
But as of Windows 8.1, the creation of the PSOAInterface proxies can be very sub-optimal. Don Box claims in the above article that combase!CreateProxyFromTypeInfo and the type library marshaler perform caching. However, in my testing the type library is reloaded from file after every time you release all your interfaces. This is a significant problem with the UIAutomation library, which uses the IGlobalInterfaceTable extensively, causing a bunch of extra system calls.
Here is my test. One apartment instantiates a coclass, and then another apartment unmarshals it into proxies 10000 times. It takes about 5s when using PSOAInterface. If I instead create and register a MIDL proxy/stub, it only takes about 100ms.
#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;
}
Running it in windbg confirms that it reloads the type library 10000 times.
bp KERNELBASE!CreateFileW "r $t0 = @$t0 + 1; g"
bp OLEAUT32!LoadTypeLibEx "r $t1 = @$t1 + 1; g"
g
r $t0, $t1
$t0=000000000000c35c $t1=0000000000002712