|
|
/*++
Copyright (c) 1999-2000 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>
ULONG g_TabWidth = 32; BOOL g_DisasmActivateSource;
//
//
//
DOCWIN_DATA::DOCWIN_DATA() // State buffer isn't currently used.
: EDITWIN_DATA(256) { m_enumType = DOC_WINDOW;
ZeroMemory(m_szFoundFile, _tsizeof(m_szFoundFile)); ZeroMemory(m_szSymFile, _tsizeof(m_szSymFile)); 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::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) { RicheditFind(m_hwndChild, Text, Flags, &m_FindSel, &m_FindFlags, TRUE); }
BOOL DOCWIN_DATA::CodeExprAtCaret(PSTR Expr, 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 FALSE; }
// Convert to one-based.
Line++; if (Offset != NULL) { *Offset = DEBUG_INVALID_OFFSET; }
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 TRUE; } //
// 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; sprintf(Expr, "`<U>%s:%d+`", m_pszSymFile, Line); 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, "`%s:%d+`", m_pszSymFile, Line);
if (Status == S_OK) { if (Offset != NULL) { *Offset = Val.I64; } return TRUE; }
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 FALSE; }
// 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) { int Mode = QuestionBox(STR_Unresolved_Source_Expr, MB_YESNOCANCEL); if (Mode == IDCANCEL) { return FALSE; } else if (Mode == IDYES) { if (g_pUiControl->Evaluate(Expr, DEBUG_VALUE_INT64, &Val, NULL) == S_OK) { if (Offset != NULL) { *Offset = Val.I64; } return TRUE; } else { return FALSE; } } }
// Let the expression go without trying to further resolve it.
return TRUE; }
void DOCWIN_DATA::ToggleBpAtCaret(void) { 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;
if (!CodeExprAtCaret(CodeExpr, &Offset)) { MessageBeep(0); ErrorBox(NULL, 0, ERR_No_Code_For_File_Line); } else { if (Offset != DEBUG_INVALID_OFFSET) { 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); } }
BOOL DOCWIN_DATA::OnCreate(void) { if (!EDITWIN_DATA::OnCreate()) { return FALSE; }
SendMessage(m_hwndChild, EM_SETEVENTMASK, 0, ENM_SELCHANGE | ENM_KEYEVENTS); SendMessage(m_hwndChild, EM_SETTABSTOPS, 1, (LPARAM)&g_TabWidth); return TRUE; }
LRESULT DOCWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm) { SELCHANGE* SelChange = (SELCHANGE*)Lpm;
if (SelChange->nmhdr.code == EN_SELCHANGE) { 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; }
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) { if (m_szFoundFile[0] && CheckForFileChanges(m_szFoundFile, &m_LastWriteTime) == IDYES) { char Found[MAX_SOURCE_PATH], Sym[MAX_SOURCE_PATH];
// Save away filenames since they're copied over
// on a successful load.
strcpy(Found, m_szFoundFile); strcpy(Sym, m_szSymFile); if (!LoadFile(Found, Sym)) { PostMessage(g_hwndMDIClient, WM_MDIDESTROY, (WPARAM)m_Win, 0); } } } }
ULONG DOCWIN_DATA::GetWorkspaceSize(void) { ULONG Len = EDITWIN_DATA::GetWorkspaceSize(); Len += _tcslen(m_szFoundFile) + 1; Len += _tcslen(m_szSymFile) + 1; return Len; }
PUCHAR DOCWIN_DATA::SetWorkspace(PUCHAR Data) { PTSTR Str = (PTSTR)EDITWIN_DATA::SetWorkspace(Data); _tcscpy(Str, m_szFoundFile); Str += _tcslen(m_szFoundFile) + 1; _tcscpy(Str, m_szSymFile); Str += _tcslen(m_szSymFile) + 1; 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; if (Found[0]) { if (FindDocWindowByFileName(Found, NULL, NULL) || !LoadFile(Found, Sym[0] ? Sym : NULL)) { PostMessage(g_hwndMDIClient, WM_MDIDESTROY, (WPARAM)m_Win, 0); } } return (PUCHAR)(Sym + _tcslen(Sym) + 1); } 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_pszSymFile, &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 ) /*++
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(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 );
// Restore cursor
SetCursor(hcursor);
_tcsncpy(m_szFoundFile, pszFoundFile, _tsizeof(m_szFoundFile) - 1 ); m_szFoundFile[ _tsizeof(m_szFoundFile) - 1 ] = 0; if (pszSymFile != NULL && pszSymFile[0]) { _tcsncpy(m_szSymFile, pszSymFile, _tsizeof(m_szSymFile) - 1 ); m_szSymFile[ _tsizeof(m_szSymFile) - 1 ] = 0; m_pszSymFile = m_szSymFile; } else { // No symbol file information so just use the found filename.
m_szSymFile[0] = 0; m_pszSymFile = strrchr(m_szFoundFile, '\\'); if (m_pszSymFile == NULL) { m_pszSymFile = strrchr(m_szFoundFile, '/'); if (m_pszSymFile == NULL) { m_pszSymFile = strrchr(m_szFoundFile, ':'); if (m_pszSymFile == NULL) { m_pszSymFile = m_szFoundFile - 1; } } } m_pszSymFile++; }
SetWindowText(m_Win, m_szFoundFile);
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_szFoundFile, pszFile) ) { if (ppDocWinData) { *ppDocWinData = pTmp; } if (phwnd) { *phwnd = pTmp->m_Win; } return TRUE; }
Entry = Entry->Flink; }
return FALSE; }
BOOL OpenOrActivateFile(PCSTR FoundFile, PCSTR SymFile, 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.
ActivateMDIChild(hwndDoc, UserActivated); Activated = TRUE; } } else { hwndDoc = NewDoc_CreateWindow(g_hwndMDIClient); if (hwndDoc == NULL) { return FALSE; } pDoc = GetDocWinData(hwndDoc); Assert(pDoc);
if (!pDoc->LoadFile(FoundFile, SymFile)) { DestroyWindow(pDoc->m_Win); return FALSE; }
Activated = TRUE; } // Success. Now highlight the line.
pDoc->SetCurrentLineHighlight(Line); return Activated; }
void UpdateCodeDisplay( ULONG64 Ip, PCSTR FoundFile, PCSTR SymFile, 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, Line, GetSrcMode_StatusBar() || g_DisasmActivateSource, UserActivated); } 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; } }
|