Inno-Setup-issrc/Projects/Src/Setup.DebugClient.pas

309 lines
11 KiB
ObjectPascal
Raw Permalink Normal View History

unit Setup.DebugClient;
2011-10-06 20:53:09 +02:00
{
Inno Setup
2024-03-27 17:03:31 +01:00
Copyright (C) 1997-2024 Jordan Russell
2011-10-06 20:53:09 +02:00
Portions by Martijn Laan
For conditions of distribution and use, see LICENSE.TXT.
2024-08-04 10:51:19 +02:00
Debug client functions (client=Setup, server=debugger/IDE)
2011-10-06 20:53:09 +02:00
}
interface
uses
Windows, SysUtils, Messages, Shared.DebugStruct;
2011-10-06 20:53:09 +02:00
var
Debugging: Boolean;
DebugClientCompiledCodeText: AnsiString;
DebugClientCompiledCodeDebugInfo: AnsiString;
type
TDebugNotifyGetCallStack = function(var CallStackCount: Cardinal): String of object;
2011-10-06 20:53:09 +02:00
function DebugNotify(Kind: TDebugEntryKind; Index: Integer;
var ADebugContinueStepOver: Boolean; const GetCallStack: TDebugNotifyGetCallStack = nil): Boolean;
2011-10-06 20:53:09 +02:00
procedure DebugNotifyException(Exception: String; Kind: TDebugEntryKind; Index: Integer);
function DebugNotifyIntermediate(Kind: TDebugEntryKind; Index: Integer;
var ADebugContinueStepOver: Boolean): Boolean;
procedure DebugNotifyLogMessage(const Msg: String);
procedure DebugNotifyTempDir(const Dir: String);
procedure DebugNotifyUninstExe(UninstExe: String);
procedure EndDebug;
2024-08-04 10:51:19 +02:00
procedure SetDebugServerWnd(Wnd: HWND; WantCodeText: Boolean);
2011-10-06 20:53:09 +02:00
implementation
uses
Forms, Classes, Shared.CommonFunc, Shared.Struct, Setup.InstFunc, Setup.MainFunc;
2011-10-06 20:53:09 +02:00
type
TDummyClass = class
private
class procedure DebugClientWndProc(var Message: TMessage);
end;
var
2024-08-04 10:51:19 +02:00
DebugServerWnd: HWND;
2011-10-06 20:53:09 +02:00
DebugClientWnd: HWND;
DebugContinue: Boolean;
DebugContinueStepOver: Boolean;
2024-08-04 10:51:19 +02:00
procedure SetDebugServerWnd(Wnd: HWND; WantCodeText: Boolean);
2011-10-06 20:53:09 +02:00
var
DebuggerVersion: Cardinal;
I: Integer;
begin
2024-08-04 10:51:19 +02:00
{ First, verify that the debugger/IDE is the same version as Setup.
2011-10-06 20:53:09 +02:00
A mismatch is possible when debugging an uninstaller if the uninstaller
EXE was created by an installer built with a later version of IS. We can't
continue in such a case because the debugger would send over updated
"compiled code text" that is incompatible with this version of Setup. }
DebuggerVersion := SendMessage(Wnd, WM_Debugger_QueryVersion, 0, 0);
if DebuggerVersion <> SetupBinVersion then
raise Exception.CreateFmt('Cannot debug. Debugger version ($%.8x) does ' +
'not match Setup version ($%.8x)', [DebuggerVersion, SetupBinVersion]);
Debugging := True;
2024-08-04 10:51:19 +02:00
DebugServerWnd := Wnd;
2011-10-06 20:53:09 +02:00
DebugClientWnd := AllocateHWnd(TDummyClass.DebugClientWndProc);
if DebugClientWnd = 0 then
InternalError('Failed to create DebugClientWnd');
2024-03-27 17:03:31 +01:00
{ Unprivileged processes can't send messages to elevated processes by default.
Allow the debugger (which normally runs unprivileged) to send messages to us. }
2011-10-06 20:53:09 +02:00
for I := Low(DebugClientMessages) to High(DebugClientMessages) do
AddToWindowMessageFilterEx(DebugClientWnd, DebugClientMessages[I]);
2024-08-04 10:51:19 +02:00
SendMessage(DebugServerWnd, WM_Debugger_Hello, WPARAM(DebugClientWnd), LPARAM(WantCodeText));
2011-10-06 20:53:09 +02:00
end;
procedure EndDebug;
begin
Debugging := False;
2024-08-04 10:51:19 +02:00
if DebugServerWnd <> 0 then begin
SendMessage(DebugServerWnd, WM_Debugger_Goodbye, 0, 0);
DebugServerWnd := 0;
2011-10-06 20:53:09 +02:00
end;
if DebugClientWnd <> 0 then begin
DeallocateHWnd(DebugClientWnd);
DebugClientWnd := 0;
end;
end;
function InternalDebugNotify(DebuggerMsg: UINT; Kind: TDebugEntryKind;
Index: Integer; var ADebugContinueStepOver: Boolean;
const GetCallStack: TDebugNotifyGetCallStack = nil; const GetCallStackData: Pointer = nil): Boolean;
2011-10-06 20:53:09 +02:00
{ Returns True if the debugger paused. ADebugContinueStepOver is set to True
if the debugger paused and the user resumed via Step Over, False otherwise. }
var
SaveAppTitle, CallStack: String;
2011-10-06 20:53:09 +02:00
WindowList: Pointer;
Msg: TMsg;
TopWindow: HWND;
CallStackCount: Cardinal;
2011-10-06 20:53:09 +02:00
begin
Result := False;
ADebugContinueStepOver := False;
if not Debugging then
Exit;
DebugContinue := False;
2024-08-04 10:51:19 +02:00
if SendMessage(DebugServerWnd, DebuggerMsg, Ord(Kind), Index) = 0 then begin
2011-10-06 20:53:09 +02:00
{ Don't pause }
Exit;
end;
if Assigned(GetCallStack) then begin
CallStack := GetCallStack(CallStackCount);
2024-08-04 10:51:19 +02:00
SendMessage(DebugServerWnd, WM_Debugger_CallStackCount, CallStackCount, 0);
SendCopyDataMessageStr(DebugServerWnd, DebugClientWnd, CD_Debugger_CallStackW, CallStack);
end;
2011-10-06 20:53:09 +02:00
Result := True;
{ Wait until we get clearance to continue }
SaveAppTitle := Application.Title;
WindowList := DisableTaskWindows(0);
try
Application.Title := '[Paused] ' + SaveAppTitle;
while not DebugContinue do begin
case Integer(GetMessage(Msg, 0, 0, 0)) of
-1: Break; { if GetMessage failed }
0: begin
{ Repost WM_QUIT messages }
PostQuitMessage(Msg.WParam);
Break;
end;
end;
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
ADebugContinueStepOver := DebugContinueStepOver;
finally
EnableTaskWindows(WindowList);
Application.Title := SaveAppTitle;
end;
{ Bring us back to the foreground, unless we've been detached }
if Debugging then begin
TopWindow := GetThreadTopWindow;
if TopWindow = 0 then
TopWindow := Application.Handle;
2011-10-06 20:53:09 +02:00
if TopWindow <> 0 then begin
{ First ask the debugger to call SetForegroundWindow() on our window. If
we don't do this then Windows (98/2000+) will prevent our window from
becoming activated if the debugger is currently in the foreground. }
2024-08-04 10:51:19 +02:00
SendMessage(DebugServerWnd, WM_Debugger_SetForegroundWindow, WPARAM(TopWindow), 0);
2011-10-06 20:53:09 +02:00
{ Now call SetForegroundWindow() ourself. Why? When a remote thread
calls SetForegroundWindow(), the request is queued; the window doesn't
actually become active until the next time the window's thread checks
the message queue. This call causes the window to become active
immediately. }
SetForegroundWindow(TopWindow);
end;
end;
end;
function DebugNotify(Kind: TDebugEntryKind; Index: Integer;
var ADebugContinueStepOver: Boolean;
const GetCallStack: TDebugNotifyGetCallStack = nil): Boolean;
2011-10-06 20:53:09 +02:00
begin
Result := InternalDebugNotify(WM_Debugger_Stepped, Kind, Index,
ADebugContinueStepOver, GetCallStack);
2011-10-06 20:53:09 +02:00
end;
function DebugNotifyIntermediate(Kind: TDebugEntryKind; Index: Integer;
var ADebugContinueStepOver: Boolean): Boolean;
begin
Result := InternalDebugNotify(WM_Debugger_SteppedIntermediate, Kind, Index,
ADebugContinueStepOver, nil);
2011-10-06 20:53:09 +02:00
end;
procedure DebugNotifyException(Exception: String; Kind: TDebugEntryKind; Index: Integer);
var
B: Boolean;
begin
2024-08-04 10:51:19 +02:00
SendCopyDataMessageStr(DebugServerWnd, DebugClientWnd, CD_Debugger_ExceptionW,
2011-10-06 20:53:09 +02:00
Exception);
InternalDebugNotify(WM_Debugger_Exception, Kind, Index, B);
end;
procedure DebugNotifyTempDir(const Dir: String);
begin
2024-08-04 10:51:19 +02:00
SendCopyDataMessageStr(DebugServerWnd, DebugClientWnd, CD_Debugger_TempDirW, Dir);
2011-10-06 20:53:09 +02:00
end;
procedure DebugNotifyUninstExe(UninstExe: String);
begin
2024-08-04 10:51:19 +02:00
SendCopyDataMessageStr(DebugServerWnd, DebugClientWnd, CD_Debugger_UninstExeW, UninstExe);
2011-10-06 20:53:09 +02:00
end;
procedure DebugNotifyLogMessage(const Msg: String);
begin
2024-08-04 10:51:19 +02:00
SendCopyDataMessageStr(DebugServerWnd, DebugClientWnd, CD_Debugger_LogMessageW, Msg);
2011-10-06 20:53:09 +02:00
end;
class procedure TDummyClass.DebugClientWndProc(var Message: TMessage);
var
VariableDebugEntry: TVariableDebugEntry;
EvaluateExp, EvaluateResult: String;
begin
try
case Message.Msg of
WM_DebugClient_Detach: begin
Debugging := False;
2024-08-04 10:51:19 +02:00
DebugServerWnd := 0;
2011-10-06 20:53:09 +02:00
{ If it's paused, force it to continue }
DebugContinue := True;
DebugContinueStepOver := False;
{ Make the GetMessage call in DebugNotify return immediately }
PostMessage(0, 0, 0, 0);
end;
WM_DebugClient_Continue: begin
DebugContinue := True;
DebugContinueStepOver := Message.wParam = 1;
{ Make the GetMessage call in DebugNotify return immediately }
PostMessage(0, 0, 0, 0);
end;
WM_DebugClient_SetForegroundWindow: begin
SetForegroundWindow(HWND(Message.WParam));
end;
WM_COPYDATA: begin
case TWMCopyData(Message).CopyDataStruct.dwData of
CD_DebugClient_EvaluateConstantW: begin
try
SetString(EvaluateExp, PChar(TWMCopyData(Message).CopyDataStruct.lpData),
TWMCopyData(Message).CopyDataStruct.cbData div SizeOf(Char));
try
Inc(DisableCodeConsts);
try
EvaluateResult := ExpandConst(EvaluateExp);
finally
Dec(DisableCodeConsts);
end;
Message.Result := 1;
except
EvaluateResult := GetExceptMessage;
Message.Result := 2;
end;
2024-08-04 10:51:19 +02:00
SendCopyDataMessageStr(DebugServerWnd, DebugClientWnd, CD_Debugger_ReplyW,
2011-10-06 20:53:09 +02:00
EvaluateResult);
except
{ don't propagate exceptions }
2011-10-06 20:53:09 +02:00
end;
end;
CD_DebugClient_EvaluateVariableEntry: begin
try
Move(TWMCopyData(Message).CopyDataStruct.lpData^, VariableDebugEntry, SizeOf(VariableDebugEntry));
try
if CodeRunner = nil then
raise Exception.Create('Cannot evaluate variable because [Code] isn''t running yet');
EvaluateResult := CodeRunner.EvaluateUsedVariable(VariableDebugEntry.Param1,
VariableDebugEntry.Param2, VariableDebugEntry.Param3, VariableDebugEntry.Param4);
Message.Result := 1;
except
EvaluateResult := GetExceptMessage;
Message.Result := 2;
end;
2024-08-04 10:51:19 +02:00
SendCopyDataMessageStr(DebugServerWnd, DebugClientWnd, CD_Debugger_ReplyW,
2011-10-06 20:53:09 +02:00
EvaluateResult);
except
{ don't propagate exceptions }
2011-10-06 20:53:09 +02:00
end;
end;
CD_DebugClient_CompiledCodeTextA: begin
try
DebugClientCompiledCodeText := '';
SetString(DebugClientCompiledCodeText, PAnsiChar(TWMCopyData(Message).CopyDataStruct.lpData),
TWMCopyData(Message).CopyDataStruct.cbData div SizeOf(AnsiChar));
Message.Result := 1;
except
{ don't propagate exceptions }
2011-10-06 20:53:09 +02:00
end;
end;
CD_DebugClient_CompiledCodeDebugInfoA: begin
try
DebugClientCompiledCodeDebugInfo := '';
SetString(DebugClientCompiledCodeDebugInfo, PAnsiChar(TWMCopyData(Message).CopyDataStruct.lpData),
TWMCopyData(Message).CopyDataStruct.cbData div SizeOf(AnsiChar));
Message.Result := 1;
except
{ don't propagate exceptions }
2011-10-06 20:53:09 +02:00
end;
end;
end;
end;
else
with Message do
Result := DefWindowProc(DebugClientWnd, Msg, WParam, LParam);
end;
except
Application.HandleException(nil);
end
end;
end.