Copyright © Microsoft Corporation. This document is an archived reproduction of a version originally published by Microsoft. It may have slight formatting modifications for consistency and to improve readability. |
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, these 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 them 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: // These 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 these, 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; }