1132 lines
28 KiB
C++
1132 lines
28 KiB
C++
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// File: uthread.cpp
|
||
|
//
|
||
|
// Contents: Unit test for various OLE threading model features
|
||
|
//
|
||
|
// Classes: SSTParamBlock
|
||
|
// SSTParamBlock
|
||
|
// SBTParamBlock
|
||
|
//
|
||
|
// Functions: CreateTestThread
|
||
|
// VerifyTestObject
|
||
|
// CheckForDllExistence
|
||
|
// GetDllDirectory
|
||
|
// SetRegForDll
|
||
|
// SetSingleThreadRegEntry
|
||
|
// SetAptThreadRegEntry
|
||
|
// SetBothThreadRegEntry
|
||
|
// SingleThreadTestThread
|
||
|
// AptTestThread
|
||
|
// BothTestThread
|
||
|
// SetUpRegistry
|
||
|
// TestSingleThread
|
||
|
// TestAptThread
|
||
|
// TestBothDll
|
||
|
// TestFreeAllLibraries
|
||
|
// ThreadUnitTest
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
#include <windows.h>
|
||
|
#include <ole2.h>
|
||
|
#include <uthread.h>
|
||
|
#include <cotest.h>
|
||
|
|
||
|
// Test single threaded DLL - all operations s/b executed on the main thread.
|
||
|
// Pointers between threads s/b different. Test loading class object from
|
||
|
// different than the main thread.
|
||
|
|
||
|
// Test apartment model - all operations should occur on the thread the
|
||
|
// object was created on. This should also test the helper APIs. Pointers
|
||
|
// between threads s/b different. This tests helper APIs.
|
||
|
|
||
|
// Both model DLL. We want to make sure that the marshaling works between
|
||
|
// threads so that you get the same pointer. This tests new marshal context.
|
||
|
|
||
|
// Test Free Unused Libraries from non-main thread. Test FreeUnused libraries
|
||
|
// from main thread.
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: SSTParamBlock
|
||
|
//
|
||
|
// Purpose: Parameter block for single threaded dll test.
|
||
|
//
|
||
|
// Interface:
|
||
|
//
|
||
|
// History: 01-Nov-92 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
struct SSTParamBlock
|
||
|
{
|
||
|
HANDLE hEvent;
|
||
|
BOOL fResult;
|
||
|
IClassFactory * pcf;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: SSTParamBlock
|
||
|
//
|
||
|
// Purpose: Parameter block for apt model threaded dll test.
|
||
|
//
|
||
|
// Interface:
|
||
|
//
|
||
|
// History: 01-Nov-92 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
struct SATParamBlock
|
||
|
{
|
||
|
HANDLE hEvent;
|
||
|
BOOL fResult;
|
||
|
IClassFactory * pcf;
|
||
|
IStream * pstrm;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: SBTParamBlock
|
||
|
//
|
||
|
// Purpose: Parameter block for both model dll test.
|
||
|
//
|
||
|
// Interface:
|
||
|
//
|
||
|
// History: 02-Nov-92 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
struct SBTParamBlock
|
||
|
{
|
||
|
HANDLE hEvent;
|
||
|
BOOL fResult;
|
||
|
IClassFactory * pcf;
|
||
|
IStream * pstrm;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
const TCHAR *pszRegValThreadModel = TEXT("ThreadingModel");
|
||
|
const TCHAR *pszApartmentModel = TEXT("Apartment");
|
||
|
const TCHAR *pszBoth = TEXT("Both");
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: ThreadWaitForEvent, private
|
||
|
//
|
||
|
// Synopsis: Process messages until event becomes signaled
|
||
|
//
|
||
|
// Arguments: [lphObject] - handle to become signaled
|
||
|
//
|
||
|
// History: 02-Nov-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
void ThreadWaitForEvent(HANDLE hObject)
|
||
|
{
|
||
|
// message loop lasts until we get a WM_QUIT message
|
||
|
// upon which we shall return from the function
|
||
|
while (TRUE)
|
||
|
{
|
||
|
// wait for any message sent or posted to this queue
|
||
|
// or for one of the passed handles to become signaled
|
||
|
DWORD result = MsgWaitForMultipleObjects(1, &hObject,
|
||
|
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 (result == (WAIT_OBJECT_0 + 1))
|
||
|
{
|
||
|
// block-local variable
|
||
|
MSG msg;
|
||
|
|
||
|
// read all of the messages in this next loop
|
||
|
// removing each message as we read it
|
||
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||
|
{
|
||
|
|
||
|
// if it's a quit message we're out of here
|
||
|
if (msg.message == WM_QUIT)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// otherwise dispatch it
|
||
|
DispatchMessage(&msg);
|
||
|
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Event got signaled so we are done.
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: CreateTestThread, private
|
||
|
//
|
||
|
// Synopsis: Create a test thread in standard way
|
||
|
//
|
||
|
// Arguments: [lpStartAddr] - start routine address
|
||
|
// [pvThreadArg] - argument to pass to the thread
|
||
|
//
|
||
|
// Returns: TRUE - Thread created successfully
|
||
|
// FALSE - Thread could not be created.
|
||
|
//
|
||
|
// History: 02-Nov-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL CreateTestThread(
|
||
|
LPTHREAD_START_ROUTINE lpStartAddr,
|
||
|
void *pvThreadArg)
|
||
|
{
|
||
|
// Where to put the thread ID that we don't care about
|
||
|
DWORD dwThreadId;
|
||
|
|
||
|
// Create thread to load single threaded object
|
||
|
HANDLE hThread = CreateThread(
|
||
|
NULL, // Default security descriptor
|
||
|
0, // Default stack
|
||
|
lpStartAddr, // Start routine
|
||
|
pvThreadArg, // Parameters to pass to the thread
|
||
|
0, // Thread runs immediately after creation
|
||
|
&dwThreadId); // Where to return thread id (unused).
|
||
|
|
||
|
CloseHandle(hThread);
|
||
|
|
||
|
return hThread != NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: VerifyTestObject, private
|
||
|
//
|
||
|
// Synopsis: Create a test DLL object in standard way
|
||
|
//
|
||
|
// Arguments: [pcf] - start routine address
|
||
|
// [rclsid] - clsid to check
|
||
|
//
|
||
|
// Returns: TRUE - Object behaved as expected
|
||
|
// FALSE - Object did not behave
|
||
|
//
|
||
|
// History: 02-Nov-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL VerifyTestObject(
|
||
|
IClassFactory *pcf,
|
||
|
REFCLSID rclsid)
|
||
|
{
|
||
|
// Result from test
|
||
|
BOOL fResult = FALSE;
|
||
|
|
||
|
// Pointer to unknown for the object
|
||
|
IUnknown *punk = NULL;
|
||
|
|
||
|
// Pointer to IPersist interface
|
||
|
IPersist *pIPersist = NULL;
|
||
|
|
||
|
// Create an instance of an object
|
||
|
if (pcf->CreateInstance(NULL, IID_IUnknown, (void **) &punk) == NOERROR)
|
||
|
{
|
||
|
// Do a QI to confirm object behaves correctly
|
||
|
if (punk->QueryInterface(IID_IPersist, (void **) &pIPersist) == NOERROR)
|
||
|
{
|
||
|
CLSID clsidTest;
|
||
|
|
||
|
// Make sure we can actually call through to the proxy object.
|
||
|
if ((pIPersist->GetClassID(&clsidTest) == NOERROR)
|
||
|
&& IsEqualCLSID(clsidTest, rclsid))
|
||
|
{
|
||
|
fResult = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (punk != NULL)
|
||
|
{
|
||
|
punk->Release();
|
||
|
}
|
||
|
|
||
|
if (pIPersist != NULL)
|
||
|
{
|
||
|
pIPersist->Release();
|
||
|
}
|
||
|
|
||
|
return fResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: GetFullDllName, private
|
||
|
//
|
||
|
// Synopsis: Get the directory for the registration for the test.
|
||
|
//
|
||
|
// Arguments: [pszDllName] - DLL name
|
||
|
// [pszFullDllName] - output buffer for DLL path
|
||
|
//
|
||
|
// Returns: TRUE - we could get the path for the DLL
|
||
|
// FALSE - we couldn't figure out what to use.
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL GetFullDllName(const TCHAR *pszDllName, TCHAR *pszFullDllName)
|
||
|
{
|
||
|
// Use windows to tell us what DLL we would load.
|
||
|
HINSTANCE hinstDll = LoadLibraryEx(pszDllName, NULL,
|
||
|
DONT_RESOLVE_DLL_REFERENCES | LOAD_WITH_ALTERED_SEARCH_PATH);
|
||
|
|
||
|
if (hinstDll == NULL)
|
||
|
{
|
||
|
// We could not find the DLL so there isn't much purpose in
|
||
|
// continuing the test.
|
||
|
MessageBox(NULL, TEXT("LoadLibraryEx Failed!"),
|
||
|
TEXT("FATAL ERROR"), MB_OK);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Get the DLLs path name
|
||
|
if (!GetModuleFileName(hinstDll, pszFullDllName, MAX_PATH))
|
||
|
{
|
||
|
// How can this fail?? -- anyway we better tell someone.
|
||
|
MessageBox(NULL, TEXT("Threading Test GetModuleFileName Failed!"),
|
||
|
TEXT("FATAL ERROR"), MB_OK);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
FreeLibrary(hinstDll);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: SetRegForDll, private
|
||
|
//
|
||
|
// Synopsis: Set registry entry for a DLL
|
||
|
//
|
||
|
// Arguments: [rclsid] - clsid for reg entry
|
||
|
// [pszDir] - directory for DLL path
|
||
|
// [pszDllName] - name to use for DLL
|
||
|
// [pszThreadModel] - threading model can be NULL.
|
||
|
//
|
||
|
// Returns: TRUE - Registry entry set successfully.
|
||
|
// FALSE - Registry entry set successfully.
|
||
|
//
|
||
|
// History: 01-Nov-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL SetRegForDll(
|
||
|
REFCLSID rclsid,
|
||
|
const TCHAR *pszDllName,
|
||
|
const TCHAR *pszThreadModel)
|
||
|
{
|
||
|
// Result returned by function
|
||
|
BOOL fResult = FALSE;
|
||
|
|
||
|
// String buffer used for various purposes
|
||
|
TCHAR aszWkBuf[MAX_PATH];
|
||
|
|
||
|
// Key to class
|
||
|
HKEY hKeyClass = NULL;
|
||
|
|
||
|
// Key to DLL entry
|
||
|
HKEY hKeyDll = NULL;
|
||
|
|
||
|
// Build clsid registry key
|
||
|
wsprintf(aszWkBuf,
|
||
|
TEXT("CLSID\\{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
|
||
|
rclsid.Data1, rclsid.Data2, rclsid.Data3,
|
||
|
rclsid.Data4[0], rclsid.Data4[1],
|
||
|
rclsid.Data4[2], rclsid.Data4[3],
|
||
|
rclsid.Data4[4], rclsid.Data4[5],
|
||
|
rclsid.Data4[6], rclsid.Data4[7]);
|
||
|
|
||
|
// Create the key for the class
|
||
|
if (ERROR_SUCCESS != RegCreateKey(HKEY_CLASSES_ROOT, aszWkBuf, &hKeyClass))
|
||
|
{
|
||
|
goto SetSingleThreadRegEntryExit;
|
||
|
}
|
||
|
|
||
|
// Create the key for the DLL
|
||
|
|
||
|
if (ERROR_SUCCESS != RegCreateKey(hKeyClass, TEXT("InprocServer32"),
|
||
|
&hKeyDll))
|
||
|
{
|
||
|
goto SetSingleThreadRegEntryExit;
|
||
|
}
|
||
|
|
||
|
// Build the DLL name
|
||
|
if (!GetFullDllName(pszDllName, &aszWkBuf[0]))
|
||
|
{
|
||
|
goto SetSingleThreadRegEntryExit;
|
||
|
}
|
||
|
|
||
|
OutputDebugString(&aszWkBuf[0]);
|
||
|
|
||
|
// Set the value for the DLL name
|
||
|
if (ERROR_SUCCESS != RegSetValue(hKeyDll, NULL, REG_SZ, aszWkBuf,
|
||
|
lstrlen(aszWkBuf)))
|
||
|
{
|
||
|
goto SetSingleThreadRegEntryExit;
|
||
|
}
|
||
|
|
||
|
// Set the threading model if there is one
|
||
|
if (pszThreadModel != NULL)
|
||
|
{
|
||
|
// Set the value for the DLL name
|
||
|
if (ERROR_SUCCESS != RegSetValueEx(hKeyDll, pszRegValThreadModel, 0,
|
||
|
REG_SZ, (const unsigned char*) pszThreadModel,
|
||
|
lstrlen(pszThreadModel) + 1))
|
||
|
{
|
||
|
goto SetSingleThreadRegEntryExit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fResult = TRUE;
|
||
|
|
||
|
SetSingleThreadRegEntryExit:
|
||
|
|
||
|
if (hKeyClass != NULL)
|
||
|
{
|
||
|
RegCloseKey(hKeyClass);
|
||
|
}
|
||
|
|
||
|
if (hKeyDll != NULL)
|
||
|
{
|
||
|
RegCloseKey(hKeyDll);
|
||
|
}
|
||
|
|
||
|
if (!fResult)
|
||
|
{
|
||
|
wsprintf(aszWkBuf, TEXT("Registry Setup For %s Failed"), pszDllName);
|
||
|
|
||
|
MessageBox(NULL, aszWkBuf, TEXT("FATAL ERROR"), MB_OK);
|
||
|
}
|
||
|
|
||
|
return fResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: SingleThreadTestThread, private
|
||
|
//
|
||
|
// Synopsis: Verify single threaded object call correctly from non
|
||
|
// main thread.
|
||
|
//
|
||
|
// Arguments: [pvCtrlData] - control data for the thread
|
||
|
//
|
||
|
// Returns: 0 - interesting values returned through pvCtrlData.
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
DWORD SingleThreadTestThread(void *pvCtrlData)
|
||
|
{
|
||
|
// Data shared with main thread
|
||
|
SSTParamBlock *psstp = (SSTParamBlock *) pvCtrlData;
|
||
|
|
||
|
psstp->fResult = FALSE;
|
||
|
|
||
|
// Local class factory object.
|
||
|
IClassFactory *pcf = NULL;
|
||
|
|
||
|
// IUnknown ptrs used for multiple purposes
|
||
|
IUnknown *punk = NULL;
|
||
|
|
||
|
// Initialize thread
|
||
|
if (CoInitialize(NULL) != NOERROR)
|
||
|
{
|
||
|
goto SingleThreadTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Get the class object
|
||
|
if (CoGetClassObject(clsidSingleThreadedDll, CLSCTX_INPROC, NULL,
|
||
|
IID_IClassFactory, (void **) &pcf) != NOERROR)
|
||
|
{
|
||
|
goto SingleThreadTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Make sure main thread's ptr is not the same as this thread's ptr.
|
||
|
if (pcf == psstp->pcf)
|
||
|
{
|
||
|
goto SingleThreadTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Confirm that class object is a proxy
|
||
|
if (pcf->QueryInterface(IID_IProxyManager, (void **) &punk) == NOERROR)
|
||
|
{
|
||
|
// Verify that we can play with an object.
|
||
|
psstp->fResult = VerifyTestObject(pcf, clsidSingleThreadedDll);
|
||
|
}
|
||
|
|
||
|
SingleThreadTestThreadExit:
|
||
|
|
||
|
if (pcf != NULL)
|
||
|
{
|
||
|
pcf->Release();
|
||
|
}
|
||
|
|
||
|
if (punk != NULL)
|
||
|
{
|
||
|
punk->Release();
|
||
|
}
|
||
|
|
||
|
// Exit the thread.
|
||
|
SetEvent(psstp->hEvent);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: AptTestThread, private
|
||
|
//
|
||
|
// Synopsis: Verify apt threaded object call correctly from thread
|
||
|
// if was not created on.
|
||
|
//
|
||
|
// Arguments: [pvCtrlData] - control data for the thread
|
||
|
//
|
||
|
// Returns: 0 - interesting values returned through pvCtrlData.
|
||
|
//
|
||
|
// History: 02-Nov-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
DWORD AptTestThread(void *pvCtrlData)
|
||
|
{
|
||
|
// Data shared with main thread
|
||
|
SATParamBlock *psatpb = (SATParamBlock *) pvCtrlData;
|
||
|
|
||
|
psatpb->fResult = FALSE;
|
||
|
|
||
|
// Class factory object unmarshaled from other thread.
|
||
|
IClassFactory *pcfUnmarshal = NULL;
|
||
|
|
||
|
// Class factory gotten from this thread
|
||
|
IClassFactory *pcfThisThread = NULL;
|
||
|
|
||
|
// IUnknown ptrs used for multiple purposes
|
||
|
IUnknown *punk = NULL;
|
||
|
|
||
|
// Initialize thread
|
||
|
if (CoInitialize(NULL) != NOERROR)
|
||
|
{
|
||
|
goto AptTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Get the class object from the marshaled stream
|
||
|
if (CoGetInterfaceAndReleaseStream(psatpb->pstrm, IID_IClassFactory,
|
||
|
(void **) &pcfUnmarshal) != NOERROR)
|
||
|
{
|
||
|
goto AptTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Caller doesn't have to release this now.
|
||
|
psatpb->pstrm = NULL;
|
||
|
|
||
|
// Make sure main thread's ptr is not the same as this thread's ptr.
|
||
|
if (pcfUnmarshal == psatpb->pcf)
|
||
|
{
|
||
|
goto AptTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Confirm that class object is a proxy
|
||
|
if (pcfUnmarshal->QueryInterface(IID_IProxyManager, (void **) &punk)
|
||
|
!= NOERROR)
|
||
|
{
|
||
|
goto AptTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Release the interface we got back and NULL it let the exit routine
|
||
|
// known that it does not have to clean this object up.
|
||
|
punk->Release();
|
||
|
punk = NULL;
|
||
|
|
||
|
if (!VerifyTestObject(pcfUnmarshal, clsidAptThreadedDll))
|
||
|
{
|
||
|
goto AptTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Get the class factory for this thread
|
||
|
if (CoGetClassObject(clsidAptThreadedDll, CLSCTX_INPROC, NULL,
|
||
|
IID_IClassFactory, (void **) &pcfThisThread) != NOERROR)
|
||
|
{
|
||
|
goto AptTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Make sure that it isn't the same as the one we unmarshaled
|
||
|
if (pcfUnmarshal == pcfThisThread)
|
||
|
{
|
||
|
goto AptTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Make sure the one we got for this not a proxy.
|
||
|
if (pcfThisThread->QueryInterface(IID_IProxyManager, (void **) &punk)
|
||
|
!= NOERROR)
|
||
|
{
|
||
|
psatpb->fResult = VerifyTestObject(pcfThisThread, clsidAptThreadedDll);
|
||
|
}
|
||
|
|
||
|
AptTestThreadExit:
|
||
|
|
||
|
if (pcfUnmarshal != NULL)
|
||
|
{
|
||
|
pcfUnmarshal->Release();
|
||
|
}
|
||
|
|
||
|
if (pcfThisThread != NULL)
|
||
|
{
|
||
|
pcfThisThread->Release();
|
||
|
}
|
||
|
|
||
|
if (punk != NULL)
|
||
|
{
|
||
|
punk->Release();
|
||
|
}
|
||
|
|
||
|
// Exit the thread.
|
||
|
SetEvent(psatpb->hEvent);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: BothTestThread, private
|
||
|
//
|
||
|
// Synopsis: Verify a DLL that supports both models is marshaled
|
||
|
// correctly.
|
||
|
//
|
||
|
// Arguments: [pvCtrlData] - control data for the thread
|
||
|
//
|
||
|
// Returns: 0 - interesting values returned through pvCtrlData.
|
||
|
//
|
||
|
// History: 02-Nov-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
DWORD BothTestThread(void *pvCtrlData)
|
||
|
{
|
||
|
// Data shared with main thread
|
||
|
SBTParamBlock *psbtpb = (SBTParamBlock *) pvCtrlData;
|
||
|
|
||
|
psbtpb->fResult = FALSE;
|
||
|
|
||
|
// Class factory object unmarshaled from other thread.
|
||
|
IClassFactory *pcfUnmarshal = NULL;
|
||
|
|
||
|
// IUnknown ptrs used for multiple purposes
|
||
|
IUnknown *punk = NULL;
|
||
|
IUnknown *pIPersist = NULL;
|
||
|
|
||
|
// Initialize thread
|
||
|
if (CoInitialize(NULL) != NOERROR)
|
||
|
{
|
||
|
goto BothTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Get the class object from the marshaled stream
|
||
|
if (CoGetInterfaceAndReleaseStream(psbtpb->pstrm, IID_IClassFactory,
|
||
|
(void **) &pcfUnmarshal) != NOERROR)
|
||
|
{
|
||
|
goto BothTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Caller doesn't have to release this now.
|
||
|
psbtpb->pstrm = NULL;
|
||
|
|
||
|
// Make sure main thread's ptr is not the same as this thread's ptr.
|
||
|
if (pcfUnmarshal != psbtpb->pcf)
|
||
|
{
|
||
|
goto BothTestThreadExit;
|
||
|
}
|
||
|
|
||
|
// Confirm that class object is a proxy
|
||
|
if (pcfUnmarshal->QueryInterface(IID_IProxyManager, (void **) &punk)
|
||
|
!= NOERROR)
|
||
|
{
|
||
|
// Make sure object created by the class works as expected
|
||
|
psbtpb->fResult = VerifyTestObject(pcfUnmarshal, clsidBothThreadedDll);
|
||
|
}
|
||
|
|
||
|
BothTestThreadExit:
|
||
|
|
||
|
if (pcfUnmarshal != NULL)
|
||
|
{
|
||
|
pcfUnmarshal->Release();
|
||
|
}
|
||
|
|
||
|
if (punk != NULL)
|
||
|
{
|
||
|
punk->Release();
|
||
|
}
|
||
|
|
||
|
// Exit the thread.
|
||
|
SetEvent(psbtpb->hEvent);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: SetUpRegistry, private
|
||
|
//
|
||
|
// Synopsis: Make sure registry is set up appropriately for the test
|
||
|
//
|
||
|
// Returns: TRUE - Registry set up successfully
|
||
|
// FALSE - Registry could not be set up
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL SetUpRegistry(void)
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
|
||
|
// Update the registry with the correct information
|
||
|
fRet = SetRegForDll(clsidSingleThreadedDll, pszSingleThreadedDll, NULL)
|
||
|
&& SetRegForDll(clsidAptThreadedDll, pszAptThreadedDll,
|
||
|
pszApartmentModel)
|
||
|
&& SetRegForDll(clsidBothThreadedDll, pszBothThreadedDll, pszBoth);
|
||
|
|
||
|
// Give Registry a chance to get updated
|
||
|
Sleep(1000);
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: TestSingleThread, private
|
||
|
//
|
||
|
// Synopsis: Driver to verify testing of single threaded behavior
|
||
|
//
|
||
|
// Returns: TRUE - Test Passed
|
||
|
// FALSE - Test Failed
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL TestSingleThread(void)
|
||
|
{
|
||
|
// Result of test - default to FALSE.
|
||
|
BOOL fResult = FALSE;
|
||
|
|
||
|
// Create an event for test to wait for completion of test.
|
||
|
SSTParamBlock sstp;
|
||
|
|
||
|
sstp.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
|
||
|
sstp.pcf = NULL;
|
||
|
|
||
|
if (sstp.hEvent == NULL)
|
||
|
{
|
||
|
goto TestSingleThreadExit;
|
||
|
}
|
||
|
|
||
|
// Create a class object and put in a parameter block
|
||
|
if (CoGetClassObject(clsidSingleThreadedDll, CLSCTX_INPROC, NULL,
|
||
|
IID_IClassFactory, (void **) &sstp.pcf) != NOERROR)
|
||
|
{
|
||
|
goto TestSingleThreadExit;
|
||
|
}
|
||
|
|
||
|
// Create the thread.
|
||
|
if (CreateTestThread(SingleThreadTestThread, &sstp))
|
||
|
{
|
||
|
// Wait for test to complete - ignore deadlock for now at least. The
|
||
|
// test thread is simple enough that it should not be a problem.
|
||
|
ThreadWaitForEvent(sstp.hEvent);
|
||
|
|
||
|
// Get result from thread
|
||
|
fResult = sstp.fResult;
|
||
|
}
|
||
|
|
||
|
TestSingleThreadExit:
|
||
|
|
||
|
if (sstp.hEvent != NULL)
|
||
|
{
|
||
|
CloseHandle(sstp.hEvent);
|
||
|
}
|
||
|
|
||
|
if (sstp.pcf != NULL)
|
||
|
{
|
||
|
sstp.pcf->Release();
|
||
|
}
|
||
|
|
||
|
// Let user know this didn't work
|
||
|
if (!fResult)
|
||
|
{
|
||
|
MessageBox(NULL, TEXT("Single Threaded Test Failed"),
|
||
|
TEXT("FATAL ERROR"), MB_OK);
|
||
|
}
|
||
|
|
||
|
// Return results of test
|
||
|
return fResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: TestAptThread, private
|
||
|
//
|
||
|
// Synopsis: Test an apartment model object. The most important
|
||
|
// aspect of this is that it tests the helper APIs.
|
||
|
//
|
||
|
// Returns: TRUE - Test Passed
|
||
|
// FALSE - Test Failed
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL TestAptThread(void)
|
||
|
{
|
||
|
// Return result for test
|
||
|
BOOL fResult = FALSE;
|
||
|
|
||
|
// Block for passing parameters to the test thread
|
||
|
SATParamBlock satpb;
|
||
|
|
||
|
satpb.pstrm = NULL;
|
||
|
satpb.pcf = NULL;
|
||
|
|
||
|
// Create an event for test to wait for completion of test.
|
||
|
satpb.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
|
||
|
if (satpb.hEvent == NULL)
|
||
|
{
|
||
|
goto TestAptThreadExit;
|
||
|
}
|
||
|
satpb.pcf = NULL;
|
||
|
// Create a class object and put in parameter block
|
||
|
if (CoGetClassObject(clsidAptThreadedDll, CLSCTX_INPROC, NULL,
|
||
|
IID_IClassFactory, (void **) &satpb.pcf) != NOERROR)
|
||
|
{
|
||
|
goto TestAptThreadExit;
|
||
|
}
|
||
|
|
||
|
// Create stream using helper API
|
||
|
if (CoMarshalInterThreadInterfaceInStream(IID_IClassFactory,
|
||
|
satpb.pcf, &satpb.pstrm) != NOERROR)
|
||
|
{
|
||
|
goto TestAptThreadExit;
|
||
|
}
|
||
|
|
||
|
// Create thread to do apartment model test
|
||
|
if (CreateTestThread(AptTestThread, &satpb))
|
||
|
{
|
||
|
// Wait for test to complete - ignore deadlock for now at least. The
|
||
|
// test thread is simple enough that it should not be a problem.
|
||
|
ThreadWaitForEvent(satpb.hEvent);
|
||
|
|
||
|
// Get result from thread
|
||
|
fResult = satpb.fResult;
|
||
|
}
|
||
|
|
||
|
TestAptThreadExit:
|
||
|
|
||
|
// Clean up any resources
|
||
|
if (satpb.hEvent != NULL)
|
||
|
{
|
||
|
CloseHandle(satpb.hEvent);
|
||
|
}
|
||
|
|
||
|
if (satpb.pcf != NULL)
|
||
|
{
|
||
|
satpb.pcf->Release();
|
||
|
}
|
||
|
|
||
|
if (satpb.pstrm != NULL)
|
||
|
{
|
||
|
satpb.pstrm->Release();
|
||
|
}
|
||
|
|
||
|
// Let user know this didn't work
|
||
|
if (!fResult)
|
||
|
{
|
||
|
MessageBox(NULL, TEXT("Apartment Threaded Test Failed"),
|
||
|
TEXT("FATAL ERROR"), MB_OK);
|
||
|
}
|
||
|
|
||
|
// Return results of test
|
||
|
return fResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: TestBothDll, private
|
||
|
//
|
||
|
// Synopsis: Test using DLL that purports to support both free
|
||
|
// threading and apt model. The most important aspect
|
||
|
// of this test is that it tests the marshal context.
|
||
|
//
|
||
|
// Returns: TRUE - Test Passed
|
||
|
// FALSE - Test Failed
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL TestBothDll(void)
|
||
|
{
|
||
|
// Return result for test
|
||
|
BOOL fResult = FALSE;
|
||
|
|
||
|
// Block for passing parameters to the test thread
|
||
|
SBTParamBlock sbtpb;
|
||
|
|
||
|
sbtpb.pstrm = NULL;
|
||
|
sbtpb.pcf = NULL;
|
||
|
|
||
|
IClassFactory *pcfFromMarshal = NULL;
|
||
|
IStream *pstmForMarshal = NULL;
|
||
|
HGLOBAL hglobForStream = NULL;
|
||
|
|
||
|
// Create an event for test to wait for completion of test.
|
||
|
sbtpb.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
|
||
|
if (sbtpb.hEvent == NULL)
|
||
|
{
|
||
|
goto TestBothDllExit;
|
||
|
}
|
||
|
|
||
|
// Create a class object and put in parameter block
|
||
|
if (CoGetClassObject(clsidBothThreadedDll, CLSCTX_INPROC, NULL,
|
||
|
IID_IClassFactory, (void **) &sbtpb.pcf) != NOERROR)
|
||
|
{
|
||
|
goto TestBothDllExit;
|
||
|
}
|
||
|
|
||
|
// Marshal this for the local context and unmarshal it and
|
||
|
// see if we get the same result.
|
||
|
|
||
|
if ((hglobForStream = GlobalAlloc(GMEM_MOVEABLE, 100)) == NULL)
|
||
|
{
|
||
|
GetLastError();
|
||
|
goto TestBothDllExit;
|
||
|
}
|
||
|
|
||
|
if (CreateStreamOnHGlobal(hglobForStream, TRUE, &pstmForMarshal) != NOERROR)
|
||
|
{
|
||
|
goto TestBothDllExit;
|
||
|
}
|
||
|
|
||
|
if (CoMarshalInterface(pstmForMarshal, IID_IClassFactory, sbtpb.pcf,
|
||
|
MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL) != NOERROR)
|
||
|
{
|
||
|
goto TestBothDllExit;
|
||
|
}
|
||
|
|
||
|
// Reset the stream to the begining
|
||
|
{
|
||
|
LARGE_INTEGER li;
|
||
|
LISet32(li, 0);
|
||
|
pstmForMarshal->Seek(li, STREAM_SEEK_SET, NULL);
|
||
|
}
|
||
|
|
||
|
if (CoUnmarshalInterface(pstmForMarshal, IID_IClassFactory,
|
||
|
(void **) &pcfFromMarshal) != NOERROR)
|
||
|
{
|
||
|
goto TestBothDllExit;
|
||
|
}
|
||
|
|
||
|
if (sbtpb.pcf != pcfFromMarshal)
|
||
|
{
|
||
|
goto TestBothDllExit;
|
||
|
}
|
||
|
|
||
|
// Create stream using helper API
|
||
|
if (CoMarshalInterThreadInterfaceInStream(IID_IClassFactory,
|
||
|
sbtpb.pcf, &sbtpb.pstrm) != NOERROR)
|
||
|
{
|
||
|
goto TestBothDllExit;
|
||
|
}
|
||
|
|
||
|
// Create thread to do apartment model test
|
||
|
if (CreateTestThread(BothTestThread, &sbtpb))
|
||
|
{
|
||
|
// Wait for test to complete - ignore deadlock for now at least. The
|
||
|
// test thread is simple enough that it should not be a problem.
|
||
|
WaitForSingleObject(sbtpb.hEvent, INFINITE);
|
||
|
|
||
|
// Get result from thread
|
||
|
fResult = sbtpb.fResult;
|
||
|
}
|
||
|
|
||
|
TestBothDllExit:
|
||
|
|
||
|
// Clean up any resources
|
||
|
if (sbtpb.hEvent != NULL)
|
||
|
{
|
||
|
CloseHandle(sbtpb.hEvent);
|
||
|
}
|
||
|
|
||
|
if (sbtpb.pcf != NULL)
|
||
|
{
|
||
|
sbtpb.pcf->Release();
|
||
|
}
|
||
|
|
||
|
if (sbtpb.pstrm != NULL)
|
||
|
{
|
||
|
sbtpb.pstrm->Release();
|
||
|
}
|
||
|
|
||
|
if (pcfFromMarshal != NULL)
|
||
|
{
|
||
|
pcfFromMarshal->Release();
|
||
|
}
|
||
|
|
||
|
if (pstmForMarshal != NULL)
|
||
|
{
|
||
|
pstmForMarshal->Release();
|
||
|
}
|
||
|
else if (hglobForStream != NULL)
|
||
|
{
|
||
|
GlobalFree(hglobForStream);
|
||
|
}
|
||
|
|
||
|
// Let user know this didn't work
|
||
|
if (!fResult)
|
||
|
{
|
||
|
MessageBox(NULL, TEXT("Both Threaded Test Failed"),
|
||
|
TEXT("FATAL ERROR"), MB_OK);
|
||
|
}
|
||
|
|
||
|
// Return results of test
|
||
|
return fResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: TestFreeAllLibraries, private
|
||
|
//
|
||
|
// Synopsis: Test free from non-main thread. This is really to
|
||
|
// just make sure that nothing really bad happens when
|
||
|
// we do this.
|
||
|
//
|
||
|
// Returns: TRUE - Test Passed
|
||
|
// FALSE - Test Failed
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
BOOL TestFreeAllLibraries(void)
|
||
|
{
|
||
|
CoFreeUnusedLibraries();
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: ThreadUnitTest, public
|
||
|
//
|
||
|
// Synopsis: Test various messaging enhancements to OLE
|
||
|
//
|
||
|
// Returns: TRUE - Test Passed
|
||
|
// FALSE - Test Failed
|
||
|
//
|
||
|
// History: 31-Oct-94 Ricksa Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
HRESULT ThreadUnitTest(void)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
// Make sure OLE is initialized
|
||
|
HRESULT hrInit = OleInitialize(NULL);
|
||
|
|
||
|
if (FAILED(hrInit))
|
||
|
{
|
||
|
MessageBox(NULL, TEXT("ThreadUnitTest: OleInitialize FAILED"),
|
||
|
TEXT("FATAL ERROR"), MB_OK);
|
||
|
goto ThreadUnitTestExit;
|
||
|
}
|
||
|
|
||
|
// Set up the registry
|
||
|
if (!SetUpRegistry())
|
||
|
{
|
||
|
goto ThreadUnitTestExit;
|
||
|
}
|
||
|
|
||
|
// Test Single Threaded DLL
|
||
|
if (!TestSingleThread())
|
||
|
{
|
||
|
goto ThreadUnitTestExit;
|
||
|
}
|
||
|
|
||
|
// Test an aparment model DLL
|
||
|
if (!TestAptThread())
|
||
|
{
|
||
|
goto ThreadUnitTestExit;
|
||
|
}
|
||
|
|
||
|
// Test a both DLL
|
||
|
if (!TestBothDll())
|
||
|
{
|
||
|
goto ThreadUnitTestExit;
|
||
|
}
|
||
|
|
||
|
// Test CoFreeAllLibraries
|
||
|
if (TestFreeAllLibraries())
|
||
|
{
|
||
|
hr = NOERROR;
|
||
|
}
|
||
|
|
||
|
ThreadUnitTestExit:
|
||
|
|
||
|
OleUninitialize();
|
||
|
|
||
|
return hr;
|
||
|
}
|