windows-nt/Source/XPSP1/NT/com/oleutest/cachetst/main.cpp
2020-09-26 16:20:57 +08:00

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);
}