The root cause of your crash is that your are using an inner function as the EnumChildWindows()
callback and it is referencing a parameter from its outer function, which will not work (and why it does work when you comment out the access of that parameter). The call stack frame is not what EnumChildWindows()
is expecting. You need to make the inner function be a standalone function instead.
With that said, there is another bug in your code. Even if the above worked, your code would still fail because you are storing child Control IDs in your TStringList
but then using them as if they were HWND
values instead. They are not!
Try something more like this:
uses
..., System.Generics.Collections;
{ *
* AControl: Control handle determined by Spy++ (e.g. 0037064A)
* ANewText: Text to assign to control
* AWinTitle: Window Title/Caption
* }
type
THWndList = TList<HWND>;
function EnumChildren(AWindowHandle: HWND; AParam: LPARAM): BOOL; stdcall;
begin
THWndList(AParam).Add(AWindowHandle);
Result := TRUE;
end;
function TryStrToHWnd(const AStr: String; var Wnd: HWND): Boolean;
begin
{$IFDEF WIN64}
Result := TryStrToInt64(AStr, Int64(Wnd));
{$ELSE}
Result := TryStrToInt(AStr, Integer(Wnd));
{$ENDIF}
end;
function ControlSetText(const AControl, ANewText, AWinTitle: String): Boolean;
var
_MainWindowHandle: HWND;
_WindowControlList: THWndList;
i: integer;
_ControlHandle: HWND;
EnumInfo: TEnumInfo;
begin
Result := False;
_MainWindowHandle := FindWindow(nil, PChar(AWinTitle));
if _MainWindowHandle <> 0 then
begin
_WindowControlList := THWndList;
try
if TryStrToHWnd('$' + Trim(AControl), _ControlHandle) then
try
EnumChildWindows(_MainWindowHandle, @EnumChildren, LPARAM(_WindowControlList));
for i := 0 to _WindowControlList.Count - 1 do
begin
if (_WindowControlList[i] = _ControlHandle) then
begin
Result := SendMessage(_WindowControlList[i], WM_SETTEXT, 0, LPARAM(PChar(ANewText))) = 1;
Break;
end;
end;
except
on E: Exception do
MessageDlg(E.Message, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
end;
finally
FreeAndNil(_WindowControlList);
end;
end;
end;
Alternatively:
{ *
* AControl: Control handle determined by Spy++ (e.g. 0037064A)
* ANewText: Text to assign to control
* AWinTitle: Window Title/Caption
* }
type
PEnumInfo = ^TEnumInfo;
TEnumInfo = record
Control: HWND;
Found: Boolean;
end;
function EnumChildren(AWindowHandle: HWND; AParam: LPARAM): BOOL; stdcall;
begin
PEnumInfo(AParam).Found := (AWindowHandle = PEnumInfo(AParam).Control);
Result := not PEnumInfo(AParam).Found;
end;
function TryStrToHWnd(const AStr: String; var Wnd: HWND): Boolean;
begin
{$IFDEF WIN64}
Result := TryStrToInt64(AStr, Int64(Wnd));
{$ELSE}
Result := TryStrToInt(AStr, Integer(Wnd));
{$ENDIF}
end;
function ControlSetText(const AControl, ANewText, AWinTitle: String): Boolean;
var
_MainWindowHandle: HWND;
_ControlHandle: HWND;
EnumInfo: TEnumInfo;
begin
Result := False;
_MainWindowHandle := FindWindow(nil, PChar(AWinTitle));
if _MainWindowHandle <> 0 then
begin
if TryStrToHWnd('$' + Trim(AControl), _ControlHandle) then
try
EnumInfo.Control := _ControlHandle;
EnumInfo.Found := False;
EnumChildWindows(_MainWindowHandle, @EnumChildren, LPARAM(@EnumInfo));
if EnumInfo.Found then
begin
Result := SendMessage(_ControlHandle, WM_SETTEXT, 0, LPARAM(PChar(ANewText))) = 1;
end;
except
on E: Exception do
MessageDlg(E.Message, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
end;
end;
end;
Or just get rid of EnumChilWindows()
and let Windows validate the HWND
you try to send to:
{ *
* AControl: Control handle determined by Spy++ (e.g. 0037064A)
* ANewText: Text to assign to control
* }
function TryStrToHWnd(const AStr: String; var Wnd: HWND): Boolean;
begin
{$IFDEF WIN64}
Result := TryStrToInt64(AStr, Int64(Wnd));
{$ELSE}
Result := TryStrToInt(AStr, Integer(Wnd));
{$ENDIF}
end;
function ControlSetText(const AControl, ANewText: String): Boolean;
var
_ControlHandle: HWND;
begin
Result := TryStrToHWnd('$' + Trim(AControl), _ControlHandle) and
(SendMessage(_ControlHandle, WM_SETTEXT, 0, LPARAM(PChar(ANewText))) = 1);
end;