1522 lines
32 KiB
C++
1522 lines
32 KiB
C++
// This is a multi-threaded app with two primary threads. One
|
|
// sits in the message loop, waiting specifically for WM_PAINT
|
|
// messages which are generated by the other thread, on which
|
|
// the actual unit test runs.
|
|
//
|
|
// When the window thread receives an update message, it takes
|
|
// a snapshot of the unit test state (protected by a mutex),
|
|
// and redraws the screen accordingly.
|
|
//
|
|
// When the unit test thread wants a resource to be drawn in
|
|
// the main window, it places the handle to that resource (for
|
|
// example, an HMETAFILEPICT) in the ctest object associated
|
|
// with the window thread, then fires a screen update. In
|
|
// doing so, ownership of the resource is transfered from the
|
|
// unit test thread to the window thread. By using this
|
|
// mechanism, the window thread can draw the resource at its
|
|
// leisure, while the unit test proceeds. The onus is on
|
|
// the window thread to clean up any resources which have
|
|
// been deposited in its care when it exists.
|
|
//
|
|
// If the window thread receives a WM_CLOSE message, it must
|
|
// first check to see that the unit test thread has completed.
|
|
// If not, it spins in a RETRY/CANCEL loop until the unit test
|
|
// thread has completed, or until the user selects CANCEL, at
|
|
// which point execution proceeds, ignoring the WM_CLOSE.
|
|
//
|
|
// "OVER-ENGINEERED, AND BUILT TO STAY THAT WAY" (tm)
|
|
//
|
|
|
|
|
|
#include "headers.hxx"
|
|
#pragma hdrstop
|
|
|
|
CCacheTestApp ctest; // Application instance
|
|
TestInstance inst; // Test instance
|
|
|
|
//
|
|
// Prototype for the entry-point of the unit test thread
|
|
//
|
|
|
|
unsigned long __stdcall testmain(void *);
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: WinMain
|
|
//
|
|
// Synopsis: windows entry point
|
|
//
|
|
// Arguments: [hInst] --
|
|
// [hPrevInst] --
|
|
// [lpszCmdLine] --
|
|
// [nCmdShow] --
|
|
//
|
|
// Returns: int
|
|
//
|
|
// History: 05-Sep-94 davepl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)
|
|
{
|
|
MSG message;
|
|
|
|
//
|
|
// Initialize the application
|
|
//
|
|
|
|
if (SUCCEEDED(ctest.Initialize(hInst, hPrevInst, lpszCmdLine)))
|
|
{
|
|
//
|
|
// Show and update the window
|
|
//
|
|
|
|
ShowWindow(ctest.Window(), nCmdShow);
|
|
UpdateWindow(ctest.Window());
|
|
|
|
//
|
|
// The main message loop
|
|
//
|
|
|
|
while (GetMessage(&message, NULL, NULL, NULL))
|
|
{
|
|
TranslateMessage(&message);
|
|
DispatchMessage(&message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
return(message.wParam);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CCacheTestApp::CCacheTestApp
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// History: 05-Sep-94 Davepl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CCacheTestApp::CCacheTestApp ()
|
|
{
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CCacheTestApp::Initialize
|
|
//
|
|
// Synopsis: initializes the application
|
|
//
|
|
// Arguments: [hInst] -- current instance
|
|
// [hPrevInst] -- previous instance
|
|
// [lpszCmdLine] -- command line parameters
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// History: 05-Sep-94 Davepl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CCacheTestApp::Initialize (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Register the window class
|
|
//
|
|
|
|
if (hPrevInst == NULL)
|
|
{
|
|
WNDCLASS wndclass;
|
|
|
|
wndclass.style = 0;
|
|
wndclass.lpfnWndProc = CacheTestAppWndProc;
|
|
|
|
wndclass.cbClsExtra = 0;
|
|
wndclass.cbWndExtra = 0;
|
|
|
|
wndclass.hInstance = hInst;
|
|
wndclass.hIcon = LoadIcon(hInst, IDI_EXCLAMATION);
|
|
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wndclass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
|
|
wndclass.lpszMenuName = NULL;
|
|
wndclass.lpszClassName = CTESTAPPCLASS;
|
|
|
|
if (RegisterClass(&wndclass) == 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the mutex
|
|
//
|
|
|
|
m_hMutex = CreateMutex(NULL, FALSE, NULL);
|
|
if (NULL == m_hMutex)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
//
|
|
// Create the window
|
|
//
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if ((m_hWnd = CreateWindowEx(0L,
|
|
CTESTAPPCLASS,
|
|
CTESTAPPTITLE,
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT,
|
|
0,
|
|
CW_USEDEFAULT,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
hInst,
|
|
NULL)) == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CCacheTestApp::~CCacheTestApp
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// History: 05-Sep-94 Davepl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CCacheTestApp::~CCacheTestApp ()
|
|
{
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CacheTestAppWndProc
|
|
//
|
|
// Synopsis: window procedure
|
|
//
|
|
// Arguments: [hWnd] -- window
|
|
// [message] -- message id
|
|
// [wParam] -- parameter
|
|
// [lParam] -- parameter
|
|
//
|
|
// Returns: LRESULT
|
|
//
|
|
// History: 05-Sep-94 Davepl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
LRESULT FAR PASCAL CacheTestAppWndProc (HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
//
|
|
// Process the messages
|
|
//
|
|
|
|
switch (message)
|
|
{
|
|
case WM_CREATE:
|
|
|
|
//
|
|
// The unit test window is opening. Create another thread
|
|
// on which the unit test itself runs, while this thread
|
|
// continues to spin, waiting for redraws, close, and so
|
|
// on...
|
|
//
|
|
|
|
ctest.m_hTest = CreateThread(NULL,
|
|
0,
|
|
testmain,
|
|
NULL,
|
|
0,
|
|
&ctest.m_dwThreadID);
|
|
|
|
if (NULL == ctest.m_hTest)
|
|
{
|
|
mprintf("Unable to begin unit test\n");
|
|
PostQuitMessage(0);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hDC;
|
|
|
|
//
|
|
// Get the DC for painting
|
|
//
|
|
|
|
hDC = BeginPaint(hWnd, &ps);
|
|
if (hDC)
|
|
{
|
|
//
|
|
// Draw the metafile
|
|
//
|
|
|
|
inst.Draw(hDC);
|
|
|
|
EndPaint(hWnd, &ps);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
|
|
//
|
|
// Invalidate the rectangle
|
|
//
|
|
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
|
|
{
|
|
//
|
|
// The user has tried to exit the main program. Before we
|
|
// can shut down, we must wait until the child thread has
|
|
// completed. We allow the user to keep retrying on the
|
|
// thread, or to "cancel" and wait until later. We do not
|
|
// provide the option of terminating the child thread while
|
|
// it is still busy.
|
|
//
|
|
|
|
DWORD dwStatus = 0;
|
|
|
|
if (FALSE == GetExitCodeThread(ctest.m_hTest, &dwStatus))
|
|
{
|
|
mprintf("Could not get thread information!");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
INT response = IDRETRY;
|
|
|
|
while (STILL_ACTIVE == dwStatus)
|
|
{
|
|
response = MessageBox(ctest.Window(),
|
|
"The child thread has not yet completed.",
|
|
"Cannot Shutdown",
|
|
MB_RETRYCANCEL);
|
|
|
|
if (IDCANCEL == response)
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Destroy the window if the child has gone away
|
|
//
|
|
|
|
if (STILL_ACTIVE != dwStatus)
|
|
{
|
|
DestroyWindow(hWnd);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 23-Aug-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
unsigned long __stdcall testmain(void *)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = inst.CreateAndInit( OLESTR("mystg") );
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
mprintf("Cache Unit Test Failed [CreateAndInit] hr = %x\n", hr);
|
|
goto exit;
|
|
}
|
|
|
|
hr = inst.EnumeratorTest();
|
|
if (S_OK != hr)
|
|
{
|
|
mprintf("Cache Unit Test Failed [EnumeratorTest] hr = %x\n", hr);
|
|
goto exit;
|
|
}
|
|
|
|
hr = inst.MultiCache(50);
|
|
if (S_OK != hr)
|
|
{
|
|
mprintf("Cache Unit Test Failed [MultiCache] hr = %x\n", hr);
|
|
goto exit;
|
|
}
|
|
|
|
hr = inst.CacheDataTest("TIGER.BMP", "TIGERNPH.WMF");
|
|
if (S_OK != hr)
|
|
{
|
|
mprintf("Cache Unit Test Failed [CacheDataTest] hr = %x\n", hr);
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
|
|
PostMessage(ctest.Window(), WM_CLOSE, (WPARAM) hr, 0);
|
|
return (ULONG) hr;
|
|
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: TestInstance::TestInstance
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 23-Aug-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TestInstance::TestInstance()
|
|
{
|
|
|
|
m_pStorage = NULL;
|
|
m_pPersistStorage = NULL;
|
|
m_pOleCache = NULL;
|
|
m_pOleCache2 = NULL;
|
|
m_pDataObject = NULL;
|
|
m_pViewObject = NULL;
|
|
m_State = TEST_STARTING;
|
|
}
|
|
|
|
TestInstance::~TestInstance()
|
|
{
|
|
//
|
|
// Release our interface pointers
|
|
//
|
|
|
|
if (m_pDataObject)
|
|
{
|
|
m_pDataObject->Release();
|
|
}
|
|
|
|
if (m_pViewObject)
|
|
{
|
|
m_pViewObject->Release();
|
|
}
|
|
|
|
if (m_pPersistStorage)
|
|
{
|
|
m_pPersistStorage->Release();
|
|
}
|
|
|
|
if (m_pOleCache2)
|
|
{
|
|
m_pOleCache2->Release();
|
|
}
|
|
|
|
if (m_pOleCache)
|
|
{
|
|
m_pOleCache->Release();
|
|
}
|
|
|
|
if (m_pStorage)
|
|
{
|
|
m_pStorage->Release();
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: TestInstance::CreateAndInit
|
|
//
|
|
// Synopsis: Creates a cache and sets up internal interface ptrs
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 23-Aug-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT TestInstance::CreateAndInit(LPOLESTR lpwszStgName)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceLog Log(this, "TestInstance::CreateAndInit", GS_CACHE, VB_MINIMAL);
|
|
Log.OnEntry (" ( %p ) \n", lpwszStgName);
|
|
Log.OnExit (" ( %X ) \n", &hr);
|
|
|
|
//
|
|
// Create the storage on which we will instantiate our cache
|
|
//
|
|
|
|
// BUGBUG use correct strcpy fn
|
|
|
|
wcscpy(m_wszStorage, lpwszStgName);
|
|
|
|
hr = StgCreateDocfile(lpwszStgName,
|
|
STGM_DIRECT |
|
|
STGM_READWRITE |
|
|
STGM_SHARE_EXCLUSIVE |
|
|
STGM_CREATE,
|
|
0,
|
|
&m_pStorage);
|
|
|
|
//
|
|
// Create a Data Cache on our IStorage
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CreateDataCache(NULL,
|
|
CLSID_NULL,
|
|
IID_IPersistStorage,
|
|
(void **)&m_pPersistStorage);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->InitNew(m_pStorage);
|
|
}
|
|
|
|
//
|
|
// Get an IOleCache interface pointer to the cache
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->QueryInterface(IID_IOleCache,
|
|
(void **) &m_pOleCache);
|
|
}
|
|
|
|
//
|
|
// Get an IOleCache2 interface pointer to the cache
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->QueryInterface(IID_IOleCache2,
|
|
(void **) &m_pOleCache2);
|
|
}
|
|
|
|
//
|
|
// Get an IViewObject interface pointer to the cache
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->QueryInterface(IID_IViewObject,
|
|
(void **) &m_pViewObject);
|
|
}
|
|
|
|
//
|
|
// Get an IDataObject interface pointer to the cache
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->QueryInterface(IID_IDataObject,
|
|
(void **) &m_pDataObject);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: TestInstance::SaveAndReload
|
|
//
|
|
// Synopsis: Saves the cache to its storage and reloads it
|
|
// right back.
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes: Once saved, the behavior of DiscardCache will
|
|
// change, since each node present at the time of
|
|
// save will have a stream from which it can demand
|
|
// load its data.
|
|
//
|
|
// Since each interface pointer is released and
|
|
// reaquired, the pointer values will (likely) change
|
|
// during this call; hence, so _not_ cache the pointers
|
|
// locally around this call.
|
|
//
|
|
// History: 23-Aug-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT TestInstance::SaveAndReload()
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceLog Log(NULL, "TestInstance::SaveAndReload", GS_CACHE, VB_MINIMAL);
|
|
Log.OnEntry ();
|
|
Log.OnExit (" ( %X )\n", &hr);
|
|
|
|
SetCurrentState(SAVE_AND_RELOAD);
|
|
|
|
hr = m_pPersistStorage->Save(m_pStorage, TRUE);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->SaveCompleted(NULL);
|
|
}
|
|
|
|
// Release our hold on the storage and the cache
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
m_pViewObject->Release();
|
|
m_pViewObject = NULL;
|
|
m_pDataObject->Release();
|
|
m_pDataObject = NULL;
|
|
|
|
m_pStorage->Release();
|
|
m_pStorage = NULL;
|
|
|
|
m_pPersistStorage->Release();
|
|
m_pPersistStorage = NULL;
|
|
|
|
m_pOleCache2->Release();
|
|
m_pOleCache2 = NULL;
|
|
|
|
m_pOleCache->Release();
|
|
m_pOleCache = NULL;
|
|
|
|
|
|
//
|
|
// Reload the cache and QI to get our interfaces back
|
|
//
|
|
|
|
hr = StgOpenStorage(m_wszStorage,
|
|
NULL,
|
|
STGM_DIRECT |
|
|
STGM_READWRITE |
|
|
STGM_SHARE_EXCLUSIVE,
|
|
NULL,
|
|
0,
|
|
&m_pStorage);
|
|
|
|
//
|
|
// Create a Data Cache on our IStorage
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CreateDataCache(NULL,
|
|
CLSID_NULL,
|
|
IID_IPersistStorage,
|
|
(void **)&m_pPersistStorage);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->Load(m_pStorage);
|
|
}
|
|
|
|
//
|
|
// Get an IOleCache interface pointer to the cache
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->QueryInterface(IID_IOleCache,
|
|
(void **) &m_pOleCache);
|
|
}
|
|
|
|
//
|
|
// Get an IOleCache2 interface pointer to the cache
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->QueryInterface(IID_IOleCache2,
|
|
(void **) &m_pOleCache2);
|
|
}
|
|
|
|
//
|
|
// Get an IViewObject interface pointer to the cache
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->QueryInterface(IID_IViewObject,
|
|
(void **) &m_pViewObject);
|
|
}
|
|
|
|
//
|
|
// Get an IDataObject interface pointer to the cache
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pPersistStorage->QueryInterface(IID_IDataObject,
|
|
(void **) &m_pDataObject);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: TestInstance::CacheDataTest
|
|
//
|
|
// Synopsis: Checks the cache for data integrity
|
|
//
|
|
// Arguments: lpszBMPFileName - Name of .BMP file to use for test
|
|
// lpszWMFFileName - Name of .WMF file to use for test
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 04-Sep-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT TestInstance::CacheDataTest(char * lpszBMPFileName, char * lpszWMFFileName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TraceLog Log(NULL, "TestInstance::CacheDataTest", GS_CACHE, VB_MINIMAL);
|
|
Log.OnEntry (" ( BMP=%s, WMF=%s )\n", lpszBMPFileName, lpszWMFFileName);
|
|
Log.OnExit (" ( %X )\n", &hr);
|
|
|
|
SetCurrentState(DATA_TEST);
|
|
|
|
CBitmapFile bmpFile;
|
|
HGLOBAL hDIB = NULL;
|
|
|
|
//
|
|
// Allocate an hglobal to hold our metafilepict structure
|
|
//
|
|
|
|
HGLOBAL hMFPICT = GlobalAlloc(GMEM_FIXED, sizeof(METAFILEPICT));
|
|
if (NULL == hMFPICT)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
METAFILEPICT * pMFPICT = (METAFILEPICT *) hMFPICT;
|
|
|
|
//
|
|
// Load the bitmap from disk
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = bmpFile.LoadBitmapFile(lpszBMPFileName);
|
|
}
|
|
|
|
//
|
|
// Create a DIB on an HGLOBAL from the bitmap
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = bmpFile.CreateDIBInHGlobal(&hDIB);
|
|
}
|
|
|
|
//
|
|
// Add DIB and MF nodes to the cache
|
|
//
|
|
|
|
DWORD dwDIBCon;
|
|
DWORD dwMFCon;
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = AddDIBCacheNode(&dwDIBCon);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = AddMFCacheNode(&dwMFCon);
|
|
}
|
|
|
|
//
|
|
// Load the metafile from disk, then set up the
|
|
// METAFILEPICT structure
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
pMFPICT->hMF = GetMetaFileA(lpszWMFFileName);
|
|
if (NULL == pMFPICT->hMF)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We deem the metafile to have the same extents
|
|
// as the the DIB. This is completely arbitrary,
|
|
// but might aid in tracking down extents problems.
|
|
// After all, we have to pick _some_ value, so it
|
|
// might as well be a useful one...
|
|
//
|
|
|
|
pMFPICT->xExt = ConvWidthInPelsToLHM(NULL, bmpFile.GetDIBHeight());
|
|
pMFPICT->yExt = ConvHeightInPelsToLHM(NULL, bmpFile.GetDIBWidth());
|
|
pMFPICT->mm = MM_ANISOTROPIC;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Place the nodes in the cache. We keep ownership of the handles,
|
|
// which will force the cache to duplicate it. We can then compare
|
|
// our original with whatever we later get back from the cache.
|
|
//
|
|
|
|
FORMATETC fetcDIB =
|
|
{
|
|
CF_DIB,
|
|
NULL,
|
|
DVASPECT_CONTENT,
|
|
DEF_LINDEX,
|
|
TYMED_HGLOBAL
|
|
};
|
|
|
|
STGMEDIUM stgmDIB;
|
|
|
|
FORMATETC fetcMF =
|
|
{
|
|
CF_METAFILEPICT,
|
|
NULL,
|
|
DVASPECT_CONTENT,
|
|
DEF_LINDEX,
|
|
TYMED_MFPICT
|
|
};
|
|
|
|
STGMEDIUM stgmMF;
|
|
|
|
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
stgmDIB.tymed = TYMED_HGLOBAL;
|
|
stgmDIB.hGlobal = hDIB;
|
|
|
|
hr = m_pOleCache->SetData(&fetcDIB, &stgmDIB, FALSE);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
stgmMF.tymed = TYMED_MFPICT,
|
|
stgmMF.hMetaFilePict = hMFPICT;
|
|
|
|
hr = m_pOleCache->SetData(&fetcMF, &stgmMF, FALSE);
|
|
}
|
|
|
|
//
|
|
// If we were able to place the data in the cache, check
|
|
// to make sure whatever is in the cache matches our
|
|
// original.
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CompareDIB(hDIB);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CompareMF(hMFPICT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save and Reload the cache to test persistance
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = SaveAndReload();
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
SetCurrentState(DATA_TEST);
|
|
}
|
|
|
|
//
|
|
// Compare the data again
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CompareDIB(hDIB);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CompareMF(hMFPICT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Discard the cache.
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = m_pOleCache2->DiscardCache(DISCARDCACHE_NOSAVE);
|
|
}
|
|
|
|
//
|
|
// Now compare again against the current presentations,
|
|
// which would have to be demand-loaded after the discard.
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CompareDIB(hDIB);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CompareMF(hMFPICT);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Try to draw the cache's best presentation (which should
|
|
// be metafile at this point) into a metafile DC which we
|
|
// will then hand off to the window thread for drawing.
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = DrawCacheToMetaFilePict(&ctest.m_hMFP, FALSE);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
SetCurrentState(DRAW_METAFILE_NOW);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now draw the metafile tiled 4 times into the display
|
|
// metafile, and hand it off...
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = DrawCacheToMetaFilePict(&ctest.m_hMFPTILED, TRUE);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
SetCurrentState(DRAW_METAFILETILED_NOW);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Uncache the metafile node, which will leave the DIB node
|
|
// as the best (and only) node left for drawing
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = UncacheFormat(CF_METAFILEPICT);
|
|
}
|
|
|
|
//
|
|
// Now draw the DIB into a metafile and hand that off
|
|
// to the window thread for drawing
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = DrawCacheToMetaFilePict(&ctest.m_hMFPDIB, FALSE);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
SetCurrentState(DRAW_DIB_NOW);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now draw the DIB again, this time tiled into the mf
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = DrawCacheToMetaFilePict(&ctest.m_hMFPDIBTILED, TRUE);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
SetCurrentState(DRAW_DIBTILED_NOW);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup our local DIB
|
|
//
|
|
|
|
if (hDIB)
|
|
{
|
|
GlobalFree(hDIB);
|
|
}
|
|
|
|
//
|
|
// Cleaup our local metafile
|
|
//
|
|
|
|
if (pMFPICT)
|
|
{
|
|
if (pMFPICT->hMF)
|
|
{
|
|
if (FALSE == DeleteMetaFile(pMFPICT->hMF))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
GlobalFree(hMFPICT);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT TestInstance::CompareDIB(HGLOBAL hDIB)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT TestInstance::CompareMF(HMETAFILEPICT hMFPICT)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: TestInstance::DrawCacheToMetaFilePict
|
|
//
|
|
// Synopsis: Draws the cache's current best presentation to
|
|
// a metafile, contained in a metafilepict structure,
|
|
// which is allocated off of the hGlobal pointer passed
|
|
// in by the caller
|
|
//
|
|
// Arguments: [phGlobal] - The ptr to the hglobal to allocate on
|
|
// [fTile] - If true, the pres is tiled into the mf
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 06-Sep-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT TestInstance::DrawCacheToMetaFilePict(HGLOBAL *phGlobal, BOOL fTile)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TraceLog Log(NULL, "TestInstance::DrawCacheToMetaFilePict", GS_CACHE, VB_MINIMAL);
|
|
Log.OnEntry (" ( %p, %d )\n", phGlobal, fTile);
|
|
Log.OnExit (" ( %X )\n", &hr);
|
|
|
|
//
|
|
// Create a metafile, and have the cache draw its metafile
|
|
// into _our_ metafile.
|
|
//
|
|
|
|
//
|
|
// First, set up the METAFILEPICT structure.
|
|
// Since ANISOTROPIC mode allows arbitrary scaling extents, we
|
|
// pick 1000 as a nice arbitrary size.
|
|
//
|
|
//
|
|
|
|
METAFILEPICT *pmfp = NULL;
|
|
if (S_OK == hr)
|
|
{
|
|
*phGlobal = GlobalAlloc(GMEM_FIXED, sizeof(METAFILEPICT));
|
|
if (NULL == *phGlobal)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
pmfp = (METAFILEPICT *) GlobalLock(*phGlobal);
|
|
if (NULL == pmfp)
|
|
{
|
|
GlobalFree(*phGlobal);
|
|
*phGlobal = NULL;
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
pmfp->xExt = 1000;
|
|
pmfp->yExt = 1000;
|
|
pmfp->mm = MM_ANISOTROPIC;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now create the metafile within the METAFILEPICT structure,
|
|
// and ask the cache to draw to it.
|
|
//
|
|
|
|
HDC mfdc;
|
|
if (S_OK == hr)
|
|
{
|
|
mfdc = CreateMetaFile(NULL);
|
|
if (NULL == mfdc)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
GlobalUnlock(*phGlobal);
|
|
GlobalFree(*phGlobal);
|
|
*phGlobal = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we are not tiling the metafile, we draw it exactly once,
|
|
// scaled to fit the entire output metafile
|
|
//
|
|
|
|
if (S_OK == hr && FALSE == fTile)
|
|
{
|
|
RECTL rcBounds = {0, 0, 1000, 1000};
|
|
RECTL rcWBounds = {0, 0, 1000, 1000};
|
|
|
|
SetMapMode(mfdc, MM_ANISOTROPIC);
|
|
SetWindowExtEx(mfdc, 1000, 1000, NULL);
|
|
SetWindowOrgEx(mfdc, 0, 0, NULL);
|
|
|
|
hr = m_pViewObject->Draw(DVASPECT_CONTENT, // Aspect
|
|
DEF_LINDEX, // LIndex
|
|
NULL, // pvAspect
|
|
NULL, // ptd
|
|
NULL, // hicTargetDev
|
|
mfdc, // hdc to draw to
|
|
&rcBounds, // rectange to draw to
|
|
&rcWBounds, // bounds of our mfdc
|
|
NULL, // callback fn
|
|
0); // continue param
|
|
|
|
}
|
|
|
|
//
|
|
// If we are tiling the metafile (which tests the ability of
|
|
// the cache to offset and scale the presentation to a rect within
|
|
// a larger metafile rect), we draw it once in each of the four
|
|
// corners
|
|
//
|
|
|
|
if (S_OK == hr && TRUE == fTile)
|
|
{
|
|
RECTL rcBounds;
|
|
RECTL rcWBounds = {0, 0, 1000, 1000};
|
|
|
|
SetMapMode(mfdc, MM_ANISOTROPIC);
|
|
SetWindowExtEx(mfdc, 1000, 1000, NULL);
|
|
SetWindowOrgEx(mfdc, 0, 0, NULL);
|
|
|
|
for (int a=0; a < 4 && S_OK == hr; a++)
|
|
{
|
|
switch(a)
|
|
{
|
|
case 0: // Upper left hand tile
|
|
|
|
rcBounds.left = 0;
|
|
rcBounds.top = 0;
|
|
rcBounds.right = 500;
|
|
rcBounds.bottom = 500;
|
|
break;
|
|
|
|
case 1: // Upper right hand tile
|
|
|
|
rcBounds.left = 500;
|
|
rcBounds.top = 0;
|
|
rcBounds.right = 1000;
|
|
rcBounds.bottom = 500;
|
|
break;
|
|
|
|
case 2: // Lower left hand tile
|
|
|
|
rcBounds.left = 0;
|
|
rcBounds.top = 500;
|
|
rcBounds.right = 500;
|
|
rcBounds.bottom = 1000;
|
|
break;
|
|
|
|
case 3: // Lower right hand tile
|
|
|
|
rcBounds.left = 500;
|
|
rcBounds.top = 500;
|
|
rcBounds.right = 1000;
|
|
rcBounds.bottom = 1000;
|
|
break;
|
|
}
|
|
|
|
hr = m_pViewObject->Draw(DVASPECT_CONTENT, // Aspect
|
|
DEF_LINDEX, // LIndex
|
|
NULL, // pvAspect
|
|
NULL, // ptd
|
|
NULL, // hicTargetDev
|
|
mfdc, // hdc to draw to
|
|
&rcBounds, // rectange to draw to
|
|
&rcWBounds, // bounds of our mfdc
|
|
NULL, // callback fn
|
|
0); // continue param
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the draw failed, clean up the metafile DC now
|
|
//
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
GlobalUnlock(*phGlobal);
|
|
GlobalFree(*phGlobal);
|
|
|
|
HMETAFILE temp = CloseMetaFile(mfdc);
|
|
if (temp)
|
|
{
|
|
DeleteMetaFile(temp);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finish up the metafile and prepare to return it to the caller
|
|
//
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
pmfp->hMF = CloseMetaFile(mfdc);
|
|
|
|
if (pmfp->hMF)
|
|
{
|
|
GlobalUnlock(*phGlobal);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
GlobalUnlock(*phGlobal);
|
|
GlobalFree(*phGlobal);
|
|
*phGlobal = NULL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: TestInstance::GetCurrentState
|
|
//
|
|
// Synopsis: Returns the state of the unit test (for drawing)
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 04-Sep-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
TEST_STATE TestInstance::GetCurrentState()
|
|
{
|
|
//
|
|
// In order to avoid race conditions, we have a mutex around the
|
|
// current state of the unit test (required because this member
|
|
// function will be running on the window's thread, not the current
|
|
// test instance thread.)
|
|
//
|
|
|
|
DWORD dwResult = WaitForSingleObject(ctest.Mutex(), INFINITE);
|
|
if (WAIT_FAILED == dwResult)
|
|
{
|
|
return INVALID_STATE;
|
|
}
|
|
|
|
TEST_STATE tsSnapshot = m_State;
|
|
|
|
ReleaseMutex(ctest.Mutex());
|
|
|
|
return tsSnapshot;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: TestInstance::SetCurrentState
|
|
//
|
|
// Synopsis: Sets the current (drawing) state of the unit test
|
|
//
|
|
// Arguments: [state] - the state to set
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 04-Sep-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TestInstance::SetCurrentState(TEST_STATE state)
|
|
{
|
|
//
|
|
// In order to avoid race conditions, we have a mutex around the
|
|
// current state of the unit test (required because this member
|
|
// function will be running on the window's thread, not the current
|
|
// test instance thread.)
|
|
//
|
|
|
|
DWORD dwResult = WaitForSingleObject(ctest.Mutex(), INFINITE);
|
|
if (WAIT_FAILED == dwResult)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_State = state;
|
|
|
|
ReleaseMutex(ctest.Mutex());
|
|
|
|
//
|
|
// Invalid the main window so it will redraw itself with the new
|
|
// state of the test.
|
|
//
|
|
|
|
InvalidateRgn(ctest.Window(), NULL, FALSE);
|
|
UpdateWindow(ctest.Window());
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: TestInstance::Draw
|
|
//
|
|
// Synopsis: Draws the current state of the unit test
|
|
//
|
|
// Arguments: [hdc] - The DC to draw to
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes: The DC is supplied, but the main window is assumed
|
|
//
|
|
// History: 04-Sep-94 Davepl Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static char szStarting[] = "Test is starting...";
|
|
static char szInvalid[] = "The state of the test has become invalid...";
|
|
static char szEnumerator[] = "Testing the cache enumerator...";
|
|
static char szSaveReload[] = "Saving and reloading the cache and its data...";
|
|
static char szDataTest[] = "Testing data integrity within the cache...";
|
|
static char szMulti[] = "Testing a large number of simultaneous cache nodes...";
|
|
static char szMetafile[] = "MF -> MF";
|
|
static char szMetafileTiled[] = "MF -> MF (Tiled)";
|
|
static char szDib[] = ""; // Dib contains its own title
|
|
|
|
void TestInstance::Draw(HDC hdc)
|
|
{
|
|
//
|
|
// Retrieve the current state of the unit test
|
|
//
|
|
|
|
TEST_STATE tsState = GetCurrentState();
|
|
|
|
//
|
|
// Clear the window
|
|
//
|
|
|
|
RECT rect;
|
|
if (TRUE == GetClientRect(ctest.Window(), &rect))
|
|
{
|
|
FillRect(hdc, &rect, (HBRUSH) GetStockObject(LTGRAY_BRUSH));
|
|
}
|
|
|
|
//
|
|
// Draw the current state
|
|
//
|
|
|
|
int iBackMode = SetBkMode(hdc, TRANSPARENT);
|
|
|
|
switch(tsState)
|
|
{
|
|
case TEST_STARTING:
|
|
|
|
TextOut(hdc, 10, 10, szStarting, strlen(szStarting));
|
|
break;
|
|
|
|
case TESTING_ENUMERATOR:
|
|
|
|
TextOut(hdc, 10, 10, szEnumerator, strlen(szEnumerator));
|
|
break;
|
|
|
|
case SAVE_AND_RELOAD:
|
|
|
|
TextOut(hdc, 10, 10, szSaveReload, strlen(szSaveReload));
|
|
break;
|
|
|
|
case DATA_TEST:
|
|
|
|
TextOut(hdc, 10, 10, szDataTest, strlen(szDataTest));
|
|
break;
|
|
|
|
case MULTI_CACHE:
|
|
|
|
TextOut(hdc, 10, 10, szMulti, strlen(szMulti));
|
|
break;
|
|
|
|
case DRAW_METAFILE_NOW:
|
|
case DRAW_METAFILETILED_NOW:
|
|
case DRAW_DIB_NOW:
|
|
case DRAW_DIBTILED_NOW:
|
|
{
|
|
// We know now that we have to draw a metafile, so
|
|
// determine which of the metafiles we should be drawing,
|
|
// and set a handle (so we can reuse the draw code) and
|
|
// the description text appropriately.
|
|
|
|
HGLOBAL hMFP;
|
|
char * szDesc;
|
|
|
|
if (DRAW_METAFILE_NOW == tsState)
|
|
{
|
|
hMFP = ctest.m_hMFP;
|
|
szDesc = szMetafile;
|
|
}
|
|
else if (DRAW_METAFILETILED_NOW == tsState)
|
|
{
|
|
hMFP = ctest.m_hMFPTILED;
|
|
szDesc = szMetafileTiled;
|
|
}
|
|
else if (DRAW_DIB_NOW == tsState)
|
|
{
|
|
hMFP = ctest.m_hMFPDIB;
|
|
szDesc = szDib;
|
|
}
|
|
else if (DRAW_DIBTILED_NOW == tsState)
|
|
{
|
|
hMFP = ctest.m_hMFPDIBTILED;
|
|
szDesc = szDib;
|
|
}
|
|
|
|
TextOut(hdc, 10, 10, szDesc, strlen(szDesc));
|
|
|
|
//
|
|
// Now actually draw the metafile to our main window
|
|
//
|
|
|
|
if (hMFP)
|
|
{
|
|
METAFILEPICT *pMFP = (METAFILEPICT *) GlobalLock(hMFP);
|
|
if (NULL == pMFP)
|
|
{
|
|
mprintf("Unable to lock metafile handle");
|
|
break;
|
|
}
|
|
|
|
int save = SaveDC(hdc);
|
|
|
|
SetMapMode(hdc, pMFP->mm);
|
|
SetWindowExtEx(hdc, pMFP->xExt, pMFP->yExt, NULL);
|
|
|
|
RECT client;
|
|
GetClientRect(ctest.Window(), &client);
|
|
|
|
SetViewportExtEx(hdc, client.right, client.bottom, NULL);
|
|
SetWindowOrgEx(hdc, 0, 0, NULL);
|
|
SetViewportOrgEx(hdc, client.left, client.top, NULL);
|
|
|
|
PlayMetaFile(hdc, pMFP->hMF);
|
|
|
|
RestoreDC(hdc, save);
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
case INVALID_STATE:
|
|
default:
|
|
|
|
TextOut(hdc, 10, 10, szInvalid, strlen(szInvalid));
|
|
break;
|
|
|
|
}
|
|
|
|
SetBkMode(hdc, iBackMode);
|
|
|
|
}
|
|
|