windows-nt/Source/XPSP1/NT/com/oleutest/balls/client/dllhost/tdllhost.cxx
2020-09-26 16:20:57 +08:00

601 lines
20 KiB
C++

//+------------------------------------------------------------------
//
// File: tdllhost.cxx
//
// Contents: test for dll hosting
//
//--------------------------------------------------------------------
#include <tstmain.hxx>
#include "tdllhost.h"
// BUGBUG: these should be in a public place somewhere.
DEFINE_OLEGUID(CLSID_Balls, 0x0000013a, 1, 8);
DEFINE_OLEGUID(CLSID_Cubes, 0x0000013b, 1, 8);
DEFINE_OLEGUID(CLSID_LoopSrv, 0x0000013c, 1, 8);
DEFINE_OLEGUID(CLSID_QI, 0x00000140, 0, 8);
const GUID CLSID_QI =
{0x00000140,0x0000,0x0008,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
const GUID CLSID_QIHANDLER1 =
{0x00000141,0x0000,0x0008,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
const TCHAR *pszRegValThreadModel = TEXT("ThreadingModel");
const TCHAR *pszSingleModel = TEXT("Single");
const TCHAR *pszApartmentModel = TEXT("Apartment");
const TCHAR *pszMultiThreadedModel = TEXT("Free");
const TCHAR *pszBothModel = TEXT("Both");
BOOL gfApt;
// ----------------------------------------------------------------------
//
// Structures and Function Prototypes
//
// ----------------------------------------------------------------------
typedef struct tagLoadDLLParams
{
DWORD dwCallingTID; // tid of calling thread
DWORD dwCoInitFlag; // flag to initialize OLE with
DWORD dwItfFlag; // flag if the resulting object should be a proxy
BOOL RetVal; // return value
HANDLE hEvent; // thread completion event
} SLoadDLLParam;
typedef enum tagITFFLAGS
{
ITF_REAL = 1, // expect ptr to real object
ITF_PROXY = 2 // expect ptr to proxy object
} ITFFLAGS;
// worker subroutines
BOOL SpinThread(DWORD dwInitFlag, DWORD dwItfFlag);
DWORD _stdcall LoadDLLOnThread(void *param);
BOOL LoadClassObject(DWORD dwItfFlag);
BOOL SetRegForDll(REFCLSID rclsid, const TCHAR *pszThreadModel);
// test routines - return value of TRUE return means the test passed
BOOL TestLoadSingleThreaded(void);
BOOL TestLoadApartmentThreaded(void);
BOOL TestLoadMultiThreaded(void);
BOOL TestLoadBothThreaded(void);
// ----------------------------------------------------------------------
//
// TestDllHost - main test driver. read the ini file to determine
// which tests to run.
//
// ----------------------------------------------------------------------
BOOL TestDllHost(void)
{
BOOL RetVal = TRUE;
gfApt = (gInitFlag == COINIT_APARTMENTTHREADED) ? TRUE : FALSE;
// the driver did a CoInitialize, we dont want one so do CoUninit.
CoUninitialize();
if (GetProfileInt(TEXT("DllHost Test"),TEXT("LoadSingleThreaded"),1))
RetVal &= TestLoadSingleThreaded();
if (GetProfileInt(TEXT("DllHost Test"),TEXT("LoadApartmentThreaded"),1))
RetVal &= TestLoadApartmentThreaded();
if (GetProfileInt(TEXT("DllHost Test"),TEXT("LoadMultiThreaded"),1))
RetVal &= TestLoadMultiThreaded();
if (GetProfileInt(TEXT("DllHost Test"),TEXT("LoadBothThreaded"),1))
RetVal &= TestLoadBothThreaded();
// re-initialize so we dont get a complaint from OLE in debug builds
// about an unbalanced call to Uninitialize.
CoInitializeEx(NULL, gInitFlag);
return RetVal;
}
// ----------------------------------------------------------------------
//
// TestLoadSingleThreaded
//
// Tests loading a single-threaded DLL
//
// ----------------------------------------------------------------------
BOOL TestLoadSingleThreaded(void)
{
BOOL RetVal = TRUE, RetVal2 = TRUE;
HRESULT hRes = S_OK;
OUTPUT ("\n\nStarting TestLoadSingleThreaded\n");
// First, mark the DLL appropriately in the registry.
RetVal2 = SetRegForDll(CLSID_QI, pszSingleModel);
TEST_FAILED(!RetVal2, "SetRegForDLL Failed\n");
hRes = CoInitializeEx(NULL, gInitFlag);
TEST_FAILED(FAILED(hRes), "CoInitializeEx Failed\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Single-Threaded DLL on Main Thread\n");
RetVal2 = LoadClassObject((gfApt) ? ITF_REAL : ITF_PROXY);
TEST_FAILED(!RetVal2, "SingleThreadedDLL on Main Thread Failed\n");
OUTPUT (" Done Load Single-Threaded DLL on Main Thread\n");
OUTPUT ("\n Load Single-Threaded DLL on Main Thread\n");
RetVal2 = LoadClassObject((gfApt) ? ITF_REAL : ITF_PROXY);
TEST_FAILED(!RetVal2, "SingleThreadedDLL on Main Thread Failed\n");
OUTPUT (" Done Load Single-Threaded DLL on Main Thread\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Single-Threaded DLL on Different Apartment Thread\n");
hRes = SpinThread(COINIT_APARTMENTTHREADED, ITF_PROXY);
TEST_FAILED(!RetVal2, "SingleThreadedDLL on Apartment Thread Failed\n");
OUTPUT (" Done Load Single-Threaded DLL on Different Apartment Thread\n");
OUTPUT ("\n Second Load Single-Threaded DLL on Different Apartment Thread\n");
hRes = SpinThread(COINIT_APARTMENTTHREADED, ITF_PROXY);
TEST_FAILED(!RetVal2, "Single-ThreadedDLL on Apartment Thread Failed\n");
OUTPUT (" Second Done Load Single-Threaded DLL on Different Apartment Thread\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Single-Thread DLL on Multi-Threaded Apartment Thread\n");
hRes = SpinThread(COINIT_MULTITHREADED, ITF_PROXY);
TEST_FAILED(!RetVal2, "SingleThreadedDLL on Multi Thread Failed\n");
OUTPUT (" Done Load Single-Thread DLL on Multi-Threaded Apartment Thread\n");
OUTPUT ("\n Load Single-Thread DLL on Multi-Threaded Apartment Thread\n");
hRes = SpinThread(COINIT_MULTITHREADED, ITF_PROXY);
TEST_FAILED(!RetVal2, "SingleThreadedDLL on Multi Thread Failed\n");
OUTPUT (" Done Load Single-Thread DLL on Multi-Threaded Apartment Thread\n");
// ----------------------------------------------------------------------
OUTPUT (" - Test Complete. Doing Cleanup\n");
CoUninitialize();
BOOL fResult = TestResult(RetVal, "TestLoadSingleThreaded");
Sleep(2000);
return fResult;
}
// ----------------------------------------------------------------------
//
// TestLoadApartmentThreaded
//
// Tests loading an apartment-threaded DLL
//
// ----------------------------------------------------------------------
BOOL TestLoadApartmentThreaded(void)
{
BOOL RetVal = TRUE, RetVal2 = FALSE;
HRESULT hRes = S_OK;
OUTPUT ("\n\nStarting TestLoadApartmentThreaded\n");
// First, mark the DLL appropriately in the registry.
RetVal2 = SetRegForDll(CLSID_QI, pszApartmentModel);
TEST_FAILED(!RetVal2, "SetRegForDLL Failed\n");
hRes = CoInitializeEx(NULL, gInitFlag);
TEST_FAILED(FAILED(hRes), "CoInitializeEx Failed\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Apartment-Threaded DLL on Main Thread\n");
RetVal2 = LoadClassObject((gfApt) ? ITF_REAL : ITF_PROXY);
TEST_FAILED(!RetVal2, "Apartment-ThreadedDLL on Main Thread Failed\n");
OUTPUT (" Done Load Apartment-Threaded DLL on Main Thread\n");
OUTPUT ("\n Load Apartment-Threaded DLL on Main Thread\n");
RetVal2 = LoadClassObject((gfApt) ? ITF_REAL : ITF_PROXY);
TEST_FAILED(!RetVal2, "Apartment-ThreadedDLL on Main Thread Failed\n");
OUTPUT (" Done Load Apartment-Threaded DLL on Main Thread\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Apartment-Threaded DLL on Different Apartment Thread\n");
hRes = SpinThread(COINIT_APARTMENTTHREADED, ITF_REAL);
TEST_FAILED(!RetVal2, "Apartment-ThreadedDLL on Apartment Thread Failed\n");
OUTPUT (" Done Load Apartment-Threaded DLL on Different Apartment Thread\n");
OUTPUT ("\n Second Load Apartment-Threaded DLL on Different Apartment Thread\n");
hRes = SpinThread(COINIT_APARTMENTTHREADED, ITF_REAL);
TEST_FAILED(!RetVal2, "Apartment-ThreadedDLL on Apartment Thread Failed\n");
OUTPUT (" Second Done Load Apartment-Threaded DLL on Different Apartment Thread\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Apartment-Thread DLL on Multi-Threaded Apartment Thread\n");
hRes = SpinThread(COINIT_MULTITHREADED, ITF_PROXY);
TEST_FAILED(!RetVal2, "Apartment-ThreadedDLL on Multi Thread Failed\n");
OUTPUT (" Done Load Apartment-Thread DLL on Multi-Threaded Apartment Thread\n");
OUTPUT ("\n Load Apartment-Thread DLL on Multi-Threaded Apartment Thread\n");
hRes = SpinThread(COINIT_MULTITHREADED, ITF_PROXY);
TEST_FAILED(!RetVal2, "Apartment-ThreadedDLL on Multi Thread Failed\n");
OUTPUT (" Done Load Apartment-Thread DLL on Multi-Threaded Apartment Thread\n");
// ----------------------------------------------------------------------
OUTPUT (" - Test Complete. Doing Cleanup\n");
CoUninitialize();
BOOL fResult = TestResult(RetVal, "TestLoadApartmentThreaded");
Sleep(2000);
return fResult;
}
// ----------------------------------------------------------------------
//
// TestLoadMultiThreaded
//
// Tests loading a multi-threaded DLL
//
// ----------------------------------------------------------------------
BOOL TestLoadMultiThreaded(void)
{
BOOL RetVal = TRUE, RetVal2 = FALSE;;
HRESULT hRes = S_OK;
OUTPUT ("\n\nStarting TestLoadMultiThreaded\n");
// First, mark the DLL appropriately in the registry.
RetVal2 = SetRegForDll(CLSID_QI, pszMultiThreadedModel);
TEST_FAILED(!RetVal2, "SetRegForDLL Failed\n");
hRes = CoInitializeEx(NULL, gInitFlag);
TEST_FAILED(FAILED(hRes), "CoInitializeEx Failed\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Free-Threaded DLL on Main Thread\n");
RetVal2 = LoadClassObject((gfApt) ? ITF_PROXY : ITF_REAL);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Main Thread Failed\n");
OUTPUT (" Done Load Free-Threaded DLL on Main Thread\n");
OUTPUT ("\n Load Free-Threaded DLL on Main Thread\n");
RetVal2 = LoadClassObject((gfApt) ? ITF_PROXY : ITF_REAL);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Main Thread Failed\n");
OUTPUT (" Done Load Free-Threaded DLL on Main Thread\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Free-Threaded DLL on Different Apartment Thread\n");
hRes = SpinThread(COINIT_APARTMENTTHREADED, ITF_PROXY);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Apartment Thread Failed\n");
OUTPUT (" Done Load Free-Threaded DLL on Different Apartment Thread\n");
OUTPUT ("\n Second Load Free-Threaded DLL on Different Apartment Thread\n");
hRes = SpinThread(COINIT_APARTMENTTHREADED, ITF_PROXY);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Apartment Thread Failed\n");
OUTPUT (" Second Done Load Apartment-Threaded DLL on Different Apartment Thread\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Apartment-Thread DLL on Multi-Threaded Apartment Thread\n");
hRes = SpinThread(COINIT_MULTITHREADED, ITF_REAL);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Multi Thread Failed\n");
OUTPUT (" Done Load Free-Thread DLL on Multi-Threaded Apartment Thread\n");
OUTPUT ("\n Load Apartment-Thread DLL on Multi-Threaded Apartment Thread\n");
hRes = SpinThread(COINIT_MULTITHREADED, ITF_REAL);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Multi Thread Failed\n");
OUTPUT (" Done Load Free-Thread DLL on Multi-Threaded Apartment Thread\n");
// ----------------------------------------------------------------------
OUTPUT (" - Test Complete. Doing Cleanup\n");
CoUninitialize();
BOOL fResult = TestResult(RetVal, "TestLoadMultiThreaded");
Sleep(2000);
return fResult;
}
// ----------------------------------------------------------------------
//
// TestLoadBothThreaded
//
// Tests loading a both-threaded DLL
//
// ----------------------------------------------------------------------
BOOL TestLoadBothThreaded(void)
{
BOOL RetVal = TRUE, RetVal2 = FALSE;;
HRESULT hRes = S_OK;
OUTPUT ("\n\nStarting TestLoadBothThreaded\n");
// First, mark the DLL appropriately in the registry.
RetVal2 = SetRegForDll(CLSID_QI, pszBothModel);
TEST_FAILED(!RetVal2, "SetRegForDLL Failed\n");
hRes = CoInitializeEx(NULL, gInitFlag);
TEST_FAILED(FAILED(hRes), "CoInitializeEx Failed\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Both-Threaded DLL on Main Thread\n");
RetVal2 = LoadClassObject(ITF_REAL);
TEST_FAILED(!RetVal2, "Both-ThreadedDLL on Main Thread Failed\n");
OUTPUT (" Done Load Both-Threaded DLL on Main Thread\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Both-Threaded DLL on Different Apartment Thread\n");
hRes = SpinThread(COINIT_APARTMENTTHREADED, ITF_REAL);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Apartment Thread Failed\n");
OUTPUT (" Done Load Free-Threaded DLL on Different Apartment Thread\n");
OUTPUT ("\n Second Load Free-Threaded DLL on Different Apartment Thread\n");
hRes = SpinThread(COINIT_APARTMENTTHREADED, ITF_REAL);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Apartment Thread Failed\n");
OUTPUT (" Second Done Load Free-Threaded DLL on Different Apartment Thread\n");
// ----------------------------------------------------------------------
OUTPUT ("\n Load Apartment-Thread DLL on Multi-Threaded Apartment Thread\n");
hRes = SpinThread(COINIT_MULTITHREADED, ITF_REAL);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Multi Thread Failed\n");
OUTPUT (" Done Load Free-Thread DLL on Multi-Threaded Apartment Thread\n");
OUTPUT ("\n Load Apartment-Thread DLL on Multi-Threaded Apartment Thread\n");
hRes = SpinThread(COINIT_MULTITHREADED, ITF_REAL);
TEST_FAILED(!RetVal2, "Free-ThreadedDLL on Multi Thread Failed\n");
OUTPUT (" Done Load Free-Thread DLL on Multi-Threaded Apartment Thread\n");
// ----------------------------------------------------------------------
OUTPUT (" - Test Complete. Doing Cleanup\n");
CoUninitialize();
BOOL fResult = TestResult(RetVal, "TestLoadBothThreaded");
Sleep(2000);
return fResult;
}
// ----------------------------------------------------------------------
//
// Function: SpinThread
//
// Synopsis: Creates a thread to do some work for us. Waits for it to
// complete. Returns the results.
//
// ----------------------------------------------------------------------
BOOL SpinThread(DWORD dwInitFlag, DWORD dwItfFlag)
{
BOOL RetVal = FALSE;
// set up paramters to pass to the thread
SLoadDLLParam LoadParam;
LoadParam.dwCallingTID = GetCurrentThreadId();
LoadParam.dwCoInitFlag = dwInitFlag;
LoadParam.dwItfFlag = dwItfFlag;
LoadParam.RetVal = FALSE;
LoadParam.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// create the thread
DWORD dwThrdId = 0;
HANDLE hThrd = CreateThread(NULL, 0,
LoadDLLOnThread,
&LoadParam, 0, &dwThrdId);
if (hThrd)
{
// enter a message loop and wait for the other thread to run
// We stay here until the thread posts a QUIT message.
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
DispatchMessage(&msg);
}
// close the thread handle
CloseHandle(hThrd);
}
else
{
HRESULT hRes = GetLastError();
TEST_FAILED(hRes, "CreateThread failed\n")
LoadParam.RetVal = RetVal;
}
// wait for the other thread to complete
WaitForSingleObject(LoadParam.hEvent, 0xffffffff);
CloseHandle(LoadParam.hEvent);
return LoadParam.RetVal;
}
// ----------------------------------------------------------------------
//
// Function: LoadDLLOnThread
//
// Synopsis: Initializes COM, loads the class object and creates an
// instance, releases them, Posts a message to wake up the
// calling thread, Uninitializes COM, then exits.
//
// ----------------------------------------------------------------------
DWORD _stdcall LoadDLLOnThread(void *param)
{
BOOL RetVal = TRUE;
HRESULT hRes = S_OK;
SLoadDLLParam *pLoadParam = (SLoadDLLParam *)param;
OUTPUT (" - LoadDLLOnThread Entered\n");
hRes = CoInitializeEx(NULL, pLoadParam->dwCoInitFlag);
TEST_FAILED(FAILED(hRes), "CoInitialize failed\n")
if (SUCCEEDED(hRes))
{
// attempt to load the class object on this thread.
pLoadParam->RetVal = LoadClassObject(pLoadParam->dwItfFlag);
CoUninitialize();
}
// post a message to the server thread to exit now that we are done.
PostThreadMessage(pLoadParam->dwCallingTID, WM_QUIT, 0, 0);
SetEvent(pLoadParam->hEvent);
OUTPUT (" - LoadDLLOnThread Exit\n");
return RetVal;
}
// ----------------------------------------------------------------------
//
// Function: LoadClassObject
//
// Synopsis: Loads the class object, creates an instance, releases
// them, returns the results.
//
// ----------------------------------------------------------------------
BOOL LoadClassObject(DWORD dwItfFlag)
{
BOOL RetVal = TRUE;
IClassFactory *pICF = NULL;
IUnknown *pIPM = NULL;
// try to load the dll class object
HRESULT hRes = CoGetClassObject(CLSID_QI, CLSCTX_INPROC_SERVER, NULL,
IID_IClassFactory, (void **)&pICF);
TEST_FAILED(FAILED(hRes), "CoGetClassObject failed\n");
if (SUCCEEDED(hRes))
{
hRes = pICF->QueryInterface(IID_IProxyManager, (void **)&pIPM);
if (SUCCEEDED(hRes))
{
pIPM->Release();
TEST_FAILED(dwItfFlag != ITF_PROXY, "Got Proxy when expected Real\n");
}
else
{
TEST_FAILED(dwItfFlag != ITF_REAL, "Got Real when expected Proxy\n");
}
// CODEWORK: create an instance, then release them
// release the class object.
OUTPUT (" - CoGetClassObject succeeded\n");
ULONG ulRefCnt = pICF->Release();
TEST_FAILED(ulRefCnt != 0, "pICF RefCnt not zero\n");
pICF = NULL;
OUTPUT (" - Released ClassObject\n");
}
return RetVal;
}
//+-------------------------------------------------------------------
//
// Function: SetRegForDll, private
//
// Synopsis: Set registry entry for a DLL
//
// Arguments: [rclsid] - clsid for reg entry
// [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 *pszThreadModel)
{
BOOL fResult = FALSE;
HKEY hKeyClass = NULL;
HKEY hKeyDll = NULL;
TCHAR aszWkBuf[MAX_PATH]; // String buffer used for various purposes
// 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 (RegCreateKey(HKEY_CLASSES_ROOT, aszWkBuf, &hKeyClass) == ERROR_SUCCESS)
{
// Create the key for the DLL
if (RegCreateKey(hKeyClass, TEXT("InprocServer32"), &hKeyDll) == ERROR_SUCCESS)
{
// Set the value for the Threading Model
if (RegSetValueEx(hKeyDll, pszRegValThreadModel, 0,
REG_SZ,
(const unsigned char*) pszThreadModel,
(wcslen(pszThreadModel) + 1) * sizeof(WCHAR))
== ERROR_SUCCESS)
{
fResult = TRUE;
}
RegCloseKey(hKeyDll);
}
RegCloseKey(hKeyClass);
}
return fResult;
}