从模块文件路径计算 window 句柄得到错误的结果

Calculating window handle from module file path gets wrong results

在 Windows 10 in Delphi 11 Alexandria 的 32 位 VCL 应用程序中,我试图从 window 获取 运行 主任务的句柄任务的模块路径:

type
  TFindWindowRec = record
  ModuleToFind: string;
  FoundHWnd: HWND;
end;

function EnumWindowsCallBack(aHandle: HWND; var FindWindowRec: TFindWindowRec): BOOL; stdcall;
const
  C_FileNameLength = 256;
var
  WinFileName: string;
  PID, hProcess: DWORD;
  Len: Byte;
begin
  Result := True;

  SetLength(WinFileName, C_FileNameLength);
  GetWindowThreadProcessId(aHandle, PID);
  hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
  Len := GetModuleFileNameEx(hProcess, 0, PChar(WinFileName), C_FileNameLength);
  if Len > 0 then
  begin
    SetLength(WinFileName, Len);
    if SameText(WinFileName, FindWindowRec.ModuleToFind) then
    begin
      Result := False;
      FindWindowRec.FoundHWnd := aHandle;
    end;
  end;
end;

var
  FindWindowRec: TFindWindowRec;

function TformMain.GetmainWindowHandleFRomProcessPath(aProcessPath: string): HWND;
begin
  Result := 0;
  CodeSite.Send('TformMain.GetmainWindowHandleFRomProcessPath: aProcessPath', aProcessPath);
  FindWindowRec.ModuleToFind := aProcessPath;
  FindWindowRec.FoundHWnd := 0;
  EnumWindows(@EnumWindowsCallback, Integer(@FindWindowRec));
  if FindWindowRec.FoundHWnd <> 0 then
  begin
    Result := FindWindowRec.FoundHWnd;
    CodeSite.Send('TformMain.GetmainWindowHandleFRomProcessPath: Result', Result);
  end;
end;

当我这样做时:

GetmainWindowHandleFRomProcessPath('c:\windows\system32\notepad.exe');

...然后我得到正确的 window 句柄。

当我这样做时:

GetmainWindowHandleFRomProcessPath('C:\Program Files (x86)\Embarcadero\Studio.0\bin\bds.exe');

... 然后我得到一个错误的(不存在的)window 句柄!

为什么会这样?如何获得正确的 window 句柄?

与 Remy 和 Andreas 的讨论使我得到了这个成功的工作答案:

type
  TFindWindowRec = record
  ModuleToFind: string;
  FoundHWnd: HWND;
end;

// The `RzShellUtils` unit is from Ray Konopka's Signature Library available from GetIt:
function PathsAreSamePIDL(const Path1, Path2: string): Boolean;
begin
  var AIL1: PItemIdList;
  var AIL2: PItemIdList;
  RzShellUtils.ShellGetIdListFromPath(Path1, AIL1);
  RzShellUtils.ShellGetIdListFromPath(Path2, AIL2);
  var CompResult:= RzShellUtils.CompareAbsIdLists(AIL1, AIL2);
  Result := CompResult = 0;
end;

function EnumWindowsCallBack(aHandle: HWND; var FindWindowRec: TFindWindowRec): BOOL; stdcall;
const
  C_FileNameLength = MAX_PATH;
  PROCESS_QUERY_LIMITED_INFORMATION = 00;
var
  WinFileName: string;
  PID, hProcess: DWORD;
  Len: Byte;
begin
  Result := True;

  SetLength(WinFileName, C_FileNameLength);
  GetWindowThreadProcessId(aHandle, PID);
  hProcess := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, PID);
  Len := GetModuleFileNameEx(hProcess, 0, PChar(WinFileName), C_FileNameLength);
  CloseHandle(hProcess);
  if Len > 0 then
  begin
    SetLength(WinFileName, Len);
    //if SameText(WinFileName, FindWindowRec.ModuleToFind) then
    if PathsAreSamePIDL(WinFileName, FindWindowRec.ModuleToFind) then
    begin
      var IsVisible := IsWindowVisible(aHandle);
      if not IsVisible then EXIT;
      var IsOwned := GetWindow(aHandle, GW_OWNER) <> 0;
      if IsOwned then EXIT;
      var IsAppWindow := GetWindowLongPtr(aHandle, GWL_EXSTYLE) and WS_EX_APPWINDOW <> 0;
      if not IsAppWindow then EXIT;

      Result := False;
      FindWindowRec.FoundHWnd := aHandle;
    end;
  end;
end;

function TformMain.GetMainWindowHandleFromProcessPath(aProcessPath: string): HWND;
var
  FindWindowRec: TFindWindowRec;
begin
  Result := 0;
  FindWindowRec.ModuleToFind := aProcessPath;
  FindWindowRec.FoundHWnd := 0;
  EnumWindows(@EnumWindowsCallback, LPARAM(@FindWindowRec));
  if FindWindowRec.FoundHWnd <> 0 then
  begin
    Result := FindWindowRec.FoundHWnd;
  end;
end;

我不明白为什么把讨论移到另一个页面的人删除了最新的评论。那些被删的评论有什么禁忌吗?

再次感谢 Remy 和 Andreas!