|
|
//+-------------------------------------------------------------------
//
// 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; }
|