VDMDBG Event | TOOLHELP Equivalent |
DBG_SEGLOAD | NFY_LOADSEG |
DBG_SEGMOVE | |
DBG_SEGFREE | NFY_FREESEG |
DBG_MODLOAD | |
DBG_MODFREE | NFY_DELMODULE |
DBG_SINGLESTEP | INT_1 |
DBG_BREAK | INT_3 |
DBG_GPFAULT | INT_GPFAULT |
DBG_DIVOVERFLOW | INT_DIV0 |
DBG_INSTRFAULT | INT_UDINSTR |
DBG_TASKSTART | NFY_STARTTASK |
DBG_TASKSTOP | NFY_EXITTASK |
DBG_DLLSTART | NFY_STARTDLL |
DBG_DLLSTOP | |
DBG_ATTACH | |
DBG_TOOLHELP | |
DBG_STACKFAULT | INT_STKFAULT |
DBG_WOWINIT | |
DBG_TEMPBP | |
DBG_MODMOVE | |
DBG_INIT | |
DBG_GPFAULT2 |
//==================================================
// VDMDBGDemo - Matt Pietrek 1998
// Microsoft Systems Journal, August 1998
// FILE: VDMDBGDemo.CPP
//==================================================
#include <windows.h>
#include <COMMCTRL.H>
#include <stdio.h>
#include <process.h>
#include <tchar.h>
#include <vdmdbg.h>
#pragma hdrstop
#include "VDMDBGDemo.h"
#include "VDMDBGDemoDbgLoop.h"
// Helper function prototypes
void Handle_WM_INITDIALOG(HWND hDlg);
void Handle_WM_COMMAND(HWND hWndDlg, WPARAM wParam, LPARAM lParam );
void Handle_WM_CLOSE( HWND hDlg );
void Handle_WM_SIZE(HWND hWndDlg, WPARAM wParam, LPARAM lParam );
void Handle_WM_NOTIFY( HWND hDlg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK VDMDBGDemoDlgProc(HWND,UINT,WPARAM,LPARAM);
void GetSetPositionInfoFromRegistry( BOOL fSave, POINT *lppt );
void PopulateTree( HWND hWndTree );
HTREEITEM AddTreeviewSubItem( HWND hWndTree, HTREEITEM hTreeItem,
LPTSTR pszItemText, BOOL fItemData = FALSE,
LPARAM itemData = 0 );
TCHAR gszRegistryKey[] = _T("Software\\WheatyProductions\\VDMDBGDemo");
TCHAR gszMBTitle[] = _T( "VDMDBGDemo, by Matt Pietrek - MSJ August 1998" );
TCHAR gszAboutText[] =
_T("VDMDBGDemo displays information and events in NTVDM processes\r\n\r\n")
_T("To monitor events in a NTVDM session, highlight the desired session, ")
_T("then click the Attach button");
TCHAR gszCloseWarning[] =
_T("There is at least one NTVDM session being monitored.\r\n\r\nIf you ")
_T("exit, the Se sessions will be terminated\r\n\r\nExit anyway?");
// ============================== Start of code ===============================
HWND g_hWndTree = 0;
HWND g_hDlg = 0;
DWORD g_cAttachedProcesses = 0;
BOOL
WINAPI
VDMTaskEnumProc( DWORD dwThreadId, WORD hMod16, WORD hTask16,
PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined )
{
TCHAR szTaskInfo[512];
wsprintf(szTaskInfo,
_T("hMod:%04X hTask:%04X ThreadId:%u ModName: %hs FileName:%hs"),
hMod16, hTask16, dwThreadId, pszModName, pszFileName );
HTREEITEM hTreeITem = AddTreeviewSubItem(g_hWndTree, (HTREEITEM)lpUserDefined,
szTaskInfo );
return FALSE;
}
BOOL WINAPI
VDMProcessEnumProc( DWORD dwProcessId, DWORD dwAttributes, LPARAM lpUserDefined )
{
TCHAR szVDMInfo[256];
wsprintf( szVDMInfo, _T("NTVDM session (process id: %u attributes:%X %s)"),
dwProcessId, dwAttributes,
dwAttributes & WOW_SYSTEM ? _T("(default session)") : _T("") );
HTREEITEM hTreeItem = AddTreeviewSubItem( g_hWndTree, NULL, szVDMInfo,
TRUE, dwProcessId );
VDMEnumTaskWOWEx( dwProcessId, VDMTaskEnumProc, (LPARAM)hTreeItem );
TreeView_Expand( g_hWndTree, hTreeItem, TVE_EXPAND );
// _beginthread( VDMDebugThreadFunc, 0, (void *)dwProcessId );
return FALSE;
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow )
{
InitCommonControls();
// Bring up the user interface
DialogBox( hInstance, MAKEINTRESOURCE(IDD_VDMDBGDEMO),
0, (DLGPROC)VDMDBGDemoDlgProc );
GetLastError();
return 0;
}
BOOL CALLBACK VDMDBGDemoDlgProc( HWND hDlg,UINT msg,WPARAM wParam, LPARAM lParam )
{
// The dialog procedure for the main window
switch ( msg )
{
case WM_INITDIALOG:
Handle_WM_INITDIALOG( hDlg ); return TRUE;
case WM_CLOSE:
Handle_WM_CLOSE( hDlg ); break;
case WM_SIZE:
Handle_WM_SIZE( hDlg, wParam, lParam ); break;
case WM_COMMAND:
Handle_WM_COMMAND( hDlg, wParam, lParam ); break;
case WM_NOTIFY:
Handle_WM_NOTIFY( hDlg, wParam, lParam ); break;
case WM_TIMER:
PopulateTree( g_hWndTree ); break;
case WM_LB_ADDITEM:
{
TCHAR * pwszItem = (TCHAR *)lParam;
SendDlgItemMessage(hDlg, IDC_LIST1, LB_ADDSTRING, 0,
(LPARAM)pwszItem);
delete []pwszItem;
}
break;
// let everything else fall through
}
return FALSE;
}
//============================================================================
// Walk through the list of objects, adding each object name to the root of
// the treeview
void PopulateTree( HWND hWndTree )
{
TreeView_DeleteAllItems( hWndTree );
VDMEnumProcessWOW( VDMProcessEnumProc, (LPARAM)hWndTree );
}
void Handle_WM_INITDIALOG(HWND hDlg)
{
// Get the window coordinates where the program was last running,
// and move the window to that spot.
POINT pt;
GetSetPositionInfoFromRegistry( FALSE, &pt );
SetWindowPos(hDlg, 0, pt.x, pt.y, 0, 0,
SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER | SWP_NOACTIVATE);
g_hDlg = hDlg;
g_hWndTree = GetDlgItem(hDlg, IDC_TREE1);
PopulateTree( g_hWndTree );
SetTimer( hDlg, 0, 10000, 0 ); // Make a 2 second timer
}
void Handle_WM_COMMAND(HWND hWndDlg, WPARAM wParam, LPARAM lParam )
{
WORD wNotifyCode = HIWORD(wParam); // notification code
WORD wID = LOWORD(wParam); // item, control, or accelerator id
HWND hwndCtl = (HWND) lParam; // handle of control
if ( IDC_BUTTON_REFRESH == wID )
{
if ( BN_CLICKED == wNotifyCode )
PopulateTree( g_hWndTree );
}
else if ( IDC_BUTTON_ATTACH == wID )
{
HTREEITEM hTreeItem = TreeView_GetSelection( g_hWndTree );
TVITEM tvi;
tvi.hItem = hTreeItem;
tvi.mask = TVIF_PARAM;
tvi.lParam = 0;
// Get the process ID for the session that we stashed away earlier
if ( TreeView_GetItem( g_hWndTree, &tvi ) && tvi.lParam )
{
// start a new thread to act as the debug loop
_beginthread( VDMDebugThreadFunc, 0, (void *)tvi.lParam );
}
else // User didn't pick a valid line
{
MessageBox( hWndDlg, _T("Please select an NTVDM Session line"),
0, MB_OK );
}
}
else if ( IDC_BUTTON_ABOUT == wID )
{
if ( BN_CLICKED == wNotifyCode )
MessageBox( hWndDlg, gszAboutText, gszMBTitle, MB_OK );
}
}
void Handle_WM_NOTIFY( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
if ( wParam != IDC_TREE1 )
return;
LPNMHDR pnmh = (LPNMHDR)lParam;
if ( NM_DBLCLK != pnmh->code )
return;
HTREEITEM hTreeItem = TreeView_GetSelection( g_hWndTree );
TVITEM tvi;
tvi.hItem = hTreeItem;
tvi.mask = TVIF_PARAM;
if ( TreeView_GetItem( g_hWndTree, &tvi ) )
{
if ( tvi.lParam )
_beginthread( VDMDebugThreadFunc, 0, (void *)tvi.lParam );
}
}
void Handle_WM_CLOSE( HWND hDlg )
{
// If g_cAttachedProcesses is non-zero at program exit time, we need to
// warn the user that exiting will terminate any debug loops, and hence
// make the 16 bit programs is the associated NTVDM sessions go away.
if ( g_cAttachedProcesses )
{
if ( IDNO == MessageBox(hDlg, gszCloseWarning, gszMBTitle, MB_YESNO))
return;
}
// Save off the window's X,Y coordinates for next time
RECT rect;
if ( GetWindowRect( hDlg, &rect ) )
GetSetPositionInfoFromRegistry( TRUE, (LPPOINT)&rect );
KillTimer( hDlg, 0 );
EndDialog(hDlg, 0);
}
void Handle_WM_SIZE(HWND hWndDlg, WPARAM wParam, LPARAM lParam )
{
RECT tvRect, dlgRect;
POINT pt;
WORD nClientWidth = LOWORD(lParam);
WORD nClientHeight= HIWORD(lParam);
GetClientRect( hWndDlg, &dlgRect ); // Get size of dialog
GetWindowRect( g_hWndTree, &tvRect ); // Get screen position of child
pt.x = tvRect.left; // Get the X,Y coordinates for the top left
pt.y = tvRect.top; // and reuse the M in the resized client
ScreenToClient( hWndDlg, &pt ); // Calculate screen X,Y of child window
WORD tvWidth = nClientWidth - ( pt.x * 2); // Equal spacing on all borders
WORD tvHeight= nClientHeight - (WORD)(pt.y + pt.x);
MoveWindow( g_hWndTree, pt.x, pt.y, tvWidth, tvHeight, TRUE );
}
void GetSetPositionInfoFromRegistry( BOOL fSave, POINT *lppt )
{
// Function that saves or restores the coordinates of a dialog box
// in the system registry. Handles the case where there's nothing there.
//
HKEY hKey;
DWORD dataSize, err, disposition;
TCHAR szKeyName[] = _T("DlgCoordinates");
if ( !fSave ) // In case the key's not there yet, we'll
lppt->x = lppt->y = 0; // return 0,0 for the coordinates
// Open the registry key (or create it if the first time being used)
err = RegCreateKeyEx( HKEY_CURRENT_USER, gszRegistryKey, 0, 0,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
0, &hKey, &disposition );
if ( ERROR_SUCCESS != err )
return;
if ( fSave ) // Save out coordinates
{
RegSetValueEx(hKey,szKeyName, 0, REG_BINARY,(PBYTE)lppt,sizeof(*lppt));
}
else // read in coordinates
{
dataSize = sizeof(*lppt);
RegQueryValueEx( hKey, szKeyName, 0, 0, (PBYTE)lppt, &dataSize );
}
RegCloseKey( hKey );
}
HTREEITEM AddTreeviewSubItem( HWND hWndTree, HTREEITEM hTreeItem,
LPTSTR pszItemText, BOOL fItemData,
LPARAM itemData )
{
TVINSERTSTRUCT tvi;
tvi.hParent = hTreeItem;
tvi.hInsertAfter = TVI_LAST;
tvi.item.mask = TVIF_TEXT;
tvi.item.pszText = pszItemText;
tvi.item.cchTextMax = lstrlen( pszItemText );
if ( fItemData )
{
tvi.item.mask |= TVIF_PARAM;
tvi.item.lParam = itemData;
}
return TreeView_InsertItem( hWndTree, &tvi );
}
VDMDBGDemoDbgLoop.H
void VDMDebugThreadFunc( void * args );
VDMDBGDemoDbgLoop.CPP
//==================================================
// VDMDBGDemo - Matt Pietrek 1998
// Microsoft Systems Journal, August 1998
// FILE: VDMDBGDemoDbgLoop.CPP
//==================================================
#include <windows.h>
#include <stdio.h>
#include <vdmdbg.h>
#include <tchar.h>
#include <stdarg.h>
#include "VDMDBGDemo.h"
#include "VDMDBGDemoDbgLoop.h"
//============================ Prototypes ===================================
int _lbPrintf( LPTSTR format, ... );
void HookupToPSAPI( void );
DWORD WINAPI GetModuleFileNameExW( // Stolen from PSAPI.H, which not everybody
HANDLE hProcess, // may have...
HMODULE hModule,
LPWSTR lpFilename,
DWORD nSize );
typedef DWORD (WINAPI *PFNGETMODULEFILENAMEEXW)(HANDLE,HMODULE,LPWSTR,DWORD);
//============================= Variables ===================================
PFNGETMODULEFILENAMEEXW pfnGetModuleFileNameExW = 0;
__declspec(thread) HANDLE tls_hProcess; // A per-thread variable
// Defined in VDMDBGDemo.CPP
extern HWND g_hDlg;
extern DWORD g_cAttachedProcesses;
//============================= Code ========================================
void HandleExceptionDebugEvent( DEBUG_EVENT &de )
{
EXCEPTION_RECORD & exrec = de.u.Exception.ExceptionRecord;
DWORD dwExceptionCode = exrec.ExceptionCode;
if ( STATUS_VDM_EVENT == dwExceptionCode )
{
VDMProcessException( &de );
LPTSTR pszEventName = 0;
switch ( W1( exrec ) )
{
case DBG_SEGLOAD:
{
pszEventName = _T("DBG_SEGLOAD");
SEGMENT_NOTE segNote;
DWORD cbRead = 0;
ReadProcessMemory( tls_hProcess, (PVOID)DW3(exrec),
&segNote, sizeof(segNote), &cbRead );
if ( sizeof(SEGMENT_NOTE) != cbRead )
break;
_lbPrintf( _T("%s(%u): %hs(%u)"), pszEventName,
de.dwThreadId, segNote.Module, segNote.Segment);
return;
}
case DBG_TASKSTART: // the Se events can all be handled
case DBG_DLLSTART: // in the same manner
case DBG_DLLSTOP:
case DBG_TASKSTOP:
{
switch( W1(exrec) )
{
case DBG_TASKSTART:
pszEventName = _T("DBG_TASKSTART"); break;
case DBG_DLLSTART:
pszEventName = _T("DBG_DLLSTART"); break;
case DBG_DLLSTOP:
pszEventName = _T("DBG_DLLSTOP"); break;
case DBG_TASKSTOP:
pszEventName = _T("DBG_TASKSTOP"); break;
}
IMAGE_NOTE imgNote;
DWORD cbRead;
ReadProcessMemory( tls_hProcess, (PVOID)DW3(exrec),
&imgNote, sizeof(imgNote), &cbRead );
if ( sizeof(IMAGE_NOTE) != cbRead )
break;
_lbPrintf( _T("%s(%u): %hs %hs"), pszEventName,
de.dwThreadId, imgNote.Module, imgNote.FileName);
return;
}
case DBG_MODFREE:
{
pszEventName = _T("DBG_MODFREE");
SEGMENT_NOTE segNote;
DWORD cbRead;
ReadProcessMemory( tls_hProcess, (PVOID)DW3(exrec),
&segNote, sizeof(segNote), &cbRead );
if ( sizeof(SEGMENT_NOTE) == cbRead )
{
_lbPrintf(_T("%s(%u): %hs"),
pszEventName, de.dwThreadId, segNote.Module);
return;
}
// else, fall through...
break;
}
case DBG_SEGMOVE: pszEventName = _T("DBG_SEGMOVE"); break;
case DBG_SEGFREE: pszEventName = _T("DBG_SEGFREE"); break;
case DBG_MODLOAD: pszEventName = _T("DBG_MODLOAD"); break;
case DBG_SINGLESTEP: pszEventName = _T("DBG_SINGLESTEP"); break;
case DBG_BREAK: pszEventName = _T("DBG_BREAK"); break;
case DBG_GPFAULT: pszEventName = _T("DBG_GPFAULT"); break;
case DBG_DIVOVERFLOW: pszEventName = _T("DBG_DIVOVERFLOW"); break;
case DBG_INSTRFAULT: pszEventName = _T("DBG_INSTRFAULT"); break;
case DBG_ATTACH: pszEventName = _T("DBG_ATTACH"); break;
}
_lbPrintf( _T("%s(%u)"), pszEventName, de.dwThreadId );
return;
}
_lbPrintf(
_T("EXCEPTION_DEBUG_EVENT(%u): Code:%08X Address:%08X %s chance"),
de.dwThreadId, dwExceptionCode, exrec.ExceptionAddress,
de.u.Exception.dwFirstChance ? _T("first") : _T("second") );
}
void HandleLoadDllDebugEvent( DEBUG_EVENT &de )
{
LOAD_DLL_DEBUG_INFO & loadInfo = de.u.LoadDll;
if ( pfnGetModuleFileNameExW )
{
TCHAR szModName[MAX_PATH];
szModName[0] = 0;
pfnGetModuleFileNameExW(tls_hProcess, (HINSTANCE)loadInfo.lpBaseOfDll,
szModName, sizeof(szModName) );
_lbPrintf( _T("LOAD_DLL_DEBUG_EVENT(%u): %08X %s"),
de.dwThreadId, loadInfo.lpBaseOfDll, szModName );
}
else
_lbPrintf( _T("LOAD_DLL_DEBUG_EVENT(%u): %08X"),
de.dwThreadId, loadInfo.lpBaseOfDll );
CloseHandle( loadInfo.hFile ); // Don't need this, so close it
}
void HandleCreateThreadDebugEvent( DEBUG_EVENT &de )
{
CloseHandle( de.u.CreateThread.hThread ); // Don't need this, so close it
_lbPrintf( _T("CREATE_THREAD_DEBUG_EVENT(%u)"), de.dwThreadId );
}
void HandleCreateProcessDebugEvent( DEBUG_EVENT &de )
{
CREATE_PROCESS_DEBUG_INFO & cpdi = de.u.CreateProcessInfo;
tls_hProcess = cpdi.hProcess;
CloseHandle( cpdi.hFile ); // Don't need the Se, so
CloseHandle( cpdi.hThread ); // close it!
if ( pfnGetModuleFileNameExW )
{
TCHAR szModName[MAX_PATH];
szModName[0] = 0;
pfnGetModuleFileNameExW(tls_hProcess, (HINSTANCE)cpdi.lpBaseOfImage,
szModName, sizeof(szModName) );
_lbPrintf( _T("CREATE_PROCESS_DEBUG_EVENT(%u): %08X %s"),
de.dwThreadId, cpdi.lpBaseOfImage, szModName );
}
else
_lbPrintf( _T("CREATE_PROCESS_DEBUG_EVENT: %08X"), cpdi.lpBaseOfImage );
}
void HandleExitThreadDebugEvent( DEBUG_EVENT &de )
{
_lbPrintf( _T("EXIT_THREAD_DEBUG_EVENT(%u): exit code:%u"),
de.dwThreadId, de.u.ExitThread.dwExitCode );
}
void HandleDebugEvent( DEBUG_EVENT &de )
{
LPTSTR s;
switch ( de.dwDebugEventCode )
{
case EXCEPTION_DEBUG_EVENT:
HandleExceptionDebugEvent( de ); return;
case LOAD_DLL_DEBUG_EVENT:
HandleLoadDllDebugEvent( de ); return;
case CREATE_THREAD_DEBUG_EVENT:
HandleCreateThreadDebugEvent( de ); return;
case CREATE_PROCESS_DEBUG_EVENT:
HandleCreateProcessDebugEvent( de ); return;
case EXIT_THREAD_DEBUG_EVENT:
HandleExitThreadDebugEvent( de ); return;
case EXIT_PROCESS_DEBUG_EVENT: s = _T("EXIT_PROCESS_DEBUG_EVENT"); break;
case UNLOAD_DLL_DEBUG_EVENT: s = _T("UNLOAD_DLL_DEBUG_EVENT"); break;
case OUTPUT_DEBUG_STRING_EVENT: s = _T("OUTPUT_DEBUG_STRING_EVENT");
break;
case RIP_EVENT: s = _T("RIP_EVENT"); break;
}
_lbPrintf( _T("%s(%u)"), s, de.dwThreadId );
}
void VDMDebugThreadFunc( void * args )
{
DWORD dwProcessId = (DWORD)args; // "args" is just the NTVDM process Id
if ( !DebugActiveProcess( dwProcessId ) )
return;
// Increment the count describing how many debug loops we have.
InterlockedIncrement( (LPLONG)&g_cAttachedProcesses );
HookupToPSAPI(); // In case the user has PSAPI.DLL, we can use it to
// get a better description of debug events
DEBUG_EVENT de;
while ( WaitForDebugEvent( &de, INFINITE ) )
{
HandleDebugEvent( de );
if ( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
break;
ContinueDebugEvent( de.dwProcessId, de.dwThreadId, DBG_CONTINUE );
}
// Decrement the count describing how many debug loops we have.
InterlockedDecrement( (LPLONG)&g_cAttachedProcesses );
}
void HookupToPSAPI( void )
{
HMODULE hModPSAPI;
if ( !pfnGetModuleFileNameExW )
{
hModPSAPI = LoadLibrary( _T("PSAPI.DLL") );
if ( hModPSAPI )
pfnGetModuleFileNameExW = (PFNGETMODULEFILENAMEEXW)
GetProcAddress( hModPSAPI, "GetModuleFileNameExW" );
}
}
int _lbPrintf( LPTSTR format, ... )
{
TCHAR szBuffer[1024];
va_list argptr;
va_start(argptr, format);
int retValue = wvsprintf(szBuffer, format, argptr);
va_end(argptr);
LPTSTR pwszDbgEvent = _wcsdup( szBuffer );
PostMessage( g_hDlg, WM_LB_ADDITEM, 0, (LPARAM)pwszDbgEvent );
return retValue;
}