Search code examples
delphiwinapidelphi-xe3win64

EnumWindows behaving oddly in 64-bit Delphi


I am working in Delphi XE3, on a 64-bit Windows 8 machine, building 32-bit and 64-bit applications.

I am using EnumWindows to find out the Windows Handle of the main window of a process given its Process ID. The code uses the LPARAM parameter in the call back routine to pass a pointer to a record.

The code I am using has worked fine for a 32-bit build.

It is failing when I compile and run the 64-bit build. The problem occurs because the LPARAM value appears to be passed in the Wnd parameter. The Param value is always set to $FFFF ... in other words I don't get passed the HWND value at all ... so it's not as through the parameters are just swapped around.

type
  PEnumInfo = ^TEnumInfo;
  TEnumInfo = record
    ProcessID : DWORD;
    HWND      : THandle;
  end;

function EnumWindowsProc(Wnd: HWND;  Param : LPARAM): Bool; stdcall;
    var
      PID : DWORD;
      PEI : PEnumInfo;
    begin
// in 32-bit Param matches the address of the param that is passed
// in 64-bit Param is set to $FFFF - however Wnd = the expected address
      ShowMessage('PEI = '+IntToStr(Param));

      PEI := PEnumInfo(Param);
      GetWindowThreadProcessID(Wnd, @PID);

// the code fails at this next line in 64-bit build because PEI = $FFFF rather than the actual pointer passed
      Result := (PID <> PEI^.ProcessID) or
              (not IsWindowVisible(WND)) or
              (not IsWindowEnabled(WND));

      if not Result then PEI^.HWND := WND; //break on return FALSE
    end;


    function FindMainWindow(PID: DWORD): DWORD;
    var
      EI : TEnumInfo;
    begin
      EI.ProcessID := PID;
      EI.HWND := 0;
      ShowMessage('PEI = '+IntToStr(LPARAM(@EI)));
      EnumWindows(@EnumWindowsProc, LPARAM(@EI));
      Result := EI.HWND;
    end;

Is the Win64 calling convention different? Or am I making some other fundamental mistake?

Any help or ideas gratefully accepted.


Solution

  • The code in your question works fine. Your declaration of EnumWindowsProc is correct. The parameters and return value have the correct type. The calling convention is correct, although this does not actually matter for x64 Windows which only has one calling convention.

    If you build a simple application using the code from your question, you will find that it behaves correctly and enumerates windows correctly.

    The problem is surely that your actual code differs from the code that you have shown. My guess is that in your actual code, EnumWindowsProc is a nested function: Why cannot take address to a nested local function in 64 bit Delphi? But that is just a guess. I don't know what your real code looks like. I do know that the code in the question works correctly.

    One other comment is that you erroneously declare the HWND member to be of type THandle. This won't affect your program's correctness, but it is semantically wrong. I would declare that type like this:

    type
      TEnumInfo = record
        PID: DWORD;
        Wnd: HWND;
      end;