|
|
/******************************Module*Header*******************************\
* Module Name: viewer.cxx * * Copyright (c) 2000 Microsoft Corporation * \**************************************************************************/
#include "precomp.hxx"
#include <tchar.h>
LRESULT CALLBACK ViewerWndProc(HWND, UINT, WPARAM, LPARAM);
typedef struct { HWND hViewerWnd; // To be set by created thread
PDEBUG_CLIENT Client; PSURF_INFO SurfInfo; } ViewerThreadParams;
DWORD WINAPI ViewerThread(ViewerThreadParams *);
const _TCHAR szClassName[] = _T("KD GDI Viewer"); const _TCHAR szWindowName[] = _T("KD GDI Viewer"); ATOM gViewerAtom; HBRUSH ghbrWhite; HPEN ghBorderPen;
const LONG DEFAULT_SCALE = 2;
class ViewerManager { public: ViewerManager(ULONG GrowLength = 4) { BeingDestroyed = FALSE; Wnds = 0; MaxWnds = 0; phWndList = NULL; GrowLen = (GrowLength == 0) ? 4 : GrowLength; __try { InitializeCriticalSection(&CritSect); CritOk = TRUE; Grow(); } __except (STATUS_NO_MEMORY) { CritOk = FALSE; } }
~ViewerManager() { if (CritOk) EnterCriticalSection(&CritSect); BeingDestroyed = TRUE; if (CritOk) LeaveCriticalSection(&CritSect);
DestroyAll(); // If we have Wnds left at this point all of them
// are now tracked as threads. Wait for each
// thread to completely finish.
if (Wnds) { DWORD WaitReturn; DbgPrint("Waiting for remaining %lu threads...\n", Wnds); WaitReturn = WaitForMultipleObjects(Wnds, (HANDLE *)phWndList, TRUE, INFINITE); DbgPrint("WaitForMultipleObjects returned %lx.\n", WaitReturn); while (Wnds-- > 0) { CloseHandle(phWndList[Wnds]); DbgPrint("ViewerManager::~ViewerManager calling ExtRelease().\n"); ExtRelease(); } }
HeapFree(hHeap, 0, phWndList); if (CritOk) DeleteCriticalSection(&CritSect); }
BOOL Grow();
BOOL Destroy(HWND);
void DestroyAll() { ULONG i = Wnds; while (i-- > 0) { Destroy(phWndList[i]); } }
private: ULONG Wnds; ULONG MaxWnds; HWND *phWndList; HANDLE hHeap; ULONG GrowLen; BOOL BeingDestroyed; BOOL CritOk; CRITICAL_SECTION CritSect;
friend DWORD WINAPI ViewerThread(ViewerThreadParams *);
BOOL Track(HWND hWnd) { if (this == NULL || !CritOk) return FALSE;
BOOL bTracked = FALSE;
EnterCriticalSection(&CritSect);
if (!BeingDestroyed && ((Wnds < MaxWnds) || Grow())) { DbgPrint("ViewerManager: Tracking %lx.\n", hWnd); phWndList[Wnds++] = hWnd; bTracked = TRUE; }
LeaveCriticalSection(&CritSect);
return bTracked; }
BOOL Untrack(HWND hWnd) { if (this == NULL || !CritOk) return FALSE;
BOOL bFound = FALSE;
EnterCriticalSection(&CritSect);
ULONG i = Wnds;
while (i-- > 0) { if (phWndList[i] == hWnd) { DbgPrint("ViewerManager: No longer tracking %lx.\n", hWnd); phWndList[i] = phWndList[--Wnds]; phWndList[Wnds] = NULL; bFound = TRUE; if (!BeingDestroyed) { DbgPrint("ViewerManager::Untrack calling ExtRelease().\n"); ExtRelease(); } break; } }
if (!bFound) DbgPrint("ViewerManager::Untrack didn't find %lx.\n", hWnd);
LeaveCriticalSection(&CritSect);
return bFound; }
};
BOOL ViewerManager::Grow() { if (MaxWnds > 0) { HWND *pNewList = (HWND *)HeapReAlloc(hHeap, HEAP_ZERO_MEMORY, phWndList, (MaxWnds + GrowLen)*sizeof(HWND));
if (pNewList != NULL) { phWndList = pNewList; MaxWnds += GrowLen; return TRUE; } } else { hHeap = GetProcessHeap();
if (hHeap) { phWndList = (HWND *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, GrowLen*sizeof(HWND)); if (phWndList != NULL) { MaxWnds = GrowLen; return TRUE; } } }
DbgPrint("ViewerManager::Grow FAILED!\n"); return FALSE; }
BOOL ViewerManager::Destroy(HWND hWnd) { ULONG i = Wnds;
DbgPrint("Looking for window %lx in %lu entries.\n", hWnd, i);
while (i-- > 0) { if (phWndList[i] == hWnd) { DbgPrint("Destroying window %lx at entry %lu.\n", hWnd, i); DWORD ThreadID = GetWindowThreadProcessId(hWnd, NULL); HANDLE hThread; if (hThread = OSCompat_OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, FALSE, ThreadID)) { BOOL bCloseHandle = TRUE;
DWORD ExitCode = STILL_ACTIVE; while (!PostMessage(hWnd, WM_DESTROY, 0, 0)) { DbgPrint("Waiting on post msg to %lx...\n", hWnd); Sleep(10); if (GetExitCodeThread(hThread, &ExitCode) && ExitCode != STILL_ACTIVE) { break; } }
// Check thread exit status
if (ExitCode == STILL_ACTIVE) { if (!GetExitCodeThread(hThread, &ExitCode)) { DbgPrint("GetExitCodeThread returned error %lx.\n", GetLastError()); } }
// Give the thread a chance to exit
if (ExitCode == STILL_ACTIVE) { DWORD WaitReturn;
DbgPrint("Waiting for hThread: %lx, ThreadID: %lx, hWnd: %lx.\n", hThread, ThreadID, hWnd);
if (WAIT_OBJECT_0 != (WaitReturn = WaitForSingleObject(hThread, 100))) { DbgPrint("WaitForSingleObject returned %lx.\n", WaitReturn); // If it hasn't exited and it called untrack
// to remove the hWnd we're concerned with,
// replace it with the thread handle so we may
// wait on it later.
EnterCriticalSection(&CritSect); if (phWndList[i] == hWnd) { phWndList[i] = (HWND)hThread; bCloseHandle = FALSE; } LeaveCriticalSection(&CritSect); } }
if (bCloseHandle) { // If the thread was still active, but the track entry
// has been removed, we have to wait for the thread to
// completely terminate.
if (ExitCode == STILL_ACTIVE) { DbgPrint("Inifinitely waiting for thread %lx to complete.\n", ThreadID); WaitForSingleObject(hThread, INFINITE);
if (!GetExitCodeThread(hThread, &ExitCode)) { DbgPrint("GetExitCodeThread returned error %lx.\n", GetLastError()); } else { DbgPrint("Thread exit code was %lx.\n", ExitCode); } }
DbgPrint("Closing hThread: %lx\n", hThread); CloseHandle(hThread);
if (BeingDestroyed) { DbgPrint("ViewerManager::Destroy calling ExtRelease().\n"); ExtRelease(); } } } else { // This really hurts.
// We have a tracked window, but we can't get
// information on it's thread, we have to stop
// tracking it.
DbgPrint("ViewerManager::Destroy: OpenThread returned error %lx!\n", GetLastError());
EnterCriticalSection(&CritSect); if (phWndList[i] == hWnd) { phWndList[i] = phWndList[--Wnds]; phWndList[Wnds] = NULL; } LeaveCriticalSection(&CritSect); }
return TRUE; } } return FALSE; }
ViewerManager *ViewerMgr;
void ViewerInit() { if (ViewerMgr == NULL) { ViewerMgr = new ViewerManager;
if (ViewerMgr == NULL) return; }
if (! ghbrWhite) { DbgPrint("ViewerInit: Creating white brush\n"); ghbrWhite = CreateSolidBrush(RGB(0xFF,0xFF,0xFF)); DbgPrint("ViewerInit: Created brush %lx\n", ghbrWhite); }
if (! ghBorderPen) { DbgPrint("ViewerInit: Creating redish pen\n"); ghBorderPen = CreatePen(PS_SOLID, 1, RGB(0xF0, 0x00, 0x3F)); DbgPrint("ViewerInit: Created pen %lx\n", ghBorderPen); }
if (! gViewerAtom) { WNDCLASSEX wcex;
DbgPrint("ViewerInit: Registering Class\n"); DbgPrint("ViewerInit: ghDllInst = %lx\n", ghDllInst);
wcex.cbSize = sizeof(wcex); wcex.style = CS_VREDRAW | CS_HREDRAW; wcex.lpfnWndProc = ViewerWndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = ghDllInst; wcex.hIcon = NULL; wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = (HBRUSH)( COLOR_WINDOW+1 ); wcex.lpszMenuName = NULL; wcex.lpszClassName = szClassName; wcex.hIconSm = NULL;
gViewerAtom = RegisterClassEx( &wcex ); } }
void ViewerExit() { if (ViewerMgr != NULL) { delete ViewerMgr; ViewerMgr = NULL; }
if (gViewerAtom) { DbgPrint("ViewerExit: Unregistering Class\n"); UnregisterClass((LPCSTR)gViewerAtom, 0); gViewerAtom = 0; }
if (ghBorderPen) { DbgPrint("ViewerExit: Deleting border pen\n"); DeleteObject(ghBorderPen); ghBorderPen = NULL; }
if (ghbrWhite) { DbgPrint("ViewerInit: Deleting white brush\n"); DeleteObject(ghbrWhite); ghbrWhite = NULL; } }
BOOL CALLBACK ViewerWndEnumProc( HWND hWnd, LPARAM lParam ) { HWND *phWndParent = (HWND *)lParam;
DbgPrint("Found hWnd %lx.\n", hWnd);
if (*phWndParent == NULL) { *phWndParent = hWnd; }
return TRUE; }
DWORD WINAPI ViewerThread( ViewerThreadParams *Params ) { HWND hWnd; BOOL bGetMsg; MSG msg; _TCHAR ViewerWndName[sizeof(szWindowName) + sizeof(Params->SurfInfo->SurfName) + 20]; _TCHAR *pszName = Params->SurfInfo->SurfName;
if (!pszName[0]) pszName = _T("UNAMED");
_stprintf(ViewerWndName, "%s: %s (%ldx%ldx%hubpp)", szWindowName, pszName, Params->SurfInfo->Width, Params->SurfInfo->Height, Params->SurfInfo->BitsPixel);
hWnd = CreateWindowEx(WS_EX_LEFT, (LPCSTR)gViewerAtom, ViewerWndName, WS_OVERLAPPEDWINDOW,// | WS_HSCROLL | WS_VSCROLL,
0, 0, Params->SurfInfo->Width*DEFAULT_SCALE+10,//32,
Params->SurfInfo->Height*DEFAULT_SCALE+29,//48,
NULL, NULL, ghDllInst, Params->SurfInfo // lParam passed to WM_CREATE handler
);
if (hWnd) { ViewerMgr->Track(hWnd);
Params->hViewerWnd = hWnd; // Params may no longer be valid.
ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd);
while( (bGetMsg = GetMessage(&msg, NULL, 0, 0 )) != 0 ) { if (bGetMsg == -1) { DbgPrint("ViewerThread exiting due to GetMessage error 0x%lx.\n", GetLastError()); break; } else { TranslateMessage( &msg ); DispatchMessage( &msg ); } }
DbgPrint("ViewerThread exiting properly.\n");
ViewerMgr->Untrack(hWnd); } else { DbgPrint("CreateWindow returned error %lx.\n", GetLastError());
msg.wParam = -1; }
DbgPrint("ViewerThread calling ExitThread().\n");
ExitThread((DWORD)msg.wParam); }
DWORD CreateViewer( PDEBUG_CLIENT Client, PSURF_INFO SurfInfo ) { ViewerThreadParams NewThreadParams = { NULL, Client, SurfInfo }; HRESULT Status;
// Reference Debug Client for ViewerThread
// since dbgeng/dbghelp aren't thread safe.
// ViewerManager will release client in a safe manner.
if ((Status = ExtQuery(Client)) != S_OK) return 0;
HWND hWndParent = NULL; EnumThreadWindows(GetCurrentThreadId(), (WNDENUMPROC)ViewerWndEnumProc, (LPARAM)&hWndParent);
HANDLE hThread; DWORD ThreadID = 0; hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ViewerThread, &NewThreadParams, 0, &ThreadID);
if (hThread) { while (NewThreadParams.hViewerWnd == NULL) { DWORD ExitCode = 0; if (!GetExitCodeThread(hThread, &ExitCode)) DbgPrint("GetExitCodeThread returned error %lx.\n", GetLastError()); if (ExitCode != STILL_ACTIVE) { ThreadID = 0; break; }
SleepEx(10, TRUE); }
CloseHandle(hThread); }
if (ThreadID == 0) { ExtRelease(); }
return ThreadID; }
// DelPropProc is a callback function
// that deletes a window property.
BOOL CALLBACK DelPropProc( HWND hwndSubclass, // handle of window with property
LPCSTR lpszString, // property string or atom
HANDLE hData) // data handle
{ RemoveProp(hwndSubclass, lpszString); return TRUE; }
LRESULT CALLBACK ViewerWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { // DbgPrint("ViewerWndProc(%lx, %lx, , )\n", hWnd, msg);
switch( msg ) { case WM_CREATE: { LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam; PSURF_INFO SurfInfo = (PSURF_INFO) CreateStruct->lpCreateParams;
DbgPrint("ViewerWndProc: WM_CREATE\n");
if (SurfInfo) { SetProp(hWnd, "hBitmap", SurfInfo->hBitmap); SetProp(hWnd, "xOrigin", LongToHandle(SurfInfo->xOrigin)); SetProp(hWnd, "yOrigin", LongToHandle(SurfInfo->yOrigin)); SetProp(hWnd, "Width", LongToHandle(SurfInfo->Width)); SetProp(hWnd, "Height", LongToHandle(SurfInfo->Height)); SetProp(hWnd, "BPP", LongToHandle(SurfInfo->BitsPixel)); SetProp(hWnd, "Scale", LongToHandle(DEFAULT_SCALE)); // SetProp(hWnd, "", SurfInfo->);
} else { ExtErr("ViewerWindow created with NULL PSURF_INFO.\n"); return -1; } } return 0;
case WM_KEYDOWN: if (wParam == VK_DOWN || wParam == VK_UP) { LONG Scale = HandleToLong(GetProp(hWnd, "Scale")); if (wParam == VK_DOWN) { if (Scale > 1) { SetProp(hWnd, "Scale", LongToHandle((Scale-1))); InvalidateRect(hWnd, NULL, TRUE); } } else { if (Scale < 16) { SetProp(hWnd, "Scale", LongToHandle((Scale+1))); InvalidateRect(hWnd, NULL, TRUE); } } return 0; } break;
case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; HBITMAP hBitmapOrg; HBRUSH hBrushOrg; HPEN hPenOrg;
BeginPaint(hWnd, &ps);
hdc = CreateCompatibleDC(ps.hdc); hBitmapOrg = (HBITMAP)SelectObject(hdc, (HBITMAP)GetProp(hWnd, "hBitmap")); if (hBitmapOrg == NULL) { DbgPrint("Error from SelectObject(, HBITMAP): %lx\n", GetLastError()); } hBrushOrg = (HBRUSH)SelectObject(ps.hdc, ghbrWhite); hPenOrg = (HPEN)SelectObject(ps.hdc, ghBorderPen); LONG xOrigin = HandleToLong(GetProp(hWnd, "xOrigin")); LONG yOrigin = HandleToLong(GetProp(hWnd, "yOrigin")); LONG Width = HandleToLong(GetProp(hWnd, "Width")); LONG Height = HandleToLong(GetProp(hWnd, "Height")); LONG Scale = HandleToLong(GetProp(hWnd, "Scale")); Rectangle(ps.hdc, 0, 0, Width*Scale+2, Height*Scale+2); if (!StretchBlt(ps.hdc, 1, 1, Width*Scale, Height*Scale, hdc, xOrigin, yOrigin, Width, Height, SRCCOPY)) { DbgPrint("Error from StrectBlt): %lx\n", GetLastError()); } SelectObject(ps.hdc, hPenOrg); SelectObject(ps.hdc, hBrushOrg); SelectObject(hdc, hBitmapOrg); DeleteDC(hdc);
EndPaint(hWnd, &ps); } return DefWindowProc( hWnd, msg, wParam, lParam );
case WM_DESTROY:
DbgPrint("ViewerWndProc: WM_DESTROY\n");
DeleteObject((HBITMAP)GetProp(hWnd, "hBitmap"));
EnumPropsEx(hWnd, (PROPENUMPROCEX)DelPropProc, NULL);
PostQuitMessage(0); break;
default: // DbgPrint("ViewerWndProc: unhandled msg %lx\n", msg);
break; }
return DefWindowProc( hWnd, msg, wParam, lParam ); }
|