You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1935 lines
56 KiB
1935 lines
56 KiB
/*++
|
|
|
|
Copyright (c) 1999-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
windbg.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the main program, main window proc and MDICLIENT
|
|
window proc for Windbg.
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#include <dbghelp.h>
|
|
|
|
ULONG g_CodeDisplaySequence;
|
|
|
|
PTSTR g_ProgramName;
|
|
ULONG g_CommandLineStart;
|
|
PSTR g_RemoteOptions;
|
|
|
|
QUIET_MODES g_QuietMode = QMODE_DISABLED;
|
|
QUIET_MODES g_QuietSourceMode = QMODE_DISABLED;
|
|
|
|
ULONG g_DefPriority;
|
|
|
|
char g_TitleServerText[MAX_PATH];
|
|
char g_TitleExtraText[MAX_PATH];
|
|
BOOL g_ExplicitTitle;
|
|
|
|
PFN_FlashWindowEx g_FlashWindowEx;
|
|
|
|
BOOL g_AllowJournaling;
|
|
|
|
BOOL g_Exit;
|
|
|
|
// Handle to main window
|
|
HWND g_hwndFrame = NULL;
|
|
|
|
// Handle to MDI client
|
|
HWND g_hwndMDIClient = NULL;
|
|
|
|
// Width and height of MDI client.
|
|
ULONG g_MdiWidth, g_MdiHeight;
|
|
|
|
//Handle to instance data
|
|
HINSTANCE g_hInst;
|
|
|
|
//Handle to accelerator table
|
|
HACCEL g_hMainAccTable;
|
|
|
|
//Keyboard Hooks functions
|
|
HHOOK hKeyHook;
|
|
|
|
// WinDBG title text
|
|
TCHAR g_MainTitleText[MAX_MSG_TXT];
|
|
|
|
// menu that belongs to g_hwndFrame
|
|
HMENU g_hmenuMain;
|
|
HMENU g_hmenuMainSave;
|
|
|
|
// Window submenu
|
|
HMENU g_hmenuWindowSub;
|
|
|
|
#ifdef DBG
|
|
// Used to define debugger output
|
|
DWORD dwVerboseLevel = MIN_VERBOSITY_LEVEL;
|
|
#endif
|
|
|
|
INDEXED_COLOR g_Colors[COL_COUNT] =
|
|
{
|
|
// Set from GetSysColor(COLOR_WINDOW).
|
|
"Background", 0, 0, NULL,
|
|
|
|
// Set from GetSysColor(COLOR_WINDOWTEXT).
|
|
"Text", 0, 0, NULL,
|
|
|
|
// Set from GetSysColor(COLOR_HIGHLIGHT).
|
|
"Current line background", 0, 0, NULL,
|
|
|
|
// Set from GetSysColor(COLOR_HIGHLIGHTTEXT).
|
|
"Current line text", 0, 0, NULL,
|
|
|
|
// Purple.
|
|
"Breakpoint current line background", 0, RGB(255, 0, 255), NULL,
|
|
|
|
// Set from GetSysColor(COLOR_HIGHLIGHTTEXT).
|
|
"Breakpoint current line text", 0, 0, NULL,
|
|
|
|
// Red.
|
|
"Enabled breakpoint background", 0, RGB(255, 0, 0), NULL,
|
|
|
|
// Set from GetSysColor(COLOR_HIGHLIGHTTEXT).
|
|
"Enabled breakpoint text", 0, 0, NULL,
|
|
|
|
// Yellow.
|
|
"Disabled breakpoint background", 0, RGB(255, 255, 0), NULL,
|
|
|
|
// Set from GetSysColor(COLOR_HIGHLIGHTTEXT).
|
|
"Disabled breakpoint text", 0, 0, NULL,
|
|
|
|
// Red.
|
|
"Changed data text", 0, RGB(255, 0, 0), NULL,
|
|
|
|
// Set from GetSysColor(COLOR_3DFACE).
|
|
"Disabled window", 0, 0, NULL,
|
|
};
|
|
|
|
// There is a foreground and background color for each
|
|
// possible bit in the output mask. The default foreground
|
|
// color is normal window text and the background is
|
|
// the normal window background.
|
|
//
|
|
// There are also some extra colors for user-added output.
|
|
//
|
|
// Some mask bits have no assigned meaning right now and
|
|
// are given NULL names to mark them as skip entries. Their
|
|
// indices are allocated now for future use.
|
|
INDEXED_COLOR g_OutMaskColors[OUT_MASK_COL_COUNT] =
|
|
{
|
|
// 0x00000001 - 0x00000008.
|
|
"Normal level command window text", 0, 0, NULL,
|
|
"Normal level command window text background", 0, 0, NULL,
|
|
"Error level command window text", 0, 0, NULL,
|
|
"Error level command window text background", 0, 0, NULL,
|
|
"Warning level command window text", 0, 0, NULL,
|
|
"Warning level command window text background", 0, 0, NULL,
|
|
"Verbose level command window text", 0, 0, NULL,
|
|
"Verbose level command window text background", 0, 0, NULL,
|
|
// 0x00000010 - 0x00000080.
|
|
"Prompt level command window text", 0, 0, NULL,
|
|
"Prompt level command window text background", 0, 0, NULL,
|
|
"Prompt registers level command window text", 0, 0, NULL,
|
|
"Prompt registers level command window text background", 0, 0, NULL,
|
|
"Extension warning level command window text", 0, 0, NULL,
|
|
"Extension warning level command window text background", 0, 0, NULL,
|
|
"Debuggee level command window text", 0, 0, NULL,
|
|
"Debuggee level command window text background", 0, 0, NULL,
|
|
// 0x00000100 - 0x00000800.
|
|
"Debuggee prompt level command window text", 0, 0, NULL,
|
|
"Debuggee prompt level command window text background", 0, 0, NULL,
|
|
"Symbol message level command window text", 0, 0, NULL,
|
|
"Symbol message level command window text background", 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
// 0x00001000 - 0x00008000.
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
// 0x00010000 - 0x00080000.
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
// 0x00100000 - 0x00800000.
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
// 0x01000000 - 0x08000000.
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
NULL, 0, 0, NULL,
|
|
// 0x10000000 - 0x80000000.
|
|
"Internal event level command window text", 0, 0, NULL,
|
|
"Internal event level command window text background", 0, 0, NULL,
|
|
"Internal breakpoint level command window text", 0, 0, NULL,
|
|
"Internal breakpoint level command window text background", 0, 0, NULL,
|
|
"Internal remoting level command window text", 0, 0, NULL,
|
|
"Internal remoting level command window text background", 0, 0, NULL,
|
|
"Internal KD protocol level command window text", 0, 0, NULL,
|
|
"Internal KD protocol level command window text background", 0, 0, NULL,
|
|
// User-added text.
|
|
"User-added command window text", 0, 0, NULL,
|
|
"User-added command window text background", 0, 0, NULL,
|
|
};
|
|
|
|
COLORREF g_CustomColors[CUSTCOL_COUNT];
|
|
|
|
void
|
|
UpdateFrameTitle(void)
|
|
{
|
|
char Title[MAX_MSG_TXT + 2 * MAX_PATH + 32];
|
|
|
|
Title[0] = 0;
|
|
|
|
if (g_ExplicitTitle)
|
|
{
|
|
CatString(Title, g_TitleExtraText, DIMA(Title));
|
|
CatString(Title, " - ", DIMA(Title));
|
|
}
|
|
else
|
|
{
|
|
if (g_TitleServerText[0])
|
|
{
|
|
CatString(Title, g_TitleServerText, DIMA(Title));
|
|
CatString(Title, " - ", DIMA(Title));
|
|
}
|
|
|
|
if (g_TitleExtraText[0])
|
|
{
|
|
CatString(Title, g_TitleExtraText, DIMA(Title));
|
|
CatString(Title, " - ", DIMA(Title));
|
|
}
|
|
}
|
|
|
|
CatString(Title, g_MainTitleText, DIMA(Title));
|
|
|
|
SetWindowText(g_hwndFrame, Title);
|
|
}
|
|
|
|
void
|
|
SetTitleServerText(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
va_start(Args, Format);
|
|
_vsnprintf(g_TitleServerText, sizeof(g_TitleServerText), Format, Args);
|
|
g_TitleServerText[sizeof(g_TitleServerText) - 1] = 0;
|
|
va_end(Args);
|
|
UpdateFrameTitle();
|
|
}
|
|
|
|
void
|
|
SetTitleSessionText(PCSTR Format, ...)
|
|
{
|
|
// Don't override an explicit title.
|
|
if (g_ExplicitTitle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Format == NULL)
|
|
{
|
|
g_TitleExtraText[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
va_list Args;
|
|
va_start(Args, Format);
|
|
_vsnprintf(g_TitleExtraText, sizeof(g_TitleExtraText), Format, Args);
|
|
g_TitleExtraText[sizeof(g_TitleExtraText) - 1] = 0;
|
|
va_end(Args);
|
|
}
|
|
|
|
UpdateFrameTitle();
|
|
}
|
|
|
|
void
|
|
SetTitleExplicitText(PCSTR Text)
|
|
{
|
|
CopyString(g_TitleExtraText, Text, DIMA(g_TitleExtraText));
|
|
g_ExplicitTitle = TRUE;
|
|
UpdateFrameTitle();
|
|
|
|
if (g_ExplicitWorkspace && g_Workspace != NULL)
|
|
{
|
|
g_Workspace->SetString(WSP_WINDOW_FRAME_TITLE, Text);
|
|
}
|
|
}
|
|
|
|
void
|
|
UpdateTitleSessionText(void)
|
|
{
|
|
if (!g_RemoteClient)
|
|
{
|
|
char ProcServer[MAX_CMDLINE_TXT];
|
|
|
|
if (g_ProcessServer != NULL)
|
|
{
|
|
PrintString(ProcServer, DIMA(ProcServer),
|
|
"[%s] ", g_ProcessServer);
|
|
}
|
|
else
|
|
{
|
|
ProcServer[0] = 0;
|
|
}
|
|
|
|
if (g_NumDumpFiles)
|
|
{
|
|
SetTitleSessionText("Dump %s", g_DumpFiles[0]);
|
|
}
|
|
else if (g_DebugCommandLine != NULL)
|
|
{
|
|
SetTitleSessionText("%s%s", ProcServer, g_DebugCommandLine);
|
|
}
|
|
else if (g_PidToDebug != 0)
|
|
{
|
|
SetTitleSessionText("%sPid %d", ProcServer, g_PidToDebug);
|
|
}
|
|
else if (g_ProcNameToDebug != NULL)
|
|
{
|
|
SetTitleSessionText("%sProcess %s", ProcServer, g_ProcNameToDebug);
|
|
}
|
|
else if (g_AttachKernelFlags == DEBUG_ATTACH_LOCAL_KERNEL)
|
|
{
|
|
SetTitleSessionText("Local kernel");
|
|
}
|
|
else if (g_AttachKernelFlags == DEBUG_ATTACH_EXDI_DRIVER)
|
|
{
|
|
SetTitleSessionText("eXDI '%s'",
|
|
g_KernelConnectOptions);
|
|
}
|
|
else
|
|
{
|
|
SetTitleSessionText("Kernel '%s'",
|
|
g_KernelConnectOptions);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetTitleSessionText("Remote '%s'", g_RemoteOptions);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CreateUiInterfaces(
|
|
BOOL Remote,
|
|
LPTSTR CreateOptions
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
|
|
//
|
|
// Destroy the old interfaces if they existed.
|
|
//
|
|
|
|
ReleaseUiInterfaces();
|
|
|
|
//
|
|
// Create the new debugger interfaces the UI will use.
|
|
//
|
|
|
|
if (Remote)
|
|
{
|
|
if ((Status = DebugConnect(CreateOptions, IID_IDebugClient,
|
|
(void **)&g_pUiClient)) != S_OK)
|
|
{
|
|
if (Status == E_INVALIDARG)
|
|
{
|
|
InformationBox(ERR_Invalid_Remote_Param);
|
|
}
|
|
else if (Status == RPC_E_VERSION_MISMATCH)
|
|
{
|
|
InformationBox(ERR_Remoting_Version_Mismatch);
|
|
}
|
|
else if (Status == RPC_E_SERVER_DIED ||
|
|
Status == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
|
|
Status == HRESULT_FROM_WIN32(ERROR_BAD_NETPATH))
|
|
{
|
|
InformationBox(ERR_No_Remote_Server, CreateOptions);
|
|
}
|
|
else
|
|
{
|
|
InformationBox(ERR_Unable_To_Connect, CreateOptions,
|
|
FormatStatusCode(Status), FormatStatus(Status));
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
g_RemoteClient = TRUE;
|
|
g_RemoteOptions = _tcsdup(CreateOptions);
|
|
}
|
|
else
|
|
{
|
|
if ((Status = DebugCreate(IID_IDebugClient,
|
|
(void **)&g_pUiClient)) != S_OK)
|
|
{
|
|
InternalError(Status, "UI DebugCreate");
|
|
return FALSE;
|
|
}
|
|
|
|
if (CreateOptions != NULL &&
|
|
(Status = g_pUiClient->StartServer(CreateOptions)) != S_OK)
|
|
{
|
|
if (Status == E_INVALIDARG)
|
|
{
|
|
InformationBox(ERR_Invalid_Server_Param);
|
|
}
|
|
else if (Status == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) ||
|
|
Status == HRESULT_FROM_WIN32(WSAEADDRINUSE))
|
|
{
|
|
InformationBox(ERR_Connection_In_Use);
|
|
}
|
|
else
|
|
{
|
|
InternalError(Status, "UI StartServer");
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ((Status = g_pUiClient->QueryInterface(IID_IDebugControl,
|
|
(void **)&g_pUiControl)) != S_OK)
|
|
{
|
|
if (Status == RPC_E_VERSION_MISMATCH)
|
|
{
|
|
InformationBox(ERR_Remoting_Version_Mismatch);
|
|
}
|
|
else
|
|
{
|
|
InternalError(Status, "UI QueryControl");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if ((Status = g_pUiClient->QueryInterface(IID_IDebugSymbols,
|
|
(void **)&g_pUiSymbols)) != S_OK)
|
|
{
|
|
if (Status == RPC_E_VERSION_MISMATCH)
|
|
{
|
|
InformationBox(ERR_Remoting_Version_Mismatch);
|
|
}
|
|
else
|
|
{
|
|
InternalError(Status, "UI QuerySymbols");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if ((Status = g_pUiClient->QueryInterface(IID_IDebugSystemObjects,
|
|
(void **)&g_pUiSystem)) != S_OK)
|
|
{
|
|
if (Status == RPC_E_VERSION_MISMATCH)
|
|
{
|
|
InformationBox(ERR_Remoting_Version_Mismatch);
|
|
}
|
|
else
|
|
{
|
|
InternalError(Status, "UI QuerySystem");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Optional interfaces.
|
|
//
|
|
|
|
if ((Status = g_pUiClient->
|
|
QueryInterface(IID_IDebugSymbols2, (void **)&g_pUiSymbols2)) != S_OK)
|
|
{
|
|
g_pUiSymbols2 = NULL;
|
|
}
|
|
|
|
if ((Status = g_pUiClient->
|
|
QueryInterface(IID_IDebugControl3, (void **)&g_pUiControl3)) != S_OK)
|
|
{
|
|
g_pUiControl3 = NULL;
|
|
}
|
|
|
|
if (g_RemoteClient)
|
|
{
|
|
// Create a local client to do local source file lookups.
|
|
if ((Status = DebugCreate(IID_IDebugClient,
|
|
(void **)&g_pUiLocClient)) != S_OK ||
|
|
(Status = g_pUiLocClient->
|
|
QueryInterface(IID_IDebugControl,
|
|
(void **)&g_pUiLocControl)) != S_OK ||
|
|
(Status = g_pUiLocClient->
|
|
QueryInterface(IID_IDebugSymbols,
|
|
(void **)&g_pUiLocSymbols)) != S_OK)
|
|
{
|
|
InternalError(Status, "UI local symbol object");
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_pUiLocClient = g_pUiClient;
|
|
g_pUiLocClient->AddRef();
|
|
g_pUiLocControl = g_pUiControl;
|
|
g_pUiLocControl->AddRef();
|
|
g_pUiLocSymbols = g_pUiSymbols;
|
|
g_pUiLocSymbols->AddRef();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
ReleaseUiInterfaces(void)
|
|
{
|
|
RELEASE(g_pUiClient);
|
|
RELEASE(g_pUiControl);
|
|
RELEASE(g_pUiControl3);
|
|
RELEASE(g_pUiSymbols);
|
|
RELEASE(g_pUiSymbols2);
|
|
RELEASE(g_pUiSystem);
|
|
RELEASE(g_pUiLocClient);
|
|
RELEASE(g_pUiLocControl);
|
|
RELEASE(g_pUiLocSymbols);
|
|
}
|
|
|
|
PTSTR
|
|
GetArg(
|
|
PTSTR *lpp
|
|
)
|
|
{
|
|
static PTSTR pszBuffer = NULL;
|
|
int r;
|
|
PTSTR p1 = *lpp;
|
|
|
|
while (*p1 == _T(' ') || *p1 == _T('\t'))
|
|
{
|
|
p1++;
|
|
}
|
|
|
|
if (pszBuffer)
|
|
{
|
|
free(pszBuffer);
|
|
}
|
|
pszBuffer = (PTSTR) calloc(_tcslen(p1) + 1, sizeof(TCHAR));
|
|
if (pszBuffer == NULL)
|
|
{
|
|
ErrorExit(NULL, "Unable to allocate command line argument\n");
|
|
}
|
|
|
|
r = CPCopyString(&p1, pszBuffer, 0, (*p1 == _T('\'') || *p1 == _T('"') ));
|
|
if (r >= 0)
|
|
{
|
|
*lpp = p1;
|
|
}
|
|
return pszBuffer;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ParseCommandLine(BOOL FirstParse)
|
|
{
|
|
PTSTR lp1 = GetCommandLine();
|
|
PTSTR lp2 = NULL;
|
|
int Starts;
|
|
HRESULT Status;
|
|
|
|
g_CommandLineStart = 0;
|
|
g_EngOptModified = 0;
|
|
|
|
// skip whitespace
|
|
while (*lp1 == _T(' ') || *lp1 == _T('\t'))
|
|
{
|
|
lp1++;
|
|
}
|
|
|
|
// skip over our program name
|
|
if (_T('"') != *lp1)
|
|
{
|
|
lp1 += _tcslen(g_ProgramName);
|
|
}
|
|
else
|
|
{
|
|
// The program name is quoted. This is a tough thing
|
|
// to parse through as the system allows a lot of weird
|
|
// quote placement and no trailing quote is required.
|
|
// Once we skip ahead by the program name we continue
|
|
// to whitespace on the theory that the program
|
|
// name and the first argument must be separated by whitespace
|
|
// at some point.
|
|
lp1 += _tcslen(g_ProgramName) + 1;
|
|
while (*lp1 && *lp1 != _T(' ') && *lp1 != _T('\t'))
|
|
{
|
|
lp1++;
|
|
}
|
|
}
|
|
|
|
while (*lp1)
|
|
{
|
|
if (*lp1 == _T(' ') || *lp1 == _T('\t'))
|
|
{
|
|
lp1++;
|
|
continue;
|
|
}
|
|
|
|
if (*lp1 == _T('-') || *lp1 == _T('/'))
|
|
{
|
|
++lp1;
|
|
|
|
switch (*lp1++)
|
|
{
|
|
case _T('?'):
|
|
usage:
|
|
SpawnHelp(HELP_TOPIC_COMMAND_LINE_WINDBG);
|
|
exit(1);
|
|
|
|
case _T(' '):
|
|
case _T('\t'):
|
|
break;
|
|
|
|
case 'a':
|
|
ULONG64 ExtHandle;
|
|
|
|
lp2 = GetArg(&lp1);
|
|
g_pUiControl->AddExtension(lp2, DEBUG_EXTENSION_AT_ENGINE,
|
|
&ExtHandle);
|
|
break;
|
|
|
|
case _T('b'):
|
|
g_pUiControl->AddEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK);
|
|
if (g_RemoteClient)
|
|
{
|
|
// The engine may already be waiting so just ask
|
|
// for a breakin immediately.
|
|
g_pUiControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE);
|
|
}
|
|
g_EngOptModified |= DEBUG_ENGOPT_INITIAL_BREAK;
|
|
break;
|
|
|
|
case _T('c'):
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 == lp1 + 5 &&
|
|
!memcmp(lp1, "lines", 5))
|
|
{
|
|
lp1 = lp2;
|
|
g_HistoryLines = atoi(GetArg(&lp1));
|
|
}
|
|
else
|
|
{
|
|
g_InitialCommand = _tcsdup(GetArg(&lp1));
|
|
}
|
|
break;
|
|
|
|
case _T('d'):
|
|
g_pUiControl->
|
|
AddEngineOptions(DEBUG_ENGOPT_INITIAL_MODULE_BREAK);
|
|
g_EngOptModified |= DEBUG_ENGOPT_INITIAL_MODULE_BREAK;
|
|
break;
|
|
|
|
case _T('e'):
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 == lp1 + 1 &&
|
|
*lp1 == _T('e'))
|
|
{
|
|
if (!g_pUiControl3)
|
|
{
|
|
goto usage;
|
|
}
|
|
|
|
lp1 = lp2;
|
|
if (g_pUiControl3->
|
|
SetExpressionSyntaxByName(GetArg(&lp1)) != S_OK)
|
|
{
|
|
goto usage;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Signal an event after process is attached.
|
|
g_pUiControl->SetNotifyEventHandle(_atoi64(GetArg(&lp1)));
|
|
}
|
|
break;
|
|
|
|
case _T('f'):
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 != lp1 + 6 ||
|
|
memcmp(lp1, "ailinc", 6))
|
|
{
|
|
goto usage;
|
|
}
|
|
|
|
lp1 = lp2;
|
|
g_pUiControl->
|
|
AddEngineOptions(DEBUG_ENGOPT_FAIL_INCOMPLETE_INFORMATION);
|
|
g_pUiSymbols->
|
|
AddSymbolOptions(SYMOPT_EXACT_SYMBOLS);
|
|
break;
|
|
|
|
case _T('g'):
|
|
g_pUiControl->
|
|
RemoveEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK);
|
|
g_EngOptModified |= DEBUG_ENGOPT_INITIAL_BREAK;
|
|
break;
|
|
|
|
case _T('G'):
|
|
g_pUiControl->
|
|
RemoveEngineOptions(DEBUG_ENGOPT_FINAL_BREAK);
|
|
g_EngOptModified |= DEBUG_ENGOPT_FINAL_BREAK;
|
|
break;
|
|
|
|
case _T('h'):
|
|
if (*lp1 == _T('d'))
|
|
{
|
|
lp1++;
|
|
g_DebugCreateFlags |=
|
|
DEBUG_CREATE_PROCESS_NO_DEBUG_HEAP;
|
|
}
|
|
else
|
|
{
|
|
goto usage;
|
|
}
|
|
break;
|
|
|
|
case _T('i'):
|
|
g_pUiSymbols->SetImagePath(GetArg(&lp1));
|
|
break;
|
|
|
|
case _T('I'):
|
|
if (!InstallAsAeDebug(NULL))
|
|
{
|
|
InformationBox(ERR_Fail_Inst_Postmortem_Dbg);
|
|
}
|
|
else
|
|
{
|
|
InformationBox(ERR_Success_Inst_Postmortem_Dbg);
|
|
exit(1);
|
|
}
|
|
break;
|
|
|
|
// XXX AndreVa - This needs to be checked before we start
|
|
// the GUI.
|
|
case _T('J'):
|
|
case _T('j'):
|
|
g_AllowJournaling = TRUE;
|
|
break;
|
|
|
|
case _T('k'):
|
|
if (*lp1 == _T('l'))
|
|
{
|
|
g_AttachKernelFlags = DEBUG_ATTACH_LOCAL_KERNEL;
|
|
lp1++;
|
|
}
|
|
else if (*lp1 == _T('x'))
|
|
{
|
|
g_AttachKernelFlags = DEBUG_ATTACH_EXDI_DRIVER;
|
|
lp1++;
|
|
g_KernelConnectOptions = _tcsdup(GetArg(&lp1));
|
|
}
|
|
else
|
|
{
|
|
g_KernelConnectOptions = _tcsdup(GetArg(&lp1));
|
|
}
|
|
g_CommandLineStart++;
|
|
break;
|
|
|
|
case _T('l'):
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 == lp1 + 3 &&
|
|
(!memcmp(lp1, "oga", 3) ||
|
|
!memcmp(lp1, "ogo", 3)))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiControl->OpenLogFile(GetArg(&lp1), *(lp2 - 1) == 'a');
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
goto usage;
|
|
}
|
|
break;
|
|
|
|
case _T('n'):
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 == lp1 + 6 &&
|
|
!memcmp(lp1, "oshell", 6))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiControl->AddEngineOptions
|
|
(DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
g_pUiSymbols->AddSymbolOptions(SYMOPT_DEBUG);
|
|
}
|
|
break;
|
|
|
|
case _T('o'):
|
|
if (g_RemoteClient)
|
|
{
|
|
goto usage;
|
|
}
|
|
|
|
g_DebugCreateFlags |= DEBUG_PROCESS;
|
|
g_DebugCreateFlags &= ~DEBUG_ONLY_THIS_PROCESS;
|
|
break;
|
|
|
|
case _T('p'):
|
|
// attach to an active process
|
|
// p specifies a process id
|
|
// pn specifies a process by name
|
|
// ie: -p 360
|
|
// -pn _T("foo bar")
|
|
|
|
if (!isspace(*lp1) && !isdigit(*lp1))
|
|
{
|
|
// They may have specified a -p flag with
|
|
// a tail such as -premote.
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 == lp1 + 6 &&
|
|
!memcmp(lp1, "remote", 6))
|
|
{
|
|
lp1 = lp2;
|
|
g_ProcessServer = _tcsdup(GetArg(&lp1));
|
|
break;
|
|
}
|
|
else if (_T('b') == *lp1)
|
|
{
|
|
lp1++;
|
|
g_AttachProcessFlags |=
|
|
DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK;
|
|
break;
|
|
}
|
|
else if (_T('d') == *lp1)
|
|
{
|
|
lp1++;
|
|
g_DetachOnExit = TRUE;
|
|
break;
|
|
}
|
|
else if (_T('e') == *lp1)
|
|
{
|
|
lp1++;
|
|
g_AttachProcessFlags = DEBUG_ATTACH_EXISTING;
|
|
break;
|
|
}
|
|
else if (_T('r') == *lp1)
|
|
{
|
|
lp1++;
|
|
g_AttachProcessFlags |=
|
|
DEBUG_ATTACH_INVASIVE_RESUME_PROCESS;
|
|
break;
|
|
}
|
|
else if (_T('t') == *lp1)
|
|
{
|
|
lp1++;
|
|
g_pUiControl->
|
|
SetInterruptTimeout(atoi(GetArg(&lp1)));
|
|
break;
|
|
}
|
|
else if (_T('v') == *lp1)
|
|
{
|
|
lp1++;
|
|
g_AttachProcessFlags = DEBUG_ATTACH_NONINVASIVE;
|
|
if (_T('r') == *lp1)
|
|
{
|
|
lp1++;
|
|
g_AttachProcessFlags |=
|
|
DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND;
|
|
}
|
|
break;
|
|
}
|
|
else if (_T('n') != *lp1)
|
|
{
|
|
goto usage;
|
|
}
|
|
else
|
|
{
|
|
// Skip the _T('n')
|
|
lp1++;
|
|
g_ProcNameToDebug = _tcsdup(GetArg(&lp1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// They specified -p 360
|
|
g_PidToDebug = strtoul(GetArg(&lp1), NULL, 0);
|
|
|
|
if (g_PidToDebug <= 0)
|
|
{
|
|
g_PidToDebug = -2;
|
|
ErrorBox(NULL, 0, ERR_Invalid_Process_Id,
|
|
g_PidToDebug);
|
|
}
|
|
}
|
|
g_CommandLineStart++;
|
|
break;
|
|
|
|
case _T('Q'):
|
|
if (*lp1 == 'S')
|
|
{
|
|
lp1++;
|
|
if (*lp1 == 'Y')
|
|
{
|
|
lp1++;
|
|
g_QuietSourceMode = QMODE_ALWAYS_YES;
|
|
}
|
|
else if (!*lp1 || isspace(*lp1))
|
|
{
|
|
g_QuietSourceMode = QMODE_ALWAYS_NO;
|
|
}
|
|
else
|
|
{
|
|
goto usage;
|
|
}
|
|
}
|
|
else if (*lp1 == 'Y')
|
|
{
|
|
lp1++;
|
|
g_QuietMode = QMODE_ALWAYS_YES;
|
|
}
|
|
else if (!*lp1 || isspace(*lp1))
|
|
{
|
|
g_QuietMode = QMODE_ALWAYS_NO;
|
|
}
|
|
else
|
|
{
|
|
goto usage;
|
|
}
|
|
break;
|
|
|
|
case _T('r'):
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 == lp1 + 3 &&
|
|
!memcmp(lp1, "obp", 3))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiControl->AddEngineOptions
|
|
(DEBUG_ENGOPT_ALLOW_READ_ONLY_BREAKPOINTS);
|
|
break;
|
|
}
|
|
else if (lp2 != lp1 + 5 ||
|
|
memcmp(lp1, "emote", 5))
|
|
{
|
|
goto usage;
|
|
}
|
|
|
|
lp1 = lp2;
|
|
lp2 = GetArg(&lp1);
|
|
if (!CreateUiInterfaces(TRUE, lp2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
g_CommandLineStart++;
|
|
break;
|
|
|
|
case _T('s'):
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 == lp1 + 5 &&
|
|
!memcmp(lp1, "erver", 5))
|
|
{
|
|
lp1 = lp2;
|
|
lp2 = GetArg(&lp1);
|
|
if (!CreateUiInterfaces(FALSE, lp2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SetTitleServerText("Server '%s'", lp2);
|
|
}
|
|
else if (lp2 == lp1 + 3 &&
|
|
!memcmp(lp1, "dce", 3))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->
|
|
RemoveSymbolOptions(SYMOPT_FAIL_CRITICAL_ERRORS);
|
|
}
|
|
else if (lp2 == lp1 + 5 &&
|
|
!memcmp(lp1, "ecure", 5))
|
|
{
|
|
lp1 = lp2;
|
|
if (g_pUiSymbols->
|
|
AddSymbolOptions(SYMOPT_SECURE) != S_OK)
|
|
{
|
|
ErrorBox(NULL, 0, ERR_Cant_Secure);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (lp2 == lp1 + 2 &&
|
|
!memcmp(lp1, "es", 2))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->
|
|
AddSymbolOptions(SYMOPT_EXACT_SYMBOLS);
|
|
}
|
|
else if (lp2 == lp1 + 3 &&
|
|
!memcmp(lp1, "fce", 3))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->
|
|
AddSymbolOptions(SYMOPT_FAIL_CRITICAL_ERRORS);
|
|
}
|
|
else if (lp2 == lp1 + 5 &&
|
|
!memcmp(lp1, "flags", 5))
|
|
{
|
|
lp1 = lp2;
|
|
lp2 = GetArg(&lp1);
|
|
g_pUiSymbols->
|
|
SetSymbolOptions(strtoul(lp2, NULL, 0));
|
|
}
|
|
else if (lp2 == lp1 + 3 &&
|
|
!memcmp(lp1, "icv", 3))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->AddSymbolOptions(SYMOPT_IGNORE_CVREC);
|
|
}
|
|
else if (lp2 == lp1 + 3 &&
|
|
!memcmp(lp1, "ins", 3))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->AddSymbolOptions(SYMOPT_IGNORE_NT_SYMPATH);
|
|
}
|
|
else if (lp2 == lp1 + 2 &&
|
|
!memcmp(lp1, "nc", 2))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->
|
|
AddSymbolOptions(SYMOPT_NO_CPP);
|
|
}
|
|
else if (lp2 == lp1 + 3 &&
|
|
!memcmp(lp1, "nul", 3))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->
|
|
AddSymbolOptions(SYMOPT_NO_UNQUALIFIED_LOADS);
|
|
}
|
|
else if (lp2 == lp1 + 2 &&
|
|
!memcmp(lp1, "up", 2))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->
|
|
RemoveSymbolOptions(SYMOPT_AUTO_PUBLICS |
|
|
SYMOPT_NO_PUBLICS);
|
|
}
|
|
else if (lp2 == lp1 + 6 &&
|
|
!memcmp(lp1, "rcpath", 6))
|
|
{
|
|
lp1 = lp2;
|
|
g_pUiSymbols->SetSourcePath(GetArg(&lp1));
|
|
}
|
|
else
|
|
{
|
|
goto usage;
|
|
}
|
|
break;
|
|
|
|
case _T('T'):
|
|
lp2 = GetArg(&lp1);
|
|
SetTitleExplicitText(lp2);
|
|
break;
|
|
|
|
case _T('v'):
|
|
g_Verbose = TRUE;
|
|
break;
|
|
|
|
case _T('w'):
|
|
lp2 = lp1;
|
|
while (*lp2 && *lp2 != ' ' && *lp2 != '\t')
|
|
{
|
|
lp2++;
|
|
}
|
|
if (lp2 == lp1 + 3 &&
|
|
!memcmp(lp1, "ake", 3))
|
|
{
|
|
ULONG Pid;
|
|
|
|
lp1 = lp2;
|
|
Pid = strtoul(GetArg(&lp1), NULL, 0);
|
|
if (!SetPidEvent(Pid, OPEN_EXISTING))
|
|
{
|
|
InformationBox(ERR_Wake_Failed, Pid);
|
|
ErrorExit(NULL,
|
|
"Process %d is not a sleeping debugger\n",
|
|
Pid);
|
|
}
|
|
else
|
|
{
|
|
ExitDebugger(NULL, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _T('W'):
|
|
if (*lp1 == _T('F'))
|
|
{
|
|
lp1++;
|
|
lp2 = GetArg(&lp1);
|
|
if ((Status =
|
|
UiSwitchWorkspace(WSP_NAME_FILE, lp2,
|
|
WSP_CREATE_QUERY,
|
|
WSP_APPLY_EXPLICIT,
|
|
&Starts)) != S_OK)
|
|
{
|
|
InformationBox(ERR_Cant_Open_Workspace,
|
|
FormatStatusCode(Status),
|
|
FormatStatus(Status));
|
|
ExitDebugger(NULL, 0);
|
|
}
|
|
|
|
g_CommandLineStart += Starts;
|
|
}
|
|
else if (*lp1 != _T('X'))
|
|
{
|
|
lp2 = GetArg(&lp1);
|
|
if ((Status =
|
|
UiSwitchWorkspace(WSP_NAME_EXPLICIT, lp2,
|
|
WSP_CREATE_QUERY,
|
|
WSP_APPLY_EXPLICIT,
|
|
&Starts)) != S_OK)
|
|
{
|
|
InformationBox(ERR_Cant_Open_Workspace,
|
|
FormatStatusCode(Status),
|
|
FormatStatus(Status));
|
|
ExitDebugger(NULL, 0);
|
|
}
|
|
|
|
g_CommandLineStart += Starts;
|
|
}
|
|
else
|
|
{
|
|
// Skip X.
|
|
lp1++;
|
|
if (g_Workspace != NULL)
|
|
{
|
|
g_Workspace->Flush(FALSE, FALSE);
|
|
delete g_Workspace;
|
|
}
|
|
g_Workspace = NULL;
|
|
}
|
|
g_ExplicitWorkspace = TRUE;
|
|
break;
|
|
|
|
case _T('y'):
|
|
g_pUiSymbols->SetSymbolPath(GetArg(&lp1));
|
|
break;
|
|
|
|
case _T('z'):
|
|
if (*lp1 == _T('p'))
|
|
{
|
|
lp1++;
|
|
lp2 = GetArg(&lp1);
|
|
if (g_NumDumpInfoFiles == MAX_DUMP_FILES)
|
|
{
|
|
InformationBox(ERR_Too_Many_Dump_Files, lp2);
|
|
}
|
|
else
|
|
{
|
|
g_DumpInfoFiles[g_NumDumpInfoFiles] = _tcsdup(lp2);
|
|
g_DumpInfoTypes[g_NumDumpInfoFiles] =
|
|
DEBUG_DUMP_FILE_PAGE_FILE_DUMP;
|
|
g_NumDumpInfoFiles++;
|
|
}
|
|
}
|
|
else if (*lp1 && *lp1 != _T(' ') && *lp1 != _T('\t'))
|
|
{
|
|
goto usage;
|
|
}
|
|
else
|
|
{
|
|
lp2 = GetArg(&lp1);
|
|
if (g_NumDumpFiles == MAX_DUMP_FILES)
|
|
{
|
|
InformationBox(ERR_Too_Many_Dump_Files, lp2);
|
|
}
|
|
else
|
|
{
|
|
g_DumpFiles[g_NumDumpFiles] = _tcsdup(lp2);
|
|
if (++g_NumDumpFiles == 1)
|
|
{
|
|
g_CommandLineStart++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
--lp1;
|
|
goto usage;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pick up file args. If it is a program name,
|
|
// keep the tail of the cmd line intact.
|
|
g_DebugCommandLine = _tcsdup(lp1);
|
|
g_CommandLineStart++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a command line start option was set, we can just start the engine
|
|
// right away. Otherwise, we have to wait for user input.
|
|
//
|
|
// If multiple command line option were set, print an error.
|
|
//
|
|
|
|
if (g_CommandLineStart == 1)
|
|
{
|
|
PostMessage(g_hwndFrame, WU_START_ENGINE, 0, 0);
|
|
}
|
|
else if (g_CommandLineStart > 1)
|
|
{
|
|
ErrorBox(NULL, 0, ERR_Invalid_Command_Line);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
StopDebugging(BOOL UserRequest)
|
|
{
|
|
// Flush the current workspace first so
|
|
// the engine thread doesn't.
|
|
if (g_Workspace != NULL &&
|
|
g_Workspace->Flush(FALSE, FALSE) == S_FALSE)
|
|
{
|
|
// User cancelled things so don't terminate. We
|
|
// don't offer that option right now so this
|
|
// should never happen.
|
|
return;
|
|
}
|
|
|
|
if (g_EngineThreadId)
|
|
{
|
|
DWORD WaitStatus;
|
|
|
|
if (UserRequest)
|
|
{
|
|
if (!g_RemoteClient)
|
|
{
|
|
// Try to get the current engine operation stopped.
|
|
g_pUiControl->SetInterrupt(DEBUG_INTERRUPT_EXIT);
|
|
}
|
|
|
|
// If this stop is coming from the UI thread
|
|
// clean up the current session.
|
|
AddEnumCommand(UIC_END_SESSION);
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
// Wait for the engine thread to finish.
|
|
WaitStatus = WaitForSingleObject(g_EngineThread, 30000);
|
|
if (WaitStatus != WAIT_TIMEOUT)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Engine is still busy. If the user requested
|
|
// the stop, ask the user whether they want to keep
|
|
// waiting. If they don't they'll have to exit
|
|
// windbg as the engine must be available in
|
|
// order to restart anything. If this is a stop
|
|
// from the engine thread itself it should have
|
|
// finished up by now, so something is wrong.
|
|
// For now give the user the same option but
|
|
// in the future we might want to have special
|
|
// behavior.
|
|
if (QuestionBox(STR_Engine_Still_Busy, MB_YESNO) == IDNO)
|
|
{
|
|
ExitDebugger(g_pUiClient, 0);
|
|
}
|
|
|
|
if (UserRequest)
|
|
{
|
|
// Try again to get the engine to stop.
|
|
g_pUiControl->SetInterrupt(DEBUG_INTERRUPT_EXIT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_EngineThread != NULL)
|
|
{
|
|
CloseHandle(g_EngineThread);
|
|
g_EngineThread = NULL;
|
|
}
|
|
|
|
CloseAllWindows(ALL_WINDOWS);
|
|
if (!CreateUiInterfaces(FALSE, NULL))
|
|
{
|
|
InternalError(E_OUTOFMEMORY, "CreateUiInterfaces");
|
|
ErrorExit(NULL, "Unable to recreate UI interfaces\n");
|
|
}
|
|
|
|
ClearDebuggingParams();
|
|
SetTitleSessionText(NULL);
|
|
|
|
// Any changes caused by shutting things down
|
|
// are not user changes and can be ignored.
|
|
if (g_Workspace != NULL)
|
|
{
|
|
g_Workspace->ClearDirty();
|
|
}
|
|
if (!g_ExplicitWorkspace)
|
|
{
|
|
UiSwitchWorkspace(WSP_NAME_BASE, g_WorkspaceDefaultName,
|
|
WSP_CREATE_ALWAYS, WSP_APPLY_DEFAULT, NULL);
|
|
}
|
|
|
|
SetLineColumn_StatusBar(0, 0);
|
|
SetSysPidTid_StatusBar(0, "<None>", 0, 0, 0, 0);
|
|
EnableToolbarControls();
|
|
}
|
|
|
|
void
|
|
ClearDebuggingParams(void)
|
|
{
|
|
ULONG i;
|
|
|
|
//
|
|
// Reset all session starting values.
|
|
//
|
|
// Do not clear the process server value here
|
|
// as the UI doesn't offer any way to set it
|
|
// so just let the command line setting persist
|
|
// for the entire run of the process.
|
|
//
|
|
|
|
g_AttachKernelFlags = 0;
|
|
free(g_KernelConnectOptions);
|
|
g_KernelConnectOptions = NULL;
|
|
g_PidToDebug = 0;
|
|
free(g_ProcNameToDebug);
|
|
g_ProcNameToDebug = NULL;
|
|
for (i = 0; i < g_NumDumpFiles; i++)
|
|
{
|
|
free(g_DumpFiles[i]);
|
|
g_DumpFiles[i] = NULL;
|
|
}
|
|
g_NumDumpFiles = 0;
|
|
for (i = 0; i < g_NumDumpInfoFiles; i++)
|
|
{
|
|
free(g_DumpInfoFiles[i]);
|
|
g_DumpInfoFiles[i] = NULL;
|
|
}
|
|
g_NumDumpInfoFiles = 0;
|
|
free(g_DebugCommandLine);
|
|
g_DebugCommandLine = NULL;
|
|
free(g_ProcessStartDir);
|
|
g_ProcessStartDir = NULL;
|
|
g_DebugCreateFlags = DEBUG_ONLY_THIS_PROCESS;
|
|
g_RemoteClient = FALSE;
|
|
free(g_RemoteOptions);
|
|
g_RemoteOptions = NULL;
|
|
g_DetachOnExit = FALSE;
|
|
g_AttachProcessFlags = DEBUG_ATTACH_DEFAULT;
|
|
}
|
|
|
|
|
|
void
|
|
InitDefaults(
|
|
void
|
|
)
|
|
{
|
|
SetSrcMode_StatusBar(TRUE);
|
|
}
|
|
|
|
|
|
INDEXED_COLOR*
|
|
GetIndexedColor(ULONG Index)
|
|
{
|
|
if (Index < OUT_MASK_COL_BASE)
|
|
{
|
|
if (Index >= COL_COUNT)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return g_Colors + Index;
|
|
}
|
|
else
|
|
{
|
|
Index -= OUT_MASK_COL_BASE;
|
|
if (Index >= OUT_MASK_COL_COUNT ||
|
|
g_OutMaskColors[Index].Name == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return g_OutMaskColors + Index;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
SetColor(ULONG Index, COLORREF Color)
|
|
{
|
|
INDEXED_COLOR* IdxCol = GetIndexedColor(Index);
|
|
if (IdxCol == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (IdxCol->Brush != NULL)
|
|
{
|
|
DeleteObject(IdxCol->Brush);
|
|
}
|
|
|
|
IdxCol->Color = Color;
|
|
IdxCol->Brush = CreateSolidBrush(IdxCol->Color);
|
|
|
|
// A UI color selection changing means the UI needs to refresh.
|
|
// Out mask color changes only apply to new text and do
|
|
// not need a refresh.
|
|
return Index < COL_COUNT ? TRUE : FALSE;
|
|
}
|
|
|
|
BOOL
|
|
GetOutMaskColors(ULONG Mask, COLORREF* Fg, COLORREF* Bg)
|
|
{
|
|
if (Mask == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG Idx = 0;
|
|
|
|
while ((Mask & 1) == 0)
|
|
{
|
|
Idx++;
|
|
Mask >>= 1;
|
|
}
|
|
|
|
Idx *= 2;
|
|
if (g_OutMaskColors[Idx].Name == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*Fg = g_OutMaskColors[Idx].Color;
|
|
*Bg = g_OutMaskColors[Idx + 1].Color;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
InitColors(void)
|
|
{
|
|
g_Colors[COL_PLAIN].Default =
|
|
GetSysColor(COLOR_WINDOW);
|
|
g_Colors[COL_PLAIN_TEXT].Default =
|
|
GetSysColor(COLOR_WINDOWTEXT);
|
|
g_Colors[COL_CURRENT_LINE].Default =
|
|
GetSysColor(COLOR_HIGHLIGHT);
|
|
g_Colors[COL_CURRENT_LINE_TEXT].Default =
|
|
GetSysColor(COLOR_HIGHLIGHTTEXT);
|
|
g_Colors[COL_BP_CURRENT_LINE_TEXT].Default =
|
|
GetSysColor(COLOR_HIGHLIGHTTEXT);
|
|
g_Colors[COL_ENABLED_BP_TEXT].Default =
|
|
GetSysColor(COLOR_HIGHLIGHTTEXT);
|
|
g_Colors[COL_DISABLED_BP_TEXT].Default =
|
|
GetSysColor(COLOR_HIGHLIGHTTEXT);
|
|
g_Colors[COL_DISABLED_WINDOW].Default =
|
|
GetSysColor(COLOR_3DFACE);
|
|
|
|
ULONG i;
|
|
|
|
for (i = 0; i < COL_COUNT; i++)
|
|
{
|
|
SetColor(i, g_Colors[i].Default);
|
|
}
|
|
for (i = 0; i < OUT_MASK_COL_COUNT; i++)
|
|
{
|
|
if (g_OutMaskColors[i].Name != NULL)
|
|
{
|
|
g_OutMaskColors[i].Default =
|
|
GetSysColor((i & 1) ? COLOR_WINDOW : COLOR_WINDOWTEXT);
|
|
SetColor(i + OUT_MASK_COL_BASE, g_OutMaskColors[i].Default);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < CUSTCOL_COUNT; i++)
|
|
{
|
|
g_CustomColors[i] = GetSysColor(i + 1);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
InitGUI(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the GUI components of WinDBG so we can bring up
|
|
the parent MDI window with the top level menus.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE if everything is OK, FALSE if something fails
|
|
|
|
--*/
|
|
{
|
|
WNDCLASSEX wcex = {0};
|
|
TCHAR szClassName[MAX_MSG_TXT];
|
|
INITCOMMONCONTROLSEX InitCtrls =
|
|
{
|
|
sizeof(InitCtrls), ICC_WIN95_CLASSES | ICC_COOL_CLASSES |
|
|
ICC_USEREX_CLASSES
|
|
};
|
|
|
|
|
|
// Journaling is a feature that applications, such as Visual Test, can
|
|
// enable to synchronize all message queues.
|
|
// In order to allow WinDBG to debug an app such as Visual Test, we
|
|
// provide the option to disable journaling, which ensures WinDBG
|
|
// has its own message queue at all times.
|
|
//
|
|
// Should journaling be allowed or disabled?
|
|
//
|
|
if (g_AllowJournaling == FALSE)
|
|
{
|
|
#define RST_DONTJOURNALATTACH 0x00000002
|
|
typedef VOID (WINAPI * RST)(DWORD,DWORD);
|
|
|
|
RST Rst = (RST) GetProcAddress( GetModuleHandle( _T("user32.dll") ),
|
|
"RegisterSystemThread" );
|
|
if (Rst)
|
|
{
|
|
(Rst) (RST_DONTJOURNALATTACH, 0);
|
|
}
|
|
}
|
|
|
|
// Load the richedit 2.0 dll so that it can register the window class.
|
|
// We require RichEdit 2 and cannot use RichEdit 1.
|
|
// Since we intentionally need this library the entire duration, we
|
|
// simply load it and lose the handle to it. We are in win32 and running
|
|
// separate address spaces, and don't have to worry about freeing the
|
|
// library.
|
|
if (!LoadLibrary(_T("RICHED20.DLL")))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !InitCommonControlsEx( &InitCtrls ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//We use tmp strings as edit buffers
|
|
Assert(MAX_LINE_SIZE < TMP_STRING_SIZE);
|
|
|
|
|
|
Dbg(LoadString(g_hInst, SYS_Main_wTitle,
|
|
g_MainTitleText, _tsizeof(g_MainTitleText)));
|
|
Dbg(LoadString(g_hInst, SYS_Main_wClass,
|
|
szClassName, _tsizeof(szClassName) ));
|
|
|
|
//Register the main window szClassName
|
|
|
|
wcex.cbSize = sizeof(wcex);
|
|
wcex.style = 0;
|
|
wcex.lpfnWndProc = MainWndProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = g_hInst;
|
|
wcex.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(WINDBGICON) );
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = (HBRUSH) (COLOR_ACTIVEBORDER + 1);
|
|
wcex.lpszMenuName = MAKEINTRESOURCE(MAIN_MENU);
|
|
wcex.lpszClassName = szClassName;
|
|
wcex.hIconSm = LoadIcon(g_hInst, MAKEINTRESOURCE(WINDBGICON) );
|
|
|
|
if (!RegisterClassEx (&wcex) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Generic MDI child window. Channels all processing
|
|
// through the COMMONWIN abstraction.
|
|
//
|
|
Dbg(LoadString(g_hInst, SYS_CommonWin_wClass,
|
|
szClassName, _tsizeof(szClassName)));
|
|
|
|
wcex.cbSize = sizeof(wcex);
|
|
wcex.style = 0;
|
|
wcex.lpfnWndProc = COMMONWIN_DATA::WindowProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = g_hInst;
|
|
wcex.hIcon = NULL;
|
|
// The cursor is set to SIZENS so that the proper
|
|
// cursor appears in the command window splitter area.
|
|
// All other areas are covered by child windows with
|
|
// their own cursors.
|
|
wcex.hCursor = LoadCursor(NULL, IDC_SIZENS);
|
|
wcex.hbrBackground = (HBRUSH) (COLOR_ACTIVEBORDER + 1);
|
|
wcex.lpszMenuName = NULL;
|
|
wcex.lpszClassName = szClassName;
|
|
wcex.hIconSm = NULL;
|
|
|
|
if (!RegisterClassEx(&wcex))
|
|
{
|
|
return FALSE ;
|
|
}
|
|
|
|
HDC Dc = GetDC(NULL);
|
|
if (Dc == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
g_Fonts[FONT_FIXED].Font = (HFONT)GetStockObject(ANSI_FIXED_FONT);
|
|
g_Fonts[FONT_VARIABLE].Font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
|
|
|
for (ULONG FontIndex = 0; FontIndex < FONT_COUNT; FontIndex++)
|
|
{
|
|
SelectObject(Dc, g_Fonts[FontIndex].Font);
|
|
if (!GetTextMetrics(Dc, &g_Fonts[FontIndex].Metrics))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ReleaseDC(NULL, Dc);
|
|
|
|
InitColors();
|
|
|
|
// Register message for FINDMSGSTRING.
|
|
g_FindMsgString = RegisterWindowMessage(FINDMSGSTRING);
|
|
|
|
// Look up FindWindowEx.
|
|
HMODULE User32 = GetModuleHandle("user32.dll");
|
|
if (User32 != NULL)
|
|
{
|
|
g_FlashWindowEx = (PFN_FlashWindowEx)
|
|
GetProcAddress(User32, "FlashWindowEx");
|
|
}
|
|
|
|
//
|
|
// Initialize window lists
|
|
//
|
|
InitializeListHead(&g_ActiveWin);
|
|
|
|
Dbg(g_hMainAccTable = LoadAccelerators(g_hInst, MAKEINTRESOURCE(MAIN_ACC)));
|
|
Dbg(LoadString(g_hInst, SYS_Main_wClass, szClassName, MAX_MSG_TXT));
|
|
|
|
InitializeListHead(&g_StateList);
|
|
|
|
__try
|
|
{
|
|
Dbg_InitializeCriticalSection( &g_QuickLock );
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RECT WorkRect;
|
|
RECT FrameRect;
|
|
|
|
//
|
|
// Try and create an initial window that's ready to work
|
|
// without resizing. Our goal here is to grab enough
|
|
// screen space to given plenty of room for MDI windows
|
|
// but not so much we might as well be maximized.
|
|
//
|
|
|
|
Dbg(SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkRect, FALSE));
|
|
|
|
// We don't want to take up more than 80% of either dimension.
|
|
FrameRect.right = (WorkRect.right - WorkRect.left) * 4 / 5;
|
|
FrameRect.bottom = (WorkRect.bottom - WorkRect.top) * 4 / 5;
|
|
|
|
// We want width for an 80-character window plus space for
|
|
// another narrow window like the CPU window. We want
|
|
// height for a forty row window plus space for a short
|
|
// window like the stack.
|
|
// If we can't get that much room just let the system
|
|
// take charge.
|
|
if (FrameRect.right < (CMD_WIDTH + CPU_WIDTH_32) ||
|
|
FrameRect.bottom < (CMD_HEIGHT + CALLS_HEIGHT))
|
|
{
|
|
SetRect(&FrameRect, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT);
|
|
}
|
|
else
|
|
{
|
|
// Hug the bottom left corner of the screen to
|
|
// try and keep out of the way as much as possible
|
|
// while still allowing the first bits of the
|
|
// window to be seen.
|
|
FrameRect.left = WorkRect.left;
|
|
FrameRect.top = (WorkRect.bottom - WorkRect.top) - FrameRect.bottom;
|
|
}
|
|
|
|
//
|
|
// Create the frame
|
|
//
|
|
g_hwndFrame = CreateWindow(szClassName,
|
|
g_MainTitleText,
|
|
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN
|
|
| WS_VISIBLE,
|
|
FrameRect.left,
|
|
FrameRect.top,
|
|
FrameRect.right,
|
|
FrameRect.bottom,
|
|
NULL,
|
|
NULL,
|
|
g_hInst,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Initialize the debugger
|
|
//
|
|
if ( !g_hwndFrame || !g_hwndMDIClient )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get handle to main menu, window submenu, MRU submenu
|
|
//
|
|
Dbg( g_hmenuMain = GetMenu(g_hwndFrame) );
|
|
g_hmenuMainSave = g_hmenuMain;
|
|
|
|
Dbg( g_hmenuWindowSub = GetSubMenu(g_hmenuMain, WINDOWMENU) );
|
|
|
|
Dbg( g_MruMenu = GetSubMenu(g_hmenuMain, FILEMENU) );
|
|
Dbg( g_MruMenu = GetSubMenu(g_MruMenu,
|
|
IDM_FILE_MRU_FILE1 - IDM_FILE - 1) );
|
|
|
|
//
|
|
// Init Items Colors ,Environment and RunDebug params to their default
|
|
// values 'They will later be overrided by the values in .INI file
|
|
// but we ensure to have something coherent even if we can't load
|
|
// the .INI file
|
|
//
|
|
InitDefaults();
|
|
SymSetParentWindow(g_hwndFrame);
|
|
|
|
//
|
|
// Initialize Keyboard Hook
|
|
//
|
|
hKeyHook = SetWindowsHookEx(WH_KEYBOARD,
|
|
KeyboardHook,
|
|
g_hInst,
|
|
GetCurrentThreadId()
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int
|
|
WINAPIV
|
|
main(
|
|
int argc,
|
|
PTSTR argv[ ],
|
|
PTSTR envp[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
description-of-function.
|
|
|
|
Arguments:
|
|
|
|
argc - Supplies the count of arguments on command line.
|
|
|
|
argv - Supplies a pointer to an array of string pointers.
|
|
|
|
Return Value:
|
|
|
|
int - Returns the wParam from the WM_QUIT message.
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT Status;
|
|
CHAR helpfile[MAX_PATH];
|
|
|
|
g_ProgramName = argv[0];
|
|
g_hInst = GetModuleHandle(NULL);
|
|
g_DefPriority = GetPriorityClass(GetCurrentProcess());
|
|
|
|
GetEditorCommandDefaults();
|
|
|
|
Dbg(LoadString(g_hInst, SYS_Help_File, helpfile, sizeof(helpfile)));
|
|
MakeHelpFileName(helpfile);
|
|
|
|
// We need to initialize COM so that we can use SHBrowseForFolder.
|
|
if (FAILED(Status = CoInitializeEx(NULL, COM_THREAD_MODEL)))
|
|
{
|
|
InternalError(Status, "CoInitializeEx");
|
|
return FALSE;
|
|
}
|
|
|
|
// Initialize enough of the GUI to bring up the top level window
|
|
// so the menus can be activated.
|
|
|
|
if (!InitGUI())
|
|
{
|
|
InternalError(E_OUTOFMEMORY, "InitGUI");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!CreateUiInterfaces(FALSE, NULL))
|
|
{
|
|
InternalError(E_OUTOFMEMORY, "CreateUiInterfaces");
|
|
return FALSE;
|
|
}
|
|
|
|
// Select the default workspace.
|
|
if ((Status = UiSwitchWorkspace(WSP_NAME_BASE, g_WorkspaceDefaultName,
|
|
WSP_CREATE_ALWAYS, WSP_APPLY_DEFAULT,
|
|
NULL)) != S_OK)
|
|
{
|
|
//InternalError(Status, "DefaultWorkspace");
|
|
}
|
|
|
|
// Parse the command line.
|
|
// We need to do this before any GUI window is created to support the
|
|
// journaling option.
|
|
|
|
if (!ParseCommandLine(TRUE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Enter main message loop.
|
|
for (;;)
|
|
{
|
|
WaitMessage();
|
|
ProcessPendingMessages();
|
|
|
|
if (g_Exit)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for any engine work that needs to be done.
|
|
//
|
|
|
|
ULONG EventSeq = g_CodeBufferSequence;
|
|
if (EventSeq != g_CodeDisplaySequence)
|
|
{
|
|
// We don't want to stall the engine during
|
|
// file loading so capture the state and then
|
|
// release the lock.
|
|
|
|
Dbg_EnterCriticalSection(&g_QuickLock);
|
|
|
|
ULONG64 Ip = g_CodeIp;
|
|
char FoundFile[MAX_SOURCE_PATH];
|
|
char SymFile[MAX_SOURCE_PATH];
|
|
char PathComp[MAX_SOURCE_PATH];
|
|
strcpy(FoundFile, g_CodeFileFound);
|
|
strcpy(SymFile, g_CodeSymFile);
|
|
strcpy(PathComp, g_CodePathComponent);
|
|
ULONG Line = g_CodeLine;
|
|
BOOL UserActivated = g_CodeUserActivated;
|
|
|
|
Dbg_LeaveCriticalSection(&g_QuickLock);
|
|
|
|
UpdateCodeDisplay(Ip, FoundFile[0] ? FoundFile : NULL,
|
|
SymFile, PathComp[0] ? PathComp : NULL,
|
|
Line, UserActivated);
|
|
g_CodeDisplaySequence = EventSeq;
|
|
}
|
|
|
|
LockUiBuffer(&g_UiOutputBuffer);
|
|
|
|
if (g_UiOutputBuffer.GetDataLen() > 0 &&
|
|
(!g_HoldWaitOutput || !g_WaitingForEvent))
|
|
{
|
|
PSTR Text, End;
|
|
COLORREF Fg, Bg;
|
|
|
|
Text = (PSTR)g_UiOutputBuffer.GetDataBuffer();
|
|
End = Text + g_UiOutputBuffer.GetDataLen();
|
|
while (Text < End)
|
|
{
|
|
GetOutMaskColors(*(ULONG UNALIGNED *)Text, &Fg, &Bg);
|
|
Text += sizeof(ULONG);
|
|
CmdOutput(Text, Fg, Bg);
|
|
Text += strlen(Text) + 1;
|
|
}
|
|
|
|
g_UiOutputBuffer.Empty();
|
|
}
|
|
|
|
UnlockUiBuffer(&g_UiOutputBuffer);
|
|
}
|
|
|
|
TerminateApplication(FALSE);
|
|
|
|
// Keep the C++ compiler from whining
|
|
return 0;
|
|
}
|