2024-08-03 17:15:13 +02:00
|
|
|
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
|
2024-08-03 21:19:08 +02:00
|
|
|
Windows, SysUtils, Messages, Shared.DebugStruct;
|
2011-10-06 20:53:09 +02:00
|
|
|
|
|
|
|
var
|
|
|
|
Debugging: Boolean;
|
|
|
|
DebugClientCompiledCodeText: AnsiString;
|
|
|
|
DebugClientCompiledCodeDebugInfo: AnsiString;
|
|
|
|
|
2019-08-10 22:29:47 +02:00
|
|
|
type
|
|
|
|
TDebugNotifyGetCallStack = function(var CallStackCount: Cardinal): String of object;
|
|
|
|
|
2011-10-06 20:53:09 +02:00
|
|
|
function DebugNotify(Kind: TDebugEntryKind; Index: Integer;
|
2019-08-10 22:29:47 +02:00
|
|
|
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
|
2024-08-09 08:20:49 +02:00
|
|
|
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;
|
2019-08-10 22:29:47 +02:00
|
|
|
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
|
2019-08-10 22:29:47 +02:00
|
|
|
SaveAppTitle, CallStack: String;
|
2011-10-06 20:53:09 +02:00
|
|
|
WindowList: Pointer;
|
|
|
|
Msg: TMsg;
|
|
|
|
TopWindow: HWND;
|
2019-08-10 22:29:47 +02:00
|
|
|
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;
|
2019-08-10 22:29:47 +02:00
|
|
|
|
|
|
|
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);
|
2019-08-10 22:29:47 +02:00
|
|
|
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;
|
2024-12-30 02:24:31 -06:00
|
|
|
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;
|
2019-08-10 22:29:47 +02:00
|
|
|
var ADebugContinueStepOver: Boolean;
|
|
|
|
const GetCallStack: TDebugNotifyGetCallStack = nil): Boolean;
|
2011-10-06 20:53:09 +02:00
|
|
|
begin
|
|
|
|
Result := InternalDebugNotify(WM_Debugger_Stepped, Kind, Index,
|
2019-08-10 22:29:47 +02:00
|
|
|
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,
|
2019-08-10 22:29:47 +02:00
|
|
|
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
|
2020-08-10 21:00:18 +02:00
|
|
|
{ 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
|
2020-08-10 21:00:18 +02:00
|
|
|
{ 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
|
2020-08-10 21:00:18 +02:00
|
|
|
{ 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
|
2020-08-10 21:00:18 +02:00
|
|
|
{ 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.
|