Search code examples
windowsapidelphiwinapidelphi-xe7

How to use the EnumWindows call back function?


I would like to have a single neat (close and self contained) function (let's call it GetDesktopHandle) that returns a handle to the Desktop window. I use the code below. But it only works in the DeskHandle is a global var.

How to get rid of this global variable? If I make it local I get an AV in getDesktopWnd when I try to DeskHandle := hChild

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;

The main question is: can I write this code as a single function or AT LEAST, can I get rid of the external/global var?

Possible solution:
The documentation says that the second parameter is only a IN parameter.

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

Would it be wrong to use it to pass the result back?


Solution

  • 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;