COMPERF.CPP

// =========================================================================== 
// File: C O M P E R F . C P P
#define PURPOSE "This is the reference performance sample for COM/DCOM run on local\n\
and remote machines. This program is both a client and a server.\n\
It launches as a client by default, registering its executable as a\n\
server with COM and creating other instances of itself on a remote and\n\
local machine to be servers. A single command-line argument is allowed\n\
for a remote server name. Performance numbers in calls/sec are output\n\
in tabular form. Tests COM and IDispatch method calls with two security\n\
levels: default/min and full. Use DCOMCNFG to set default security to \n\
Authn Lvl to NONE for best comparison. Parameter sizes are varied to see\n\
effect on security levels(~4/50/4k bytes). Uses psoleperf.dll MIDL generated\n\
proxy/stub implementation. Put pscomperf.dll in same dir as this exe on\n\
all machines. Run exe on each machine to automatically register class code\n\
and proxy/stub dll. You can then run on either machine and pass a remote\n\
machine name (DNS or IP address) as single cmd-line parameter.\n\n"

// Instructions:
// Install on one or more machines as described above. Run on command-line as "comperf".
// A single command-line argument is allowed for a remote server name. E.g.
// "comperf MyComputer" or "comperf 123.44.44.234" using IP address.
// This sample may be compiled as UNICODE or ANSI
//
// Copyright 1996 Microsoft Corporation. All Rights Reserved.
// ===========================================================================

// %%Includes: ---------------------------------------------------------------
#define INC_OLE2
#define STRICT
#define LOOPS 2000 // default number of test function calls
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include <stdio.h>
#include "psperf.h" // MIDL generated header

// %%Constants: --------------------------------------------------------------
#define cServerThreads 3 // number support threads each server will generate
#define dispidICOMPerformance_Test1 11
#define dispidICOMPerformance_Test23 12
#define szTitleServer TEXT("SERVER: COM Performance Sample")
#define szTitleClient TEXT("COM Performance Sample")
const LARGE_INTEGER bZero = {0,0};

// %%IDispatch support
PARAMDATA paramdata[2] = {{L"i", VT_I4},{L"bstr", VT_BSTR}};
METHODDATA methoddata[2] = {{L"Test1", &paramdata[0], 11, 3, CC_CDECL, 1, DISPATCH_METHOD, VT_I4},
{L"Test23", &paramdata[1], 12, 4, CC_CDECL, 1, DISPATCH_METHOD, VT_I4}};
INTERFACEDATA interfacedata = { methoddata, 2 };

// %%Guids: ------------------------------------------------------------------
// {DDC68870-E08E-11cf-A535-00AA00615B03}
DEFINE_GUID(CLSID_CTestCOMPerformance,0xddc68870,0xe08e,0x11cf,0xa5,0x35,0x0,0xaa,0x0,0x61,0x5b,0x3);

// %%typedefs: --------------------------------------------------------------
typedef HRESULT (WINAPI *LPFNREG)();
typedef struct perf
{
TCHAR *szTest;
float sec[3]; // time for all three methods in ICOMPerformance
} PERF;

// %%Globals: ----------------------------------------------------------------
BOOL vfServer = FALSE; // is this instance a server or client?
BOOL vfRemote = FALSE; // is there a remote server?
HANDLE vrghThread[cServerThreads]; // worker thread handles
DWORD vrgtid[cServerThreads]; // worker thread id's
HANDLE vrghEvent[cServerThreads]; // creation event for each worker
HANDLE vhEventCliDone;
HANDLE vhEventServer; // creation-complete event for class-factory
HANDLE vhEventCliStart;
HANDLE hKillSvr; // shuts down the server
UINT viNextThread; // next worker to create an object on
UINT g_cObjectCount = 0;
LPSTREAM vpstmMarshalling; // scratch stream used for cross-apt marshalling
HRESULT vhrThreadStatus; // signals status to class-factory

LPWSTR pszDesc1 = L"String is passed to Test methd of ICOMPerformance";

PERF vrgperfloc[] = {
{ TEXT("COM FreeToApt"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("IDisp FreeToApt"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("COM FreeTo Free"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("IDispFreeToFree"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("COM AptToApt"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("IDispAptToApt"), {-1.0f,-1.0f,-1.0f}},
{ NULL, {-1.0f,-1.0f,-1.0f}} };

PERF vrgperfrmt[] = {
{ TEXT("COM FreeToApt"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("IDisp FreeToApt"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("COM FreeTo Free"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("IDispFreeToFree"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("COM AptToApt"), {-1.0f,-1.0f,-1.0f}},
{ TEXT("IDispAptToApt"), {-1.0f,-1.0f,-1.0f}},
{ NULL, {-1.0f,-1.0f,-1.0f}} };

// %%Prototypes: -------------------------------------------------------------
LRESULT ServerThreadProc(LPARAM lParam);
LRESULT ClientThreadProc(LPARAM lParam);
BOOL FAutoRegister();
void Message(LPTSTR szPrefix, HRESULT hr);
void FPrintResults(void);
BOOL AptFreeCOMTest(int cLoops);
BOOL AptFreeAutoTest(int cLoops);
BOOL FreeFreeCOMTest(int cLoops);
BOOL FreeFreeAutoTest(int cLoops);
BOOL DoTests(void);
void Usage(void);

typedef IClientSecurity *LPCLIENTSECURITY;
typedef ICOMPerformance *LPCOMPERFORMANCE;
LPCOMPERFORMANCE pCOMApt, pCOMFree, pCOMAptRmt, pCOMFreeRmt;
LPDISPATCH pAutoApt, pAutoFree, pAutoAptRmt, pAutoFreeRmt;

// %%Classes: ----------------------------------------------------------------
// the class-factory object exists in the main application apartment/thread
// and is used to create instances of the worker objects on worker threads.
class CClassFactory : public IClassFactory
{
public:
// IClassFactory
STDMETHODIMP QueryInterface(REFIID iid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void) { return m_cRef++; }
STDMETHODIMP_(ULONG) Release(void) { if (--m_cRef == 0){ delete this; return 0; } return m_cRef;}
STDMETHODIMP CreateInstance(LPUNKNOWN punkOuter, REFIID iid, void **ppv);
STDMETHODIMP LockServer(BOOL fLock);

CClassFactory() { m_cRef = 1; }
private:
ULONG m_cRef;
};

// this worker object is simple: it simply supports IUnknown. more interesting
// interfaces can be readily added here and implemented for the worker.
class CTestCOMPerformance : public ICOMPerformance , public IDispatch
{
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID iid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void) { return m_cRef++; }
STDMETHODIMP_(ULONG) Release(void);

// ICOMPerformance
STDMETHODIMP Test1(int l);
STDMETHODIMP Test23(BSTR szDesc);

// IDispatch
STDMETHODIMP GetTypeInfoCount(unsigned int *pcti);
STDMETHODIMP GetTypeInfo(unsigned int iti, LCID lcid, LPTYPEINFO *ppti);
STDMETHODIMP GetIDsOfNames(REFIID riid, WCHAR * *rgszNames, unsigned int cNames, LCID lcid, DISPID *pdispid);
STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, unsigned short wFlags,
DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, unsigned int *puArgErr);

CTestCOMPerformance() { m_cRef = 1;}
private:
ULONG m_cRef;
};

class CTIMER
{
public:
inline CTIMER() { memset(this, 0, sizeof(*this)); }
inline void Start() { QueryPerformanceCounter(&m_sStart); }
inline void Stop() { QueryPerformanceCounter(&m_sStop); }
inline float OutputTime() { QueryPerformanceFrequency(&m_liFreq);
return (float)(( m_sStop.LowPart - m_sStart.LowPart)/(float)m_liFreq.LowPart);}
// data members
LARGE_INTEGER m_sStart, m_sStop, m_liFreq;
};

// ---------------------------------------------------------------------------
// %%Function: Message
//
// Formats and displays a message to the console.
// ---------------------------------------------------------------------------
void
Message(LPTSTR szPrefix, HRESULT hr)
{
LPTSTR szMessage;

if (hr == S_OK)
{
_tprintf(szPrefix);
_tprintf(TEXT("\n"));
return;
}

if (HRESULT_FACILITY(hr) == FACILITY_WINDOWS)
hr = HRESULT_CODE(hr);

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
(LPTSTR)&szMessage,
0,
NULL );

_tprintf(TEXT("%s: %s(%lx)\n"), szPrefix, szMessage, hr);
LocalFree(szMessage);
} // Message

// ---------------------------------------------------------------------------
// %%Function: main
// ---------------------------------------------------------------------------
int __cdecl
main(int argc, CHAR **argv)
{
HRESULT hr;
int i;
DWORD dwRegister = 0;
COSERVERINFO csi, *pcsi=NULL;
WCHAR wsz [MAX_PATH];
CClassFactory *pcf = NULL;
LPCLASSFACTORY pcflocal = NULL;
LPCLASSFACTORY pcfrmt = NULL;
LPCLIENTSECURITY pclntsec = NULL;
TCHAR rgch[32];

DWORD AuthnSvc, AuthzSvc, AuthnLvl, ImpLvl, Capabilities;


if(!FAutoRegister())
{
exit(-1);
}
// parse command-line
if (argc > 1)
{
// either started as server or passing in server name
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, argv[1], -1,
wsz, MAX_PATH);

// register the CTestCOMPerformance class in the registry so
// that the client can create instances of the server
if((!lstrcmpW(L"-?", wsz)) || (!lstrcmpW(L"/?", wsz)))
{
Usage();
return 0;
}
if(!lstrcmpW(L"-Embedding", wsz))
vfServer = TRUE;
else
{
// allow a machine-name as the command-line argument
csi.dwReserved1 = 0;
csi.pAuthInfo = NULL;
csi.dwReserved2 = 0;
csi.pwszName = wsz;
pcsi = &csi;
vfRemote = TRUE;
}
}
if(vfServer)
Message(szTitleServer, S_OK);
else
Message(szTitleClient, S_OK);

// initialize COM
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
{
Message(TEXT("CoInitializeEx"), hr);
exit(hr);
}

// create an IStream to be used for marshalling interfaces
hr = CreateStreamOnHGlobal(NULL, TRUE, &vpstmMarshalling);
if (FAILED(hr))
{
Message(TEXT("CreateStreamOnHGlobal"), hr);
goto LCleanup;
}

if (vfServer)
{
// create the threads and synchronization events
// which the server will need
for (i=0; i<cServerThreads; i++)
{
// create the thread suspended so its event can be
// created using its thread-id and it will be able to
// use it as soon as it runs
vrghThread[i] = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)&ServerThreadProc,
0,
CREATE_SUSPENDED,
&vrgtid[i]);
if (vrghThread[i] == NULL)
{
hr = GetLastError();
goto LCleanup;
}

// this event signals to a worker thread to create a new CTestCOMPerformance
wsprintf(rgch, TEXT("Thread_%d"), vrgtid[i]);
vrghEvent[i] = CreateEvent(NULL, FALSE, FALSE, rgch);
if (vrghEvent[i] == NULL)
{
hr = GetLastError();
goto LCleanup;
}
// now that the event is available, let the thread run
ResumeThread(vrghThread[i]);
}
hKillSvr = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hKillSvr == NULL)
{
hr = GetLastError();
goto LCleanup;
}

// this signals the status of a worker threads creation after
// receiving its create signal via vrghEvent[i]
vhEventServer = CreateEvent(NULL, FALSE, FALSE, TEXT("Server"));
if (vhEventServer == NULL)
{
hr = GetLastError();
goto LCleanup;
}
pcf = new CClassFactory;
if(pcf == NULL)
{
hr = E_OUTOFMEMORY;
goto LCleanup;
}

// register the class-factory with COM
hr = CoRegisterClassObject(CLSID_CTestCOMPerformance,
(IUnknown *)pcf,
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&dwRegister);
if (FAILED(hr))
{
Message(TEXT("CoRegisterClassObject"), hr);
goto LCleanup;
}

Message(TEXT("Server waiting"), S_OK);
WaitForSingleObject(hKillSvr, INFINITE);
Sleep(7000); // allow time for last Release call processing
delete pcf;
pcf = NULL;
}

else // client case
{
pCOMApt = pCOMFree = pCOMAptRmt =pCOMFreeRmt = NULL;
pAutoFree = pAutoFree = pAutoAptRmt = pAutoFreeRmt = NULL;

// get local class factory
hr = CoGetClassObject(CLSID_CTestCOMPerformance, CLSCTX_LOCAL_SERVER, NULL,
IID_IClassFactory, (void**)&pcflocal);
if(FAILED(hr))
{
Message(TEXT("CoGetClassObject:"), hr);
goto LCleanup;
}
// apt-model obj
hr = pcflocal->CreateInstance(NULL, IID_ICOMPerformance, (void**)&pCOMApt);
if (FAILED(hr))
{
Message(TEXT("Create Local Apt Instance:"), hr);
goto LCleanup;
}
hr = pCOMApt->QueryInterface(IID_IDispatch, (void**)&pAutoApt);
if (FAILED(hr))
{
Message(TEXT("QI for pAutoApt:"), hr);
goto LCleanup;
}
// get free-threaded obj
hr = pcflocal->CreateInstance(NULL, IID_ICOMPerformance, (void**)&pCOMFree);
if (FAILED(hr))
{
Message(TEXT("Create Local Free Instance"), hr);
goto LCleanup;
}
hr = pCOMFree->QueryInterface(IID_IDispatch, (void**)&pAutoFree);
if (FAILED(hr))
{
Message(TEXT("QI for pAutoFree"), hr);
goto LCleanup;
}
hr = pcflocal->Release();
pcflocal = NULL;
if(vfRemote)
{
hr = CoGetClassObject(CLSID_CTestCOMPerformance, CLSCTX_REMOTE_SERVER, pcsi,
IID_IClassFactory, (void**)&pcfrmt);
if(FAILED(hr))
{
Message(TEXT("CoGetClassObject for remote CF"), hr);
vfRemote = FALSE;
goto LContinue;
}
// apt-model obj
hr = pcfrmt->CreateInstance(NULL, IID_ICOMPerformance, (void**)&pCOMAptRmt);
if (FAILED(hr))
{
Message(TEXT("Create Remote Instance"), hr);
goto LCleanup;
}
hr = pCOMAptRmt->QueryInterface(IID_IDispatch, (void**)&pAutoAptRmt);
if (FAILED(hr))
{
Message(TEXT("QI for pAutoAptFreeRmt"), hr);
goto LCleanup;
}
// get free-threaded obj
hr = pcfrmt->CreateInstance(NULL, IID_ICOMPerformance, (void**)&pCOMFreeRmt);
if (FAILED(hr))
{
Message(TEXT("Create Remote Free Instance"), hr);
goto LCleanup;
}
hr = pCOMFreeRmt->QueryInterface(IID_IDispatch, (void**)&pAutoFreeRmt);
if (FAILED(hr))
{
Message(TEXT("QI for pAutoFreeFreeRmt"), hr);
goto LCleanup;
}
hr = pcfrmt->Release();
pcfrmt = NULL;
}
LContinue:
// create apartment thread for client
DWORD dwClienttid;
// create the thread suspended so its event can be
// created using its thread-id and it will be able to
// use it as soon as it runs
HANDLE hClientThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)&ClientThreadProc,
0,
CREATE_SUSPENDED,
&dwClienttid);
if (hClientThread == NULL)
{
hr = GetLastError();
goto LCleanup;
}
wsprintf(rgch, TEXT("Thread_%d"), dwClienttid);

vhEventCliStart = CreateEvent(NULL, FALSE, FALSE, rgch);
if (vhEventCliStart == NULL)
{
hr = GetLastError();
goto LCleanup;
}
vhEventCliDone = CreateEvent(NULL, FALSE, FALSE, NULL);
if (vhEventCliDone == NULL)
{
hr = GetLastError();
goto LCleanup;
}
// Marshall local apt thread
hr = vpstmMarshalling->Seek(bZero, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
throw hr;
hr = CoMarshalInterface(vpstmMarshalling,
IID_IUnknown,
(ICOMPerformance*)pCOMApt,
MSHCTX_INPROC,
NULL,
MSHLFLAGS_NORMAL);
if (FAILED(hr))
throw hr;
// now that the event is available, let the thread run
ResumeThread(hClientThread);
SetEvent(vhEventCliStart);
WaitForSingleObject(vhEventCliDone, INFINITE);

if(vfRemote && NULL != pCOMAptRmt)
{
// Marshall local apt thread
hr = vpstmMarshalling->Seek(bZero, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
throw hr;
hr = CoMarshalInterface(vpstmMarshalling,
IID_IUnknown,
(ICOMPerformance*)pCOMAptRmt,
MSHCTX_INPROC,
NULL,
MSHLFLAGS_NORMAL);
if (FAILED(hr))
throw hr;
SetEvent(vhEventCliStart);
WaitForSingleObject(vhEventCliDone, INFINITE);
}

// Test
_tprintf(TEXT("Output in Calls per sec\n"));
_tprintf(TEXT("DEF Sec:\t\tLOCAL\t\t\tREMOTE\n"));
// Do all tests
if(!DoTests())
goto LCleanup;
// Up security on proxies
// Get current security
hr = pCOMApt->QueryInterface(IID_IClientSecurity, (void**)&pclntsec);
hr = pclntsec->QueryBlanket(pCOMApt, &AuthnSvc, &AuthzSvc, NULL, &AuthnLvl, &ImpLvl, NULL,
&Capabilities);

_tprintf(TEXT("Increasing local security from AuthnLvl:%d ImpLvl:%d\n"), AuthnLvl, ImpLvl);

hr = pclntsec->SetBlanket(pCOMApt, AuthnSvc, AuthzSvc, NULL, 6,
4, NULL, Capabilities);
hr = pclntsec->QueryBlanket(pCOMApt, &AuthnSvc, &AuthzSvc, NULL, &AuthnLvl, &ImpLvl, NULL,
&Capabilities);

_tprintf(TEXT("Local security is now AuthnLvl:%d ImpLvl:%d\n"), AuthnLvl, ImpLvl);

pclntsec->Release();
pclntsec = NULL;
hr = pCOMFree->QueryInterface(IID_IClientSecurity, (void**)&pclntsec);
hr = pclntsec->SetBlanket(pCOMFree, AuthnSvc, AuthzSvc, NULL, 6,
4, NULL, Capabilities);
pclntsec->Release();
pclntsec = NULL;
hr = pAutoApt->QueryInterface(IID_IClientSecurity, (void**)&pclntsec);
hr = pclntsec->SetBlanket(pAutoApt, AuthnSvc, AuthzSvc, NULL, 6,
4, NULL, Capabilities);
pclntsec->Release();
pclntsec = NULL;
hr = pAutoFree->QueryInterface(IID_IClientSecurity, (void**)&pclntsec);
hr = pclntsec->SetBlanket(pAutoFree, AuthnSvc, AuthzSvc, NULL, 6,
4, NULL, Capabilities);
pclntsec->Release();
pclntsec = NULL;
if(vfRemote)
{
hr = pCOMAptRmt->QueryInterface(IID_IClientSecurity, (void**)&pclntsec);
hr = pclntsec->QueryBlanket(pCOMAptRmt, &AuthnSvc, &AuthzSvc, NULL, &AuthnLvl, &ImpLvl, NULL,
&Capabilities);

_tprintf(TEXT("Increasing remote security from AuthnLvl:%d ImpLvl:%d\n"), AuthnLvl, ImpLvl);

hr = pclntsec->SetBlanket(pCOMAptRmt, AuthnSvc, AuthzSvc, NULL, 6,
4, NULL, Capabilities);

_tprintf(TEXT("Remote security is now AuthnLvl:%d ImpLvl:%d\n"), AuthnLvl, ImpLvl);

pclntsec->Release();
pclntsec = NULL;
hr = pCOMFreeRmt->QueryInterface(IID_IClientSecurity, (void**)&pclntsec);
hr = pclntsec->SetBlanket(pCOMFreeRmt, AuthnSvc, AuthzSvc, NULL, 6,
4, NULL, Capabilities);
pclntsec->Release();
pclntsec = NULL;
hr = pAutoAptRmt->QueryInterface(IID_IClientSecurity, (void**)&pclntsec);
hr = pclntsec->SetBlanket(pAutoAptRmt, AuthnSvc, AuthzSvc, NULL, 6,
4, NULL, Capabilities);
pclntsec->Release();
pclntsec = NULL;
hr = pAutoFreeRmt->QueryInterface(IID_IClientSecurity, (void**)&pclntsec);
hr = pclntsec->SetBlanket(pAutoFreeRmt, AuthnSvc, AuthzSvc, NULL, 6,
4, NULL, Capabilities);
pclntsec->Release();
pclntsec = NULL;
}
// Do all tests with FULL sec
// Apt to Apt first
// Marshall local apt thread
hr = vpstmMarshalling->Seek(bZero, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
throw hr;
hr = CoMarshalInterface(vpstmMarshalling,
IID_IUnknown,
(ICOMPerformance*)pCOMApt,
MSHCTX_INPROC,
NULL,
MSHLFLAGS_NORMAL);
if (FAILED(hr))
throw hr;
// now that the event is available, let the thread run
ResumeThread(hClientThread);
SetEvent(vhEventCliStart);
WaitForSingleObject(vhEventCliDone, INFINITE);

if(vfRemote && NULL != pCOMAptRmt)
{
// Marshall local apt thread
hr = vpstmMarshalling->Seek(bZero, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
throw hr;
hr = CoMarshalInterface(vpstmMarshalling,
IID_IUnknown,
(ICOMPerformance*)pCOMAptRmt,
MSHCTX_INPROC,
NULL,
MSHLFLAGS_NORMAL);
if (FAILED(hr))
throw hr;
SetEvent(vhEventCliStart);
WaitForSingleObject(vhEventCliDone, INFINITE);
}
_tprintf(TEXT("Modified Sec:\t\tLOCAL\t\t\tREMOTE\n"));
DoTests();
}
LCleanup:
if (vpstmMarshalling != NULL)
vpstmMarshalling->Release();
if (vfServer)
{
// we explicitly don't clean up threads and events
if (dwRegister != 0)
CoRevokeClassObject(dwRegister);
if(NULL != pcf)
delete pcf;
}
else // client
{
// client case: release objects
if(NULL != pcflocal)
hr = pcflocal->Release();
if(NULL != pcfrmt)
hr = pcfrmt->Release();
if(NULL != pCOMFree)
hr = pCOMFree->Release();
if(NULL != pAutoFree)
hr = pAutoFree->Release();
if(NULL != pCOMApt)
hr = pCOMApt->Release();
if(NULL != pAutoApt)
hr = pAutoApt->Release();
if(NULL != pCOMFreeRmt)
hr = pCOMFreeRmt->Release();
if(NULL != pAutoFreeRmt)
hr = pAutoFreeRmt->Release();
if(NULL != pCOMAptRmt)
hr = pCOMAptRmt->Release();
if(NULL != pAutoAptRmt)
hr = pAutoAptRmt->Release();
}
CoUninitialize();
return hr;
} // main

// ---------------------------------------------------------------------------
// %%Function: FAutoRegister
// Registers the CTestCOMPerformance class in the registry.
// ---------------------------------------------------------------------------
BOOL
FAutoRegister()
{
static TCHAR szClassDesc[] = TEXT("COM Performance Sample");
static TCHAR szCLSIDEntry[] = TEXT("CLSID\\{DDC68870-E08E-11cf-A535-00AA00615B03}");
TCHAR szBuf[512];
TCHAR szPath[512];
HKEY hkeyT = NULL;


HRESULT hr;
LPFNREG lpfn = NULL;
HINSTANCE hLib = NULL;

// register class code
if ((RegSetValue(HKEY_CLASSES_ROOT, szCLSIDEntry, REG_SZ, szClassDesc,
lstrlen(szClassDesc)) != ERROR_SUCCESS) ||
(RegCreateKey(HKEY_CLASSES_ROOT, szCLSIDEntry, &hkeyT)
!= ERROR_SUCCESS) ||
!GetModuleFileName(NULL, szBuf, sizeof(szBuf)))
return FALSE;
lstrcpy(szPath, szBuf);
if (RegSetValue(hkeyT, TEXT("LocalServer32"), REG_SZ, szBuf, lstrlen(szBuf))
!= ERROR_SUCCESS)
goto LErrExit;
RegCloseKey(hkeyT);
hkeyT = NULL;
// Register the ICOMPerformance MIDL-generated proxy-stub component
hLib = LoadLibrary(TEXT("psperf.dll"));
if (NULL == hLib)
{
goto LErrExit;
}
// Find entry point.
lpfn = (LPFNREG)GetProcAddress(hLib, TEXT("DllRegisterServer"));
if (lpfn == NULL)
{
//Message(_T("Couldn't find entry point in DLL"), S_OK); //unable to locate entry point
goto LErrExit;
}
hr = (*lpfn)();

FreeLibrary(hLib);
if(SUCCEEDED(hr))
return TRUE;
LErrExit:
if(NULL != hkeyT)
RegCloseKey(hkeyT);
return FALSE;
} // FAutoRegister

// ===========================================================================
// C C L A S S F A C T O R Y
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: CClassFactory::QueryInterface
// Returns a new reference of the specified iid-type to a CClassFactory.
// ---------------------------------------------------------------------------
STDMETHODIMP
CClassFactory::QueryInterface(REFIID iid, void **ppv)
{
*ppv = NULL;

if (iid == IID_IClassFactory || iid == IID_IUnknown)
{
*ppv = (IClassFactory *)this;
}
if (*ppv != NULL)
{
AddRef();
return S_OK;
}
return E_NOINTERFACE;
} // CClassFactory::QueryInterface

// ---------------------------------------------------------------------------
// %%Function: CClassFactory::CreateInstance
// Creates a new instance of a CTestCOMPerformance on the next worker thread.
// ---------------------------------------------------------------------------
STDMETHODIMP
CClassFactory::CreateInstance(LPUNKNOWN punkOuter, REFIID iid, void **ppv)
{
LPUNKNOWN punk;
HRESULT hr;

*ppv = NULL;
if (punkOuter != NULL)
return CLASS_E_NOAGGREGATION;

// trigger the worker thread that we want to create an object
SetEvent(vrghEvent[viNextThread]);

// now wait for the object to signal its completion
WaitForSingleObject(vhEventServer, INFINITE);

// once the worker thread signals completion, vhrThreadStatus
// lets us know if the creation process was successful, and if
// vpstmMarshalling creates a marshalled interface pointer
if (FAILED(vhrThreadStatus))
return vhrThreadStatus;

// unmarshal an IUnknown from the scratch stream. if unmarshaling
// fails, it takes care of releasing the object inside the marshal-data
hr = vpstmMarshalling->Seek(bZero, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
return hr;
hr = CoUnmarshalInterface(vpstmMarshalling, IID_IUnknown, (void **)&punk);
if (FAILED(hr))
return hr;

// get a reference to the interface asked for
hr = punk->QueryInterface(iid, ppv);
punk->Release();
++g_cObjectCount;
viNextThread++;
// viNextThread %= cServerThreads;

return hr;
} // CClassFactory::CreateInstance

STDMETHODIMP
CClassFactory::LockServer(BOOL fLock)
{
// there's no need to support this for this sample
return E_FAIL;
} // CClassFactory::LockServer


// ===========================================================================
// C O B J E C T
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: ServerThreadProc
// The worker thread function. Handles messages for objects of its thread/apt
// and creates new objects.
// ---------------------------------------------------------------------------
LRESULT
ServerThreadProc(LPARAM lParam)
{
HRESULT hr;
MSG msg;
int iThread;

// figure out which thread this is: it needs its synchronization event
for (iThread=0; iThread<cServerThreads; iThread++)
{
if (vrgtid[iThread] == GetCurrentThreadId())
break;
}
if (iThread==cServerThreads)
return E_UNEXPECTED;

// initialize COM
if((0 == iThread) || (2 == iThread))
{
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
Message(TEXT("Apartment Thread"), hr);
}
else
{
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
Message(TEXT("Free-threaded Model"), hr);
}
if (FAILED(hr))
{
MessageBeep(0);
return hr;
}

// apartment message/event loop
// here worker message loops last forever. in situations without a
// static number of worker threads, the loop could easily be terminated by
// WM_QUITs sent from the main thread which might manage the worker thread
// pool more carefully.
while (TRUE)
{
DWORD dwWaitResult;

// wait for any message sent or posted to this queue
// or for one of the passed handles to become signaled
dwWaitResult = MsgWaitForMultipleObjects(1, &vrghEvent[iThread],
FALSE, INFINITE, QS_ALLINPUT);

// result tells us the type of event we have:
// a message or a signaled handle

// if there are one or more messages in the queue ...
if (dwWaitResult == (WAIT_OBJECT_0 + 1))
{
// dispatch all of the messages in this next loop
// (here is where we'd check for WM_QUITs to end this
// worker thread if we wanted to)
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
}
else
{
// this thread was signaled to create a new object
try
{
LPUNKNOWN punk;

// create a new CTestCOMPerformance
punk = (ICOMPerformance *)new CTestCOMPerformance;
if (punk == NULL)
throw E_OUTOFMEMORY;

hr = vpstmMarshalling->Seek(bZero, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
throw hr;
hr = CoMarshalInterface(vpstmMarshalling,
IID_IUnknown,
punk,
MSHCTX_INPROC,
NULL,
MSHLFLAGS_NORMAL);
if (FAILED(hr))
throw hr;

// punk is now referenced by its marshal-data in vpstmMarshalling.
// we release our local reference here so the unmarshaller will
// have the sole reference. a common mistake is to forget this
// release and end up with orphaned objects in the server.
punk->Release();
vhrThreadStatus = S_OK;
}
catch (HRESULT hr)
{
vhrThreadStatus = hr;
}
SetEvent(vhEventServer);
}

}

CoUninitialize();
return msg.wParam;
} // ServerThreadProc

// ---------------------------------------------------------------------------
// %%Function: ServerThreadProc
// The worker thread function. Handles messages for objects of its thread/apt
// and creates new objects.

// --------------------------------------------------------------------------- 
LRESULT
ClientThreadProc(LPARAM lParam)
{
HRESULT hr;
MSG msg;
UINT cCount = 0;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
{
return hr;
}
while (TRUE)
{
DWORD dwWaitResult;

// wait for any message sent or posted to this queue
// or for one of the passed handles to become signaled
dwWaitResult = MsgWaitForMultipleObjectsEx(1, &vhEventCliStart,
INFINITE, QS_SENDMESSAGE, 0);

// result tells us the type of event we have:
// a message or a signaled handle

// if there are one or more messages in the queue ...
if (dwWaitResult == (WAIT_OBJECT_0 + 1))
{
// dispatch all of the messages in this next loop
// (here is where we'd check for WM_QUITs to end this
// worker thread if we wanted to)
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
}
else
{
// this thread was signaled to test Apt to Apt
try
{
//Message(TEXT("Testing Apt to Apt"), hr);
LPUNKNOWN punk;
LPDISPATCH pdisp;
// unmarshal an IUnknown from the scratch stream. if unmarshaling
// fails, it takes care of releasing the object inside the marshal-data
hr = vpstmMarshalling->Seek(bZero, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
return hr;
hr = CoUnmarshalInterface(vpstmMarshalling, IID_IUnknown, (void **)&punk);
if (FAILED(hr))
return hr;
ICOMPerformance *pcomperf = NULL;
// get a reference to ICOMPerformance and IDispatch interface
hr = punk->QueryInterface(IID_ICOMPerformance, (void**)&pcomperf);
hr = punk->QueryInterface(IID_IDispatch, (void**)&pdisp);
LONG i, k;
CTIMER tmElapsed;
BSTR bstr1, bstr2;
bstr1 = SysAllocString(pszDesc1);
bstr2 = SysAllocStringByteLen((const char *)pszDesc1, 4096);
// Test1
tmElapsed.Start();
for (i=0; i<LOOPS; i++)
k = (LONG)pcomperf->Test1(i);
tmElapsed.Stop();
if(cCount && vfRemote)
vrgperfrmt[4].sec[0] = tmElapsed.OutputTime();
else
vrgperfloc[4].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
for (i=0; i<LOOPS; i++)
k = (LONG)pcomperf->Test23(bstr1);
tmElapsed.Stop();
if(cCount && vfRemote)
vrgperfrmt[4].sec[1] = tmElapsed.OutputTime();
else
vrgperfloc[4].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
for (i=0; i<LOOPS; i++)
k = (LONG)pcomperf->Test23(bstr2);
tmElapsed.Stop();
if(cCount && vfRemote)
vrgperfrmt[4].sec[2] = tmElapsed.OutputTime();
else
vrgperfloc[4].sec[2] = tmElapsed.OutputTime();
// Now IDispatch
VARIANTARG rgvt[1];
DISPPARAMS dispparams = { rgvt, NULL, 1, 0 };
EXCEPINFO excepinfo;

VARIANT vtResult;
UINT argerr;
// Test1
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<LOOPS; i++)
{
V_VT(&rgvt[0]) = VT_I4;
V_I4(&rgvt[0]) = i;
hr = pdisp->Invoke(dispidICOMPerformance_Test1, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
if(cCount && vfRemote)
vrgperfrmt[5].sec[0] = tmElapsed.OutputTime();
else
vrgperfloc[5].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<LOOPS; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr1;
hr = pdisp->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
if(cCount && vfRemote)
vrgperfrmt[5].sec[1] = tmElapsed.OutputTime();
else
vrgperfloc[5].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<LOOPS; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr2;
hr = pdisp->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
if(cCount && vfRemote)
vrgperfrmt[5].sec[2] = tmElapsed.OutputTime();
else
vrgperfloc[5].sec[2] = tmElapsed.OutputTime();
SysFreeString(bstr1);
SysFreeString(bstr2);
hr = pcomperf->Release();
hr = pdisp->Release();
hr = punk->Release();

vhrThreadStatus = S_OK;
}
catch (HRESULT hr)
{
vhrThreadStatus = hr;
}
SetEvent(vhEventCliDone);
if(cCount)
--cCount;
else
++cCount;
}
}
CoUninitialize();
return msg.wParam;
} // ClientThreadProc
// ---------------------------------------------------------------------------
// %%Function: CTestCOMPerformance::QueryInterface
// Returns a new reference of the specified iid-type to a CTestCOMPerformance.
// ---------------------------------------------------------------------------
STDMETHODIMP
CTestCOMPerformance::QueryInterface(REFIID iid, void **ppv)
{
*ppv = NULL;

if (iid == IID_IUnknown || iid == IID_ICOMPerformance)
{
*ppv = (ICOMPerformance *)this;
}
else if (iid == IID_IDispatch)
*ppv = (IDispatch *)this;
if (*ppv == NULL)
{
return E_NOINTERFACE;
}
AddRef();
return S_OK;
} // CTestCOMPerformance::QueryInterface

// ---------------------------------------------------------------------------
// %%Function: CTestCOMPerformance::Release
// Handles releases of references to a CTestCOMPerformance. Purpose here is to have code
// which alters the global state which is displayed in the servers UI.
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CTestCOMPerformance::Release(void)
{
if (--m_cRef == 0)
{
--g_cObjectCount;
delete this;
Message(TEXT("Object Deleted"), S_OK);
if(g_cObjectCount == 0) SetEvent(hKillSvr);
return 0;
}
return m_cRef;
} // CTestCOMPerformance::Release

// ---------------------------------------------------------------------------
// %%Function: CTestCOMPerformance::GetTypeInfoCount
// ---------------------------------------------------------------------------
STDMETHODIMP
CTestCOMPerformance::GetTypeInfoCount(UINT *pctInfo)
{
*pctInfo = 1;
return S_OK;
} // CTestCOMPerformance::GetTypeInfoCount

// ---------------------------------------------------------------------------
// %%Function: CTestCOMPerformance::GetTypeInfo
// ---------------------------------------------------------------------------
STDMETHODIMP
CTestCOMPerformance::GetTypeInfo(unsigned int itInfo, LCID lcid, LPTYPEINFO *ppti)
{
HRESULT hr;

if (ppti == NULL)
return E_POINTER;
hr = CreateDispTypeInfo( &interfacedata, 0, ppti);
return hr;
} // CTestCOMPerformance::GetTypeInfo

// ---------------------------------------------------------------------------
// %%Function: CTestCOMPerformance::GetIDsOfNames
// ---------------------------------------------------------------------------
STDMETHODIMP
CTestCOMPerformance::GetIDsOfNames(REFIID riid, WCHAR * *rgszNames, unsigned int cNames, LCID lcid, DISPID *pdispid)
{
return E_NOTIMPL;
} // CTestCOMPerformance::GetIDsOfNames

// ---------------------------------------------------------------------------
// %%Function: CTestCOMPerformance::Invoke
// ---------------------------------------------------------------------------
STDMETHODIMP
CTestCOMPerformance::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, unsigned short wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult,
EXCEPINFO *pexcepinfo, unsigned int *puArgErr)
{
LPTYPEINFO pti = NULL;
HRESULT hr = GetTypeInfo(0, lcid, &pti);
if(FAILED(hr))
return hr;

hr = pti->Invoke((ICOMPerformance*)this, dispidMember,
wFlags, pdispparams, pvarResult,
pexcepinfo, puArgErr);
hr = pti->Release();
return hr;
} // CTestCOMPerformance::Invoke


STDMETHODIMP
CTestCOMPerformance::Test1(int l)
{
return S_OK;
}

STDMETHODIMP
CTestCOMPerformance::Test23(BSTR bstr)
{
return S_OK;
}

// Tests
BOOL
AptFreeCOMTest(int cLoops)
{
LONG i, k;
CTIMER tmElapsed;
BSTR bstr1, bstr2;
bstr1 = SysAllocString(pszDesc1);
bstr2 = SysAllocStringByteLen((const char *)pszDesc1, 4096);
// Local
// Test1
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMApt->Test1(i);
tmElapsed.Stop();
vrgperfloc[0].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMApt->Test23(bstr1);
tmElapsed.Stop();
vrgperfloc[0].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMApt->Test23(bstr2);
tmElapsed.Stop();
vrgperfloc[0].sec[2] = tmElapsed.OutputTime();

// Remote
if(vfRemote)
{
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMAptRmt->Test1(i);
tmElapsed.Stop();
vrgperfrmt[0].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMAptRmt->Test23(bstr1);
tmElapsed.Stop();
vrgperfrmt[0].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMAptRmt->Test23(bstr2);
tmElapsed.Stop();
vrgperfrmt[0].sec[2] = tmElapsed.OutputTime();

}
SysFreeString(bstr1);
SysFreeString(bstr2);
return TRUE;
}

BOOL
AptFreeAutoTest(int cLoops)
{
HRESULT hr;
static VARIANTARG rgvt[1];
static DISPPARAMS dispparams = { rgvt, NULL, 1, 0 };
static EXCEPINFO excepinfo;
VARIANT vtResult;
UINT argerr;
LONG i, k;
CTIMER tmElapsed;
BSTR bstr1, bstr2;
bstr1 = SysAllocString(pszDesc1);
bstr2 = SysAllocStringByteLen((const char *)pszDesc1, 4096);

// Test1
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_I4;
V_I4(&rgvt[0]) = i;
hr = pAutoApt->Invoke(dispidICOMPerformance_Test1, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfloc[1].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr1;
hr = pAutoApt->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfloc[1].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr2;
hr = pAutoApt->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfloc[1].sec[2] = tmElapsed.OutputTime();
// Remote
if(vfRemote)
{
// Test1
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_I4;
V_I4(&rgvt[0]) = i;
hr = pAutoAptRmt->Invoke(dispidICOMPerformance_Test1, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfrmt[1].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr1;
hr = pAutoAptRmt->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfrmt[1].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr2;
hr = pAutoAptRmt->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfrmt[1].sec[2] = tmElapsed.OutputTime();
}

SysFreeString(bstr1);
SysFreeString(bstr2);
return TRUE;
}

BOOL
FreeFreeCOMTest(int cLoops)
{
LONG i, k;
CTIMER tmElapsed;
BSTR bstr1, bstr2;
bstr1 = SysAllocString(pszDesc1);
bstr2 = SysAllocStringByteLen((const char *)pszDesc1, 4096);

// Test1
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMFree->Test1(i);
tmElapsed.Stop();
vrgperfloc[2].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMFree->Test23(bstr1);
tmElapsed.Stop();
vrgperfloc[2].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
for (i=0; i<cLoops; i++)
{
k = (LONG)pCOMFree->Test23(bstr2);
}
tmElapsed.Stop();
vrgperfloc[2].sec[2] = tmElapsed.OutputTime();

// Remote
if(vfRemote)
{
// Test1
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMFreeRmt->Test1(i);
tmElapsed.Stop();
vrgperfrmt[2].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
for (i=0; i<cLoops; i++)
k = (LONG)pCOMFreeRmt->Test23(bstr1);
tmElapsed.Stop();
vrgperfrmt[2].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
for (i=0; i<cLoops; i++)
{
k = (LONG)pCOMFreeRmt->Test23(bstr2);
}
tmElapsed.Stop();
vrgperfrmt[2].sec[2] = tmElapsed.OutputTime();
}

SysFreeString(bstr1);
SysFreeString(bstr2);
return TRUE;
}

BOOL
FreeFreeAutoTest(int cLoops)
{
HRESULT hr;
static VARIANTARG rgvt[1];
static DISPPARAMS dispparams = { rgvt, NULL, 1, 0 };
static EXCEPINFO excepinfo;
VARIANT vtResult;
UINT argerr;
LONG i, k;
CTIMER tmElapsed;
BSTR bstr1, bstr2;
bstr1 = SysAllocString(pszDesc1);
bstr2 = SysAllocStringByteLen((const char *)pszDesc1, 4096);

// Local
// Test1
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_I4;
V_I4(&rgvt[0]) = i;
hr = pAutoFree->Invoke(dispidICOMPerformance_Test1, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfloc[3].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr1;
hr = pAutoFree->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfloc[3].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr2;
hr = pAutoFree->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfloc[3].sec[2] = tmElapsed.OutputTime();

// Remote
if(vfRemote)
{
// Test1
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_I4;
V_I4(&rgvt[0]) = i;
hr = pAutoFreeRmt->Invoke(dispidICOMPerformance_Test1, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfrmt[3].sec[0] = tmElapsed.OutputTime();
// Test2
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr1;
hr = pAutoFreeRmt->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfrmt[3].sec[1] = tmElapsed.OutputTime();
// Test3
tmElapsed.Start();
V_VT(&vtResult) = VT_HRESULT;
for (i=0; i<cLoops; i++)
{
V_VT(&rgvt[0]) = VT_BSTR;
V_BSTR(&rgvt[0]) = bstr2;
hr = pAutoFreeRmt->Invoke(dispidICOMPerformance_Test23, IID_NULL, 0, DISPATCH_METHOD, &dispparams,
&vtResult, &excepinfo, &argerr);
k = V_I4(&vtResult);
}
tmElapsed.Stop();
vrgperfrmt[3].sec[2] = tmElapsed.OutputTime();
}
SysFreeString(bstr1);
SysFreeString(bstr2);
return TRUE;
}

void
FPrintResults()
{
_tprintf(TEXT("Data Size\t\t4\t50\t4k\t4\t50\t4k\n"));
_tprintf(TEXT("%s\t\t"), vrgperfloc[0].szTest);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[0].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[0].sec[1]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[0].sec[2]);
if(vfRemote)
{
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[0].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[0].sec[1]);
_tprintf(TEXT("%4.2f\n"), LOOPS/vrgperfrmt[0].sec[2]);
}
else
{
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\n"));
}
_tprintf(TEXT("%s\t\t"), vrgperfloc[1].szTest);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[1].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[1].sec[1]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[1].sec[2]);
if(vfRemote)
{
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[1].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[1].sec[1]);
_tprintf(TEXT("%4.2f\n"), LOOPS/vrgperfrmt[1].sec[2]);
}
else
{
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\n"));
}
_tprintf(TEXT("%s\t\t"), vrgperfloc[2].szTest);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[2].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[2].sec[1]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[2].sec[2]);
if(vfRemote)
{
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[2].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[2].sec[1]);
_tprintf(TEXT("%4.2f\n"), LOOPS/vrgperfrmt[2].sec[2]);
}
else
{
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\n"));
}
_tprintf(TEXT("%s\t\t"), vrgperfloc[3].szTest);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[3].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[3].sec[1]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[3].sec[2]);
if(vfRemote)
{
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[3].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[3].sec[1]);
_tprintf(TEXT("%4.2f\n"), LOOPS/vrgperfrmt[3].sec[2]);
}
else
{
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\n"));
}
_tprintf(TEXT("%s\t\t"), vrgperfloc[4].szTest);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[4].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[4].sec[1]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[4].sec[2]);
if(vfRemote)
{
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[4].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[4].sec[1]);
_tprintf(TEXT("%4.2f\n"), LOOPS/vrgperfrmt[4].sec[2]);
}
else
{
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\n"));
}
_tprintf(TEXT("%s\t\t"), vrgperfloc[5].szTest);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[5].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[5].sec[1]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfloc[5].sec[2]);
if(vfRemote)
{
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[5].sec[0]);
_tprintf(TEXT("%4.2f\t"), LOOPS/vrgperfrmt[5].sec[1]);
_tprintf(TEXT("%4.2f\n"), LOOPS/vrgperfrmt[5].sec[2]);
}
else
{
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\t"));
_tprintf(TEXT("--\n"));
}


}

BOOL
DoTests(void)
{
if(!AptFreeCOMTest(LOOPS))
return FALSE;
if(!AptFreeAutoTest(LOOPS))
return FALSE;
if(!FreeFreeCOMTest(LOOPS))
return FALSE;
if(!FreeFreeAutoTest(LOOPS))
return FALSE;
FPrintResults();
return TRUE;
} // DoTests

void Usage()
{
_tprintf(TEXT("Usage: COMPERF [machine name | IP address | /? | -?]\n\n"));
_tprintf(TEXT("/?\t\t\tDisplays this help screen.\n"));
_tprintf(TEXT("machine name\t\tName of remote machine.\n"));
_tprintf(TEXT("IP address\t\tIP address of remote machine.\n\n"));
_tprintf("%s", PURPOSE);
}

// EOF =======================================================================