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.
1080 lines
28 KiB
1080 lines
28 KiB
/*++
|
|
|
|
Copyright (c) 1999-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
docwin.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the code for the new doc windows.
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#include <dbghelp.h>
|
|
|
|
#define INVOKE_DEFAULT "notepad %f"
|
|
|
|
ULONG g_TabWidth = 32;
|
|
BOOL g_DisasmActivateSource;
|
|
char g_EditorInvokeCommand[MAX_PATH + MAX_SOURCE_PATH] = INVOKE_DEFAULT;
|
|
char g_EditorUpdateCommand[MAX_PATH + MAX_SOURCE_PATH];
|
|
|
|
#define DOCWIN_CONTEXT_ID_BASE 0x100
|
|
|
|
#define DOCWIN_TBB_SET_IP 0
|
|
#define DOCWIN_TBB_INVOKE_EDITOR 1
|
|
#define DOCWIN_TBB_COPY 2
|
|
#define DOCWIN_TBB_EVAL 3
|
|
#define DOCWIN_TBB_DT 4
|
|
#define DOCWIN_TBB_VIEW_IP 5
|
|
|
|
TBBUTTON g_DocWinTbButtons[] =
|
|
{
|
|
TEXT_TB_BTN(DOCWIN_TBB_SET_IP,
|
|
"Set instruction pointer to current line", 0),
|
|
TEXT_TB_BTN(DOCWIN_TBB_INVOKE_EDITOR, "Edit this file...", 0),
|
|
TEXT_TB_BTN(DOCWIN_TBB_COPY, "Copy", 0),
|
|
TEXT_TB_BTN(DOCWIN_TBB_EVAL, "Evalute selection", 0),
|
|
TEXT_TB_BTN(DOCWIN_TBB_DT, "Display selected type", 0),
|
|
TEXT_TB_BTN(DOCWIN_TBB_VIEW_IP, "Disassemble at current line", 0),
|
|
};
|
|
|
|
#define NUM_DOCWIN_MENU_BUTTONS \
|
|
(sizeof(g_DocWinTbButtons) / sizeof(g_DocWinTbButtons[0]))
|
|
|
|
HMENU DOCWIN_DATA::s_ContextMenu;
|
|
|
|
void
|
|
RunEditorCommand(PCSTR Command, PCSTR FoundFile, ULONG Line)
|
|
{
|
|
char RepCommand[MAX_PATH + MAX_SOURCE_PATH];
|
|
PCSTR Src;
|
|
PSTR Dst;
|
|
|
|
if (!Command[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
Src = Command;
|
|
Dst = RepCommand;
|
|
while (*Src)
|
|
{
|
|
if (*Src == '%')
|
|
{
|
|
if (*(Src + 1) == 'l' ||
|
|
*(Src + 1) == 'L')
|
|
{
|
|
// Line number.
|
|
Src += 2;
|
|
if ((Dst - RepCommand) + 20 >= sizeof(RepCommand))
|
|
{
|
|
return;
|
|
}
|
|
sprintf(Dst, "%d", (*(Src + 1) == 'L' ? Line : (Line + 1)));
|
|
Dst += strlen(Dst);
|
|
}
|
|
else if (*(Src + 1) == 'f' ||
|
|
*(Src + 1) == 'p')
|
|
{
|
|
// File name.
|
|
Src += 2;
|
|
if ((Dst - RepCommand) + strlen(FoundFile) >=
|
|
sizeof(RepCommand))
|
|
{
|
|
return;
|
|
}
|
|
strcpy(Dst, FoundFile);
|
|
Dst += strlen(Dst);
|
|
}
|
|
else
|
|
{
|
|
*Dst++ = *Src++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*Dst++ = *Src++;
|
|
}
|
|
}
|
|
*Dst = 0;
|
|
|
|
STARTUPINFOA Start;
|
|
PROCESS_INFORMATION Info;
|
|
|
|
ZeroMemory(&Start, sizeof(Start));
|
|
Start.cb = sizeof(Start);
|
|
|
|
if (CreateProcessA(NULL, RepCommand, NULL, NULL, FALSE,
|
|
0, NULL, NULL, &Start, &Info))
|
|
{
|
|
CloseHandle(Info.hProcess);
|
|
CloseHandle(Info.hThread);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
DOCWIN_DATA::DOCWIN_DATA()
|
|
// State buffer isn't currently used.
|
|
: EDITWIN_DATA(256)
|
|
{
|
|
m_enumType = DOC_WINDOW;
|
|
|
|
ZeroMemory(m_FoundFile, _tsizeof(m_FoundFile));
|
|
ZeroMemory(m_SymFileBuffer, _tsizeof(m_SymFileBuffer));
|
|
ZeroMemory(m_PathComponent, _tsizeof(m_PathComponent));
|
|
ZeroMemory(&m_LastWriteTime, sizeof(m_LastWriteTime));
|
|
|
|
m_FindSel.cpMin = 1;
|
|
m_FindSel.cpMax = 0;
|
|
m_FindFlags = 0;
|
|
}
|
|
|
|
void
|
|
DOCWIN_DATA::Validate()
|
|
{
|
|
EDITWIN_DATA::Validate();
|
|
|
|
Assert(DOC_WINDOW == m_enumType);
|
|
}
|
|
|
|
BOOL
|
|
DOCWIN_DATA::SelectedText(PTSTR Buffer, ULONG BufferChars)
|
|
{
|
|
return RicheditGetSelectionText(m_hwndChild, Buffer, BufferChars) > 0;
|
|
}
|
|
|
|
BOOL
|
|
DOCWIN_DATA::CanGotoLine(void)
|
|
{
|
|
return m_TextLines > 0;
|
|
}
|
|
|
|
void
|
|
DOCWIN_DATA::GotoLine(ULONG Line)
|
|
{
|
|
CHARRANGE Sel;
|
|
|
|
Sel.cpMin = (LONG)SendMessage(m_hwndChild, EM_LINEINDEX, Line - 1, 0);
|
|
Sel.cpMax = Sel.cpMin;
|
|
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Sel);
|
|
}
|
|
|
|
void
|
|
DOCWIN_DATA::Find(PTSTR Text, ULONG Flags, BOOL FromDlg)
|
|
{
|
|
RicheditFind(m_hwndChild, Text, Flags,
|
|
&m_FindSel, &m_FindFlags, FromDlg);
|
|
}
|
|
|
|
HRESULT
|
|
DOCWIN_DATA::CodeExprAtCaret(PSTR Expr, ULONG ExprSize, PULONG64 Offset)
|
|
{
|
|
LRESULT LineChar;
|
|
LONG Line;
|
|
|
|
LineChar = SendMessage(m_hwndChild, EM_LINEINDEX, -1, 0);
|
|
Line = (LONG)SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, LineChar);
|
|
if (Line < 0)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Convert to one-based.
|
|
Line++;
|
|
|
|
if (Expr == NULL)
|
|
{
|
|
// Caller is just checking whether it's possible
|
|
// to get an expression or not, such as the
|
|
// menu enable code. This code always considers
|
|
// it possible since it can't know for sure without
|
|
// a full symbol check.
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// First attempt to resolve the source line using currently
|
|
// loaded symbols. This is done directly from the UI
|
|
// thread for synchronous behavior. The assumption is
|
|
// that turning off symbol loads will limit the execution
|
|
// time to something reasonably quick.
|
|
//
|
|
|
|
DEBUG_VALUE Val;
|
|
HRESULT Status;
|
|
|
|
if (!PrintString(Expr, ExprSize, "@@masm(`<U>%s:%d+`)", m_SymFile, Line))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
Status = g_pUiControl->Evaluate(Expr, DEBUG_VALUE_INT64, &Val, NULL);
|
|
|
|
// Don't preserve the <U>nqualified option in the actual
|
|
// expression returned as it's just a temporary override.
|
|
sprintf(Expr, "@@masm(`%s:%d+`)", m_SymFile, Line);
|
|
|
|
if (Status == S_OK)
|
|
{
|
|
if (Offset != NULL)
|
|
{
|
|
*Offset = Val.I64;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG SymOpts;
|
|
|
|
if (g_pUiSymbols->GetSymbolOptions(&SymOpts) == S_OK &&
|
|
(SymOpts & SYMOPT_NO_UNQUALIFIED_LOADS))
|
|
{
|
|
// The user isn't allowing unqualified loads so
|
|
// further searches won't help.
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
// We weren't able to resolve the expression with the
|
|
// existing symbols so we'll need to do a full search.
|
|
// This can be very expensive, so allow the user to cancel.
|
|
if (g_QuietMode == QMODE_DISABLED)
|
|
{
|
|
int Mode = QuestionBox(STR_Unresolved_Source_Expr, MB_YESNOCANCEL);
|
|
if (Mode == IDCANCEL)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
else if (Mode == IDYES)
|
|
{
|
|
if (g_pUiControl->Evaluate(Expr, DEBUG_VALUE_INT64,
|
|
&Val, NULL) == S_OK)
|
|
{
|
|
if (Offset != NULL)
|
|
{
|
|
*Offset = Val.I64;
|
|
}
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Let the expression go without trying to further resolve it.
|
|
if (Offset != NULL)
|
|
{
|
|
*Offset = DEBUG_INVALID_OFFSET;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
void
|
|
DOCWIN_DATA::ToggleBpAtCaret(void)
|
|
{
|
|
HRESULT Status;
|
|
LRESULT LineChar;
|
|
LONG Line;
|
|
|
|
LineChar = SendMessage(m_hwndChild, EM_LINEINDEX, -1, 0);
|
|
Line = (LONG)SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, LineChar);
|
|
if (Line < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If we have a breakpoint on this line remove it.
|
|
EDIT_HIGHLIGHT* Hl = GetLineHighlighting(Line);
|
|
if (Hl != NULL && (Hl->Flags & EHL_ANY_BP))
|
|
{
|
|
PrintStringCommand(UIC_SILENT_EXECUTE, "bc %d", (ULONG)Hl->Data);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// No breakpoint exists so add a new one.
|
|
//
|
|
|
|
char CodeExpr[MAX_OFFSET_EXPR];
|
|
ULONG64 Offset;
|
|
|
|
Status = CodeExprAtCaret(CodeExpr, DIMA(CodeExpr), &Offset);
|
|
if (FAILED(Status))
|
|
{
|
|
MessageBeep(0);
|
|
ErrorBox(NULL, 0, ERR_No_Code_For_File_Line);
|
|
}
|
|
else
|
|
{
|
|
if (Status == S_OK)
|
|
{
|
|
char SymName[MAX_OFFSET_EXPR];
|
|
ULONG64 Disp;
|
|
|
|
// Check and see whether this offset maps
|
|
// exactly to a symbol. If it does, use
|
|
// the symbol name to be more robust in the
|
|
// face of source changes.
|
|
// Symbols should be loaded at this point since
|
|
// we just used them to resolve the source
|
|
// expression that produced Offset, so we
|
|
// can safely do this on the UI thread.
|
|
if (g_pUiSymbols->GetNameByOffset(Offset, SymName, sizeof(SymName),
|
|
NULL, &Disp) == S_OK &&
|
|
Disp == 0)
|
|
{
|
|
strcpy(CodeExpr, SymName);
|
|
}
|
|
}
|
|
|
|
PrintStringCommand(UIC_SILENT_EXECUTE, "bu %s", CodeExpr);
|
|
}
|
|
}
|
|
|
|
HMENU
|
|
DOCWIN_DATA::GetContextMenu(void)
|
|
{
|
|
return s_ContextMenu;
|
|
}
|
|
|
|
void
|
|
DOCWIN_DATA::OnContextMenuSelection(UINT Item)
|
|
{
|
|
CHARRANGE Sel;
|
|
int Line;
|
|
TCHAR SelText[256];
|
|
|
|
Item -= DOCWIN_CONTEXT_ID_BASE;
|
|
|
|
switch(Item)
|
|
{
|
|
case DOCWIN_TBB_SET_IP:
|
|
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&Sel);
|
|
Line = (int)
|
|
SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, Sel.cpMin);
|
|
PrintStringCommand(UIC_SET_IP, "r$ip = @@masm(`%s:%d+`)",
|
|
m_SymFile, Line + 1);
|
|
break;
|
|
case DOCWIN_TBB_INVOKE_EDITOR:
|
|
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&Sel);
|
|
Line = (int)
|
|
SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, Sel.cpMin);
|
|
RunEditorCommand(g_EditorInvokeCommand, m_FoundFile, Line);
|
|
break;
|
|
case DOCWIN_TBB_COPY:
|
|
SendMessage(m_hwndChild, WM_COPY, 0, 0);
|
|
break;
|
|
case DOCWIN_TBB_EVAL:
|
|
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&Sel);
|
|
if (Sel.cpMax > Sel.cpMin)
|
|
{
|
|
if (!RicheditGetSelectionText(m_hwndChild, SelText, DIMA(SelText)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (!RicheditGetSourceToken(m_hwndChild, SelText, DIMA(SelText),
|
|
&Sel))
|
|
{
|
|
break;
|
|
}
|
|
PrintStringCommand(UIC_EXECUTE, "?? %s", SelText);
|
|
break;
|
|
case DOCWIN_TBB_DT:
|
|
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&Sel);
|
|
if (Sel.cpMax > Sel.cpMin)
|
|
{
|
|
if (!RicheditGetSelectionText(m_hwndChild, SelText, DIMA(SelText)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (!RicheditGetSourceToken(m_hwndChild, SelText, DIMA(SelText),
|
|
&Sel))
|
|
{
|
|
break;
|
|
}
|
|
PrintStringCommand(UIC_EXECUTE, "dt %s", SelText);
|
|
break;
|
|
case DOCWIN_TBB_VIEW_IP:
|
|
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&Sel);
|
|
Line = (int)
|
|
SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, Sel.cpMin);
|
|
PrintStringCommand(UIC_DISPLAY_CODE_EXPR, "@@masm(`%s:%d+`)",
|
|
m_SymFile, Line + 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
DOCWIN_DATA::OnCreate(void)
|
|
{
|
|
if (s_ContextMenu == NULL &&
|
|
g_EditorInvokeCommand)
|
|
{
|
|
s_ContextMenu = CreateContextMenuFromToolbarButtons
|
|
(NUM_DOCWIN_MENU_BUTTONS, g_DocWinTbButtons,
|
|
DOCWIN_CONTEXT_ID_BASE);
|
|
if (s_ContextMenu == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!EDITWIN_DATA::OnCreate())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SendMessage(m_hwndChild, EM_SETEDITSTYLE,
|
|
SES_XLTCRCRLFTOCR, SES_XLTCRCRLFTOCR);
|
|
SendMessage(m_hwndChild, EM_SETEVENTMASK,
|
|
0, ENM_SELCHANGE | ENM_KEYEVENTS | ENM_MOUSEEVENTS);
|
|
SendMessage(m_hwndChild, EM_SETTABSTOPS, 1, (LPARAM)&g_TabWidth);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT
|
|
DOCWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm)
|
|
{
|
|
NMHDR* Hdr = (NMHDR*)Lpm;
|
|
|
|
if (Hdr->code == EN_SELCHANGE)
|
|
{
|
|
SELCHANGE* SelChange = (SELCHANGE*)Lpm;
|
|
int Line = (int)
|
|
SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0,
|
|
SelChange->chrg.cpMin);
|
|
LRESULT LineFirst =
|
|
SendMessage(m_hwndChild, EM_LINEINDEX, Line, 0);
|
|
SetLineColumn_StatusBar(Line + 1,
|
|
(int)(SelChange->chrg.cpMin - LineFirst) + 1);
|
|
return 0;
|
|
}
|
|
else if (Hdr->code == EN_MSGFILTER)
|
|
{
|
|
MSGFILTER* Filter = (MSGFILTER*)Lpm;
|
|
char Token[256];
|
|
CHARRANGE TokenRange;
|
|
|
|
if (Filter->msg == WM_LBUTTONDBLCLK &&
|
|
RicheditGetSourceToken(m_hwndChild, Token, DIMA(Token),
|
|
&TokenRange))
|
|
{
|
|
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&TokenRange);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return EDITWIN_DATA::OnNotify(Wpm, Lpm);
|
|
}
|
|
|
|
void
|
|
DOCWIN_DATA::OnUpdate(
|
|
UpdateType Type
|
|
)
|
|
{
|
|
if (Type == UPDATE_BP ||
|
|
Type == UPDATE_BUFFER ||
|
|
Type == UPDATE_END_SESSION)
|
|
{
|
|
UpdateBpMarks();
|
|
}
|
|
else if (Type == UPDATE_START_SESSION ||
|
|
Type == UPDATE_REFRESH_MODULES)
|
|
{
|
|
// If there's already a message box open we don't
|
|
// want to put up a new one. This may mean we
|
|
// miss a source file change but it should be relatively
|
|
// uncommon. If it's a problem we could start up a timer
|
|
// to repost the update later.
|
|
if (g_nBoxCount == 0 &&
|
|
m_FoundFile[0] &&
|
|
CheckForFileChanges(m_FoundFile, &m_LastWriteTime) == IDYES)
|
|
{
|
|
char Found[MAX_SOURCE_PATH], Sym[MAX_SOURCE_PATH];
|
|
char PathComp[MAX_SOURCE_PATH];
|
|
|
|
// Save away filenames since they're copied over
|
|
// on a successful load.
|
|
strcpy(Found, m_FoundFile);
|
|
strcpy(Sym, m_SymFileBuffer);
|
|
strcpy(PathComp, m_PathComponent);
|
|
|
|
if (!LoadFile(Found, Sym, PathComp))
|
|
{
|
|
PostMessage(g_hwndMDIClient, WM_MDIDESTROY, (WPARAM)m_Win, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
DOCWIN_DATA::GetWorkspaceSize(void)
|
|
{
|
|
ULONG Len = EDITWIN_DATA::GetWorkspaceSize();
|
|
Len += _tcslen(m_FoundFile) + 1;
|
|
Len += _tcslen(m_SymFileBuffer) + 1;
|
|
Len += _tcslen(m_PathComponent) + 1;
|
|
Len += sizeof(LONG);
|
|
return Len;
|
|
}
|
|
|
|
PUCHAR
|
|
DOCWIN_DATA::SetWorkspace(PUCHAR Data)
|
|
{
|
|
PTSTR Str = (PTSTR)EDITWIN_DATA::SetWorkspace(Data);
|
|
_tcscpy(Str, m_FoundFile);
|
|
Str += _tcslen(m_FoundFile) + 1;
|
|
_tcscpy(Str, m_SymFileBuffer);
|
|
Str += _tcslen(m_SymFileBuffer) + 1;
|
|
_tcscpy(Str, m_PathComponent);
|
|
Str += _tcslen(m_PathComponent) + 1;
|
|
|
|
CHARRANGE Sel;
|
|
|
|
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&Sel);
|
|
*(LONG UNALIGNED *)Str = Sel.cpMin;
|
|
Str += sizeof(Sel.cpMin);
|
|
|
|
return (PUCHAR)Str;
|
|
}
|
|
|
|
PUCHAR
|
|
DOCWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End)
|
|
{
|
|
PTSTR Found = (PTSTR)EDITWIN_DATA::ApplyWorkspace1(Data, End);
|
|
PTSTR Sym = Found + _tcslen(Found) + 1;
|
|
PTSTR SymEnd = Sym + _tcslen(Sym) + 1;
|
|
PTSTR PathComp = SymEnd;
|
|
|
|
if ((PUCHAR)PathComp >= End || !PathComp[0])
|
|
{
|
|
PathComp = NULL;
|
|
}
|
|
|
|
if ((PUCHAR)SymEnd >= End)
|
|
{
|
|
Data = (PUCHAR)SymEnd;
|
|
}
|
|
else
|
|
{
|
|
Data = (PUCHAR)(SymEnd + _tcslen(SymEnd) + 1);
|
|
}
|
|
|
|
if (Found[0])
|
|
{
|
|
if (FindDocWindowByFileName(Found, NULL, NULL) ||
|
|
!LoadFile(Found, Sym[0] ? Sym : NULL, PathComp))
|
|
{
|
|
PostMessage(g_hwndMDIClient, WM_MDIDESTROY, (WPARAM)m_Win, 0);
|
|
}
|
|
}
|
|
|
|
if (Data < End)
|
|
{
|
|
CHARRANGE Sel;
|
|
|
|
Sel.cpMin = *(LONG UNALIGNED *)Data;
|
|
Sel.cpMax = Sel.cpMin;
|
|
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Sel);
|
|
SendMessage(m_hwndChild, EM_SCROLLCARET, 0, 0);
|
|
|
|
Data += sizeof(Sel.cpMin);
|
|
}
|
|
|
|
return Data;
|
|
}
|
|
|
|
void
|
|
DOCWIN_DATA::UpdateBpMarks(void)
|
|
{
|
|
if (m_TextLines == 0 ||
|
|
g_BpBuffer->UiLockForRead() != S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0);
|
|
|
|
// Remove existing BP highlights.
|
|
RemoveAllHighlights(EHL_ANY_BP);
|
|
|
|
//
|
|
// Highlight every line that matches a breakpoint.
|
|
//
|
|
|
|
BpBufferData* BpData = (BpBufferData*)g_BpBuffer->GetDataBuffer();
|
|
ULONG i;
|
|
|
|
for (i = 0; i < g_BpCount; i++)
|
|
{
|
|
if (BpData[i].FileOffset)
|
|
{
|
|
PSTR FileSpace;
|
|
ULONG Line;
|
|
PSTR FileStop, MatchStop;
|
|
ULONG HlFlags;
|
|
|
|
FileSpace = (PSTR)g_BpBuffer->GetDataBuffer() +
|
|
BpData[i].FileOffset;
|
|
// Adjust to zero-based.
|
|
Line = *(ULONG UNALIGNED *)FileSpace - 1;
|
|
FileSpace += sizeof(Line);
|
|
|
|
// If this document's file matches some suffix
|
|
// of the breakpoint's file at the path component
|
|
// level then do the highlight. This can result in
|
|
// extra highlights for multiple files with the same
|
|
// name but in different directories. That's a rare
|
|
// enough problem to wait for somebody to complain
|
|
// before trying to hack some better check up.
|
|
if (SymMatchFileName(FileSpace, (PSTR)m_SymFile,
|
|
&FileStop, &MatchStop) ||
|
|
*MatchStop == '\\' ||
|
|
*MatchStop == '/' ||
|
|
*MatchStop == ':')
|
|
{
|
|
if (BpData[i].Flags & DEBUG_BREAKPOINT_ENABLED)
|
|
{
|
|
HlFlags = EHL_ENABLED_BP;
|
|
}
|
|
else
|
|
{
|
|
HlFlags = EHL_DISABLED_BP;
|
|
}
|
|
|
|
EDIT_HIGHLIGHT* Hl = AddHighlight(Line, HlFlags);
|
|
if (Hl != NULL)
|
|
{
|
|
Hl->Data = BpData[i].Id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UnlockStateBuffer(g_BpBuffer);
|
|
|
|
SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0);
|
|
InvalidateRect(m_hwndChild, NULL, TRUE);
|
|
}
|
|
|
|
DWORD
|
|
CALLBACK
|
|
EditStreamCallback(
|
|
DWORD_PTR dwFileHandle, // application-defined value
|
|
LPBYTE pbBuff, // data buffer
|
|
LONG cb, // number of bytes to read or write
|
|
LONG *pcb // number of bytes transferred
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
PathFile* File = (PathFile*)dwFileHandle;
|
|
|
|
if ((Status = File->Read(pbBuff, cb, (PDWORD)pcb)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Edit out page-break characters (^L's) as richedit
|
|
// gives them their own line which throws off line numbers.
|
|
while (cb-- > 0)
|
|
{
|
|
if (*pbBuff == '\f')
|
|
{
|
|
*pbBuff = ' ';
|
|
}
|
|
|
|
pbBuff++;
|
|
}
|
|
|
|
return 0; // No error
|
|
}
|
|
|
|
BOOL
|
|
DOCWIN_DATA::LoadFile(
|
|
PCTSTR pszFoundFile,
|
|
PCTSTR pszSymFile,
|
|
PCTSTR pszPathComponent
|
|
)
|
|
/*++
|
|
Returns
|
|
TRUE - Success, file opened and loaded
|
|
FALSE - Failure, file not loaded
|
|
--*/
|
|
{
|
|
Assert(pszFoundFile);
|
|
|
|
BOOL bRet = TRUE;
|
|
HCURSOR hcursor = NULL;
|
|
EDITSTREAM editstr = {0};
|
|
PathFile *File = NULL;
|
|
|
|
if ((OpenPathFile(pszPathComponent, pszFoundFile, 0, &File)) != S_OK)
|
|
{
|
|
ErrorBox(NULL, 0, ERR_File_Open, pszFoundFile);
|
|
bRet = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// Store last write time to check for file changes.
|
|
if (File->GetLastWriteTime(&m_LastWriteTime) != S_OK)
|
|
{
|
|
ZeroMemory(&m_LastWriteTime, sizeof(m_LastWriteTime));
|
|
}
|
|
|
|
// Set the Hour glass cursor
|
|
hcursor = SetCursor( LoadCursor(NULL, IDC_WAIT) );
|
|
|
|
// Select all of the text so that it will be replaced
|
|
SendMessage(m_hwndChild, EM_SETSEL, 0, -1);
|
|
|
|
// Put the text into the window
|
|
editstr.dwCookie = (DWORD_PTR)File;
|
|
editstr.pfnCallback = EditStreamCallback;
|
|
|
|
SendMessage(m_hwndChild,
|
|
EM_STREAMIN,
|
|
SF_TEXT,
|
|
(LPARAM) &editstr
|
|
);
|
|
|
|
RicheditUpdateColors(m_hwndChild,
|
|
g_Colors[COL_PLAIN_TEXT].Color, TRUE,
|
|
g_Colors[COL_PLAIN].Color, TRUE);
|
|
|
|
// Restore cursor
|
|
SetCursor(hcursor);
|
|
|
|
_tcsncpy(m_FoundFile, pszFoundFile, _tsizeof(m_FoundFile) - 1 );
|
|
m_FoundFile[ _tsizeof(m_FoundFile) - 1 ] = 0;
|
|
if (pszSymFile != NULL && pszSymFile[0])
|
|
{
|
|
_tcsncpy(m_SymFileBuffer, pszSymFile, _tsizeof(m_SymFileBuffer) - 1 );
|
|
m_SymFileBuffer[ _tsizeof(m_SymFileBuffer) - 1 ] = 0;
|
|
m_SymFile = m_SymFileBuffer;
|
|
}
|
|
else
|
|
{
|
|
// No symbol file information so just use the found filename.
|
|
m_SymFileBuffer[0] = 0;
|
|
m_SymFile = strrchr(m_FoundFile, '\\');
|
|
if (m_SymFile == NULL)
|
|
{
|
|
m_SymFile = strrchr(m_FoundFile, '/');
|
|
if (m_SymFile == NULL)
|
|
{
|
|
m_SymFile = strrchr(m_FoundFile, ':');
|
|
if (m_SymFile == NULL)
|
|
{
|
|
m_SymFile = m_FoundFile - 1;
|
|
}
|
|
}
|
|
}
|
|
m_SymFile++;
|
|
}
|
|
if (pszPathComponent)
|
|
{
|
|
_tcsncpy(m_PathComponent, pszPathComponent,
|
|
_tsizeof(m_PathComponent) - 1);
|
|
m_PathComponent[_tsizeof(m_PathComponent) - 1] = 0;
|
|
}
|
|
else
|
|
{
|
|
m_PathComponent[0] = 0;
|
|
}
|
|
|
|
SetWindowText(m_Win, m_FoundFile);
|
|
|
|
if (SendMessage(m_hwndChild, WM_GETTEXTLENGTH, 0, 0) == 0)
|
|
{
|
|
m_TextLines = 0;
|
|
}
|
|
else
|
|
{
|
|
m_TextLines = (ULONG)SendMessage(m_hwndChild, EM_GETLINECOUNT, 0, 0);
|
|
}
|
|
|
|
if (g_LineMarkers)
|
|
{
|
|
// Insert marker space before every line.
|
|
for (ULONG i = 0; i < m_TextLines; i++)
|
|
{
|
|
CHARRANGE Ins;
|
|
|
|
Ins.cpMin = (LONG)SendMessage(m_hwndChild, EM_LINEINDEX, i, 0);
|
|
Ins.cpMax = Ins.cpMin;
|
|
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Ins);
|
|
SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)" ");
|
|
}
|
|
}
|
|
|
|
// Request that the engine update the line map for the file.
|
|
UiRequestRead();
|
|
|
|
exit:
|
|
delete File;
|
|
return bRet;
|
|
}
|
|
|
|
BOOL
|
|
SameFileName(PCSTR Name1, PCSTR Name2)
|
|
{
|
|
while (*Name1)
|
|
{
|
|
if (!(((*Name1 == '\\' || *Name1 == '/') &&
|
|
(*Name2 == '\\' || *Name2 == '/')) ||
|
|
toupper(*Name1) == toupper(*Name2)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
Name1++;
|
|
Name2++;
|
|
}
|
|
|
|
return *Name2 == 0;
|
|
}
|
|
|
|
BOOL
|
|
FindDocWindowByFileName(
|
|
IN PCTSTR pszFile,
|
|
OPTIONAL HWND *phwnd,
|
|
OPTIONAL PDOCWIN_DATA *ppDocWinData
|
|
)
|
|
/*++
|
|
Returns
|
|
TRUE - If the window is currently open.
|
|
FALSE - Not currently open.
|
|
--*/
|
|
{
|
|
Assert(pszFile);
|
|
|
|
PLIST_ENTRY Entry;
|
|
PDOCWIN_DATA pTmp;
|
|
|
|
Entry = g_ActiveWin.Flink;
|
|
|
|
while (Entry != &g_ActiveWin)
|
|
{
|
|
pTmp = (PDOCWIN_DATA)ACTIVE_WIN_ENTRY(Entry);
|
|
if ( pTmp->m_enumType == DOC_WINDOW &&
|
|
SameFileName(pTmp->m_FoundFile, pszFile) )
|
|
{
|
|
if (ppDocWinData)
|
|
{
|
|
*ppDocWinData = pTmp;
|
|
}
|
|
if (phwnd)
|
|
{
|
|
*phwnd = pTmp->m_Win;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
Entry = Entry->Flink;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
OpenOrActivateFile(PCSTR FoundFile, PCSTR SymFile, PCSTR PathComponent,
|
|
ULONG Line, BOOL Activate, BOOL UserActivated)
|
|
{
|
|
HWND hwndDoc = NULL;
|
|
PDOCWIN_DATA pDoc;
|
|
BOOL Activated = FALSE;
|
|
|
|
if ( FindDocWindowByFileName( FoundFile, &hwndDoc, &pDoc) )
|
|
{
|
|
if (Activate)
|
|
{
|
|
// Found it. Now activate it.
|
|
if (IsIconic(hwndDoc))
|
|
{
|
|
ShowWindow(hwndDoc, SW_RESTORE);
|
|
}
|
|
ActivateMDIChild(hwndDoc, UserActivated);
|
|
Activated = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HWND WinTop, WinUnder;
|
|
|
|
WinTop = MDIGetActive(g_hwndMDIClient, NULL);
|
|
if (WinTop)
|
|
{
|
|
WinUnder = GetNextWindow(WinTop, GW_HWNDNEXT);
|
|
}
|
|
else
|
|
{
|
|
WinUnder = NULL;
|
|
}
|
|
|
|
hwndDoc = NewDoc_CreateWindow(g_hwndMDIClient);
|
|
if (hwndDoc == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pDoc = GetDocWinData(hwndDoc);
|
|
Assert(pDoc);
|
|
|
|
if (!pDoc->LoadFile(FoundFile, SymFile, PathComponent))
|
|
{
|
|
DestroyWindow(pDoc->m_Win);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!UserActivated && WinTop)
|
|
{
|
|
// If this isn't a user-provoked activation we don't
|
|
// want the window to obscure the user's current window.
|
|
// Reorder the current windows appropriately.
|
|
ReorderChildren(WinUnder, WinTop, pDoc->m_Win,
|
|
UserActivated);
|
|
}
|
|
|
|
Activated = TRUE;
|
|
}
|
|
|
|
// Success. Now highlight the line.
|
|
pDoc->SetCurrentLineHighlight(Line);
|
|
|
|
return Activated;
|
|
}
|
|
|
|
void
|
|
UpdateCodeDisplay(
|
|
ULONG64 Ip,
|
|
PCSTR FoundFile,
|
|
PCSTR SymFile,
|
|
PCSTR PathComponent,
|
|
ULONG Line,
|
|
BOOL UserActivated
|
|
)
|
|
{
|
|
// Update the disassembly window if there's one
|
|
// active or there's no source information.
|
|
|
|
BOOL Activated = FALSE;
|
|
HWND hwndDisasm = GetDisasmHwnd();
|
|
|
|
if (hwndDisasm == NULL && FoundFile == NULL &&
|
|
(g_WinOptions & WOPT_AUTO_DISASM))
|
|
{
|
|
// No disassembly window around and no source so create one.
|
|
hwndDisasm = NewDisasm_CreateWindow(g_hwndMDIClient);
|
|
}
|
|
|
|
if (hwndDisasm != NULL)
|
|
{
|
|
PDISASMWIN_DATA pDis = GetDisasmWinData(hwndDisasm);
|
|
Assert(pDis);
|
|
|
|
pDis->SetCurInstr(Ip);
|
|
}
|
|
|
|
if (FoundFile != NULL)
|
|
{
|
|
//
|
|
// We now know the file name and line number. Either
|
|
// it's open or we open it.
|
|
//
|
|
|
|
Activated = OpenOrActivateFile(FoundFile, SymFile, PathComponent, Line,
|
|
GetSrcMode_StatusBar() ||
|
|
g_DisasmActivateSource,
|
|
UserActivated);
|
|
|
|
RunEditorCommand(g_EditorUpdateCommand, FoundFile, Line);
|
|
}
|
|
else
|
|
{
|
|
// No source file was found so make sure no
|
|
// doc windows have a highlight.
|
|
EDITWIN_DATA::RemoveActiveWinHighlights(1 << DOC_WINDOW,
|
|
EHL_CURRENT_LINE);
|
|
}
|
|
|
|
if ((!Activated || !GetSrcMode_StatusBar()) && hwndDisasm != NULL)
|
|
{
|
|
// No window has been activated yet so fall back
|
|
// on activating the disassembly window.
|
|
ActivateMDIChild(hwndDisasm, UserActivated);
|
|
}
|
|
}
|
|
|
|
void
|
|
SetTabWidth(ULONG TabWidth)
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
PDOCWIN_DATA DocData;
|
|
|
|
g_TabWidth = TabWidth;
|
|
if (g_Workspace != NULL)
|
|
{
|
|
g_Workspace->SetUlong(WSP_GLOBAL_TAB_WIDTH, TabWidth);
|
|
}
|
|
|
|
Entry = g_ActiveWin.Flink;
|
|
while (Entry != &g_ActiveWin)
|
|
{
|
|
DocData = (PDOCWIN_DATA)ACTIVE_WIN_ENTRY(Entry);
|
|
if (DocData->m_enumType == DOC_WINDOW)
|
|
{
|
|
SendMessage(DocData->m_hwndChild, EM_SETTABSTOPS,
|
|
1, (LPARAM)&g_TabWidth);
|
|
}
|
|
|
|
Entry = Entry->Flink;
|
|
}
|
|
}
|
|
|
|
void
|
|
GetEditorCommandDefaults(void)
|
|
{
|
|
PSTR Env;
|
|
HKEY Key;
|
|
|
|
// As a convenience for windiff users pick up the
|
|
// windiff editor registry setting.
|
|
if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windiff",
|
|
0, KEY_READ, &Key) == ERROR_SUCCESS)
|
|
{
|
|
DWORD Type;
|
|
DWORD Size;
|
|
|
|
Size = sizeof(g_EditorInvokeCommand);
|
|
if (RegQueryValueExA(Key, "Editor", NULL, &Type,
|
|
(LPBYTE)g_EditorInvokeCommand,
|
|
&Size) != ERROR_SUCCESS ||
|
|
Type != REG_SZ)
|
|
{
|
|
strcpy(g_EditorInvokeCommand, INVOKE_DEFAULT);
|
|
}
|
|
|
|
RegCloseKey(Key);
|
|
}
|
|
|
|
Env = getenv("WINDBG_INVOKE_EDITOR");
|
|
if (Env)
|
|
{
|
|
CopyString(g_EditorInvokeCommand, Env, DIMA(g_EditorInvokeCommand));
|
|
}
|
|
|
|
Env = getenv("WINDBG_UPDATE_EDITOR");
|
|
if (Env)
|
|
{
|
|
CopyString(g_EditorUpdateCommand, Env, DIMA(g_EditorInvokeCommand));
|
|
}
|
|
}
|