如何使用 EnumWindows 回调函数?

How to use the EnumWindows call back function?

我想要一个简洁的(封闭且独立的)函数(我们称它为 GetDesktopHandle),returns 桌面句柄 window。我使用下面的代码。但它只适用于 DeskHandle 是一个全局变量。

如何去掉这个全局变量?如果我把它放在本地,当我尝试 DeskHandle := hChild

时,我会在 getDesktopWnd 中得到一个 AV
VAR DeskHandle : HWND;

function GetDesktopHandle: HWND;

  function getDesktopWnd (Handle: HWND; NotUsed: Longint): bool; stdcall;    { Callback function }
  VAR hChild : HWND;
  begin
   if handle <> 0 then
    begin
      hChild := FindWindowEx(handle, 0, 'SHELLDLL_DefView', nil);
      if hChild <> 0 then
       begin
        hChild := FindWindowEx(hChild, 0, 'SysListView32', nil);
        if hChild <> 0
        then DeskHandle := hChild;
       end;
    end;
   Result:= TRUE;
  end;

begin
 DeskHandle := 0;
 EnumWindows(@getDesktopWnd, 0);
 Result:= DeskHandle;
end;

主要问题 是:我可以将此代码编写为单个函数或至少,我可以去掉 external/global var 吗?

可能的解决方案:
文档说第二个参数只是一个IN参数。

lParam [in] Type: LPARAM An application-defined value to be passed to the callback function.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633497%28v=vs.85%29.aspx

用它传回结果会不会出错?

局部函数不能用作回调。如果您没有使用 @ 运算符来传递您的函数,编译器会告诉您这一点。 (使用运算符将​​参数变成普通的无类型指针,因此编译器无法再检查。)

你必须让你的回调成为一个独立的函数。

要在回调和调用者之间传递数据,请使用您当前命名为 NotUsed 的第二个参数。例如,您可以将指针传递给句柄变量,然后回调可以取消引用指向 return 结果的指针。

type
  TMyData = record
    Handle: HWND;
    Pid: DWORD;
    Caption: String;
    ClassName: String;
  end;
  PMyData = ^TMyData;

function GetWindowClass(const Handle: HWND): String;
begin
  SetLength(Result, MAX_PATH);
  SetLength(Result, GetClassName(Handle, PChar(Result), Length(Result)));
end;

function GetWindowCaption(const Handle: HWND): String;
 begin
  SetLength(Result, MAX_PATH);
  SetLength(Result, GetWindowText(Handle, PChar(Result), Length(Result)));
end;

function EnumChildWindowsProc(Handle: THandle; MyData: PMyData): BOOL; stdcall;
var
  ClassName: String;
  Caption: String;
  Pid: DWORD;
begin
  ClassName := GetWindowClass(Handle);
  Caption := GetWindowCaption(Handle);

  Result := (ClassName = 'SysListView32') and (Caption = 'FolderView');
  if Result then
  begin
    MyData.Handle := Handle;
    GetWindowThreadProcessId(Handle, MyData.Pid);
    MyData.Caption := Caption;
    MyData.ClassName := ClassName;
  end;

  // To continue enumeration, the callback function must return TRUE;
  // to stop enumeration, it must return FALSE
  Result := not Result;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyData: TMyData;
begin
  ZeroMemory(@MyData, SizeOf(MyData));
  EnumChildWindows(GetDesktopWindow, @EnumChildWindowsProc, NativeInt(@MyData));
  if MyData.Handle > 0 then
  begin
     ShowMessageFmt('Found Window in Pid %d', [MyData.Pid]);
  end
  else begin
    ShowMessage('Window not found!');
  end;
end;