You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Delphi获取Windows系统中运行的GUI应用程序列表?

使用Delphi获取Windows系统中可捕获的GUI应用程序列表

我想要制作一个类似Discord应用屏幕共享功能的表单,点击按钮时列出电脑上所有带有可捕获窗口(GUI)的运行应用程序,但不知道如何获取该列表。

我尝试过一些零散代码但都没生效,理想中需要一个可调用的简单函数,返回包含应用名称和窗口句柄(HWnd)等信息的应用列表,目前还没写出可用的函数。


我目前尝试的代码(基于EnumWindows函数)

uses Winapi.Windows;

...

type
  TWindowObject = record
    Handle: HWnd; // 窗口句柄
    Caption: String; // 窗口标题
    ProcessID: Integer; // 所属进程ID
    IsVisible: Boolean; // 窗口是否可见
    IsEnabled: Boolean; // 窗口是否可交互
    IsIconic : Boolean; // 窗口是否最小化
    Dimensions: TRect; // 窗口尺寸
  end;

...

function GetListOfRunningGUIApps: TArray<TWindowObject>;
var
  TempResult: TArray<TWindowObject>;

  function WindowCallback(WHandle: HWnd; Parm: LPARAM): Boolean; stdcall;
  begin
    var MyWindow: TWindowObject;

    MyWindow.Handle := WHandle;

    var MyString: String;
    GetWindowText(WHandle, PChar(MyString), GetWindowTextLength(GetForegroundWindow));
    MyWindow.Caption := String(MyString);

    var ProcessID: Integer := 0;
    MyWindow.ProcessID := GetWindowThreadProcessId(WHandle,@ProcessID);

    MyWindow.IsVisible := IsWindowVisible(WHandle);

    MyWindow.IsEnabled := IsWindowEnabled(WHandle);

    MyWindow.IsIconic := IsIconic(WHandle);

    MyWindow.Dimensions := Rect(0,0,0,0);
    GetWindowRect(WHandle,MyWindow.Dimensions);

    TempResult := TempResult + TempResult;

    Result := True;
  end;
begin
  EnumWindows(@WindowCallback,LPARAM(Result));

  Result := TempResult;
end;

按钮点击事件调用代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Clear;
  var Apps := GetListOfRunningGUIApps;
  for var I in Apps do
  begin
    Memo1.Lines.Add(I.Caption);
  end;
end;

问题修正与最终实现代码

原代码存在多处WinAPI调用错误和逻辑问题,以下是修正后的完整实现:

uses Winapi.Windows, System.SysUtils;

type
  TWindowObject = record
    Handle: HWnd;
    Caption: string;
    ProcessID: Integer;
    IsVisible: Boolean;
    IsEnabled: Boolean;
    IsIconic: Boolean;
    Dimensions: TRect;
    AppName: string; // 新增:应用程序名称(从进程获取)
  end;

function GetWindowAppName(const ProcessID: DWORD): string;
var
  hProcess: THandle;
  ExePath: array[0..MAX_PATH] of Char;
begin
  Result := '';
  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, ProcessID);
  if hProcess <> 0 then
  begin
    if GetModuleFileNameEx(hProcess, 0, ExePath, MAX_PATH) > 0 then
      Result := ExtractFileName(ExePath);
    CloseHandle(hProcess);
  end;
end;

function GetListOfRunningGUIApps: TArray<TWindowObject>;
var
  TempResult: TArray<TWindowObject>;

  function WindowCallback(WHandle: HWnd; Parm: LPARAM): Boolean; stdcall;
  var
    MyWindow: TWindowObject;
    TextLen: Integer;
    ProcessID: DWORD;
    ExStyle: DWORD;
  begin
    // 过滤条件:仅保留顶层可见窗口,排除工具窗口、无标题窗口(可选)
    ExStyle := GetWindowLongPtr(WHandle, GWL_EXSTYLE);
    if (GetParent(WHandle) <> 0) or // 不是顶层窗口
       (not IsWindowVisible(WHandle)) or // 不可见
       (ExStyle and WS_EX_TOOLWINDOW <> 0) then // 工具窗口(如系统托盘弹窗)
    begin
      Result := True;
      Exit;
    end;

    // 初始化窗口对象
    MyWindow.Handle := WHandle;
    MyWindow.IsVisible := IsWindowVisible(WHandle);
    MyWindow.IsEnabled := IsWindowEnabled(WHandle);
    MyWindow.IsIconic := IsIconic(WHandle);
    GetWindowRect(WHandle, MyWindow.Dimensions);

    // 获取窗口标题
    TextLen := GetWindowTextLength(WHandle);
    if TextLen > 0 then
    begin
      SetLength(MyWindow.Caption, TextLen);
      GetWindowText(WHandle, PChar(MyWindow.Caption), TextLen + 1);
    end
    else
      MyWindow.Caption := '';

    // 获取进程ID和应用名称
    GetWindowThreadProcessId(WHandle, @ProcessID);
    MyWindow.ProcessID := ProcessID;
    MyWindow.AppName := GetWindowAppName(ProcessID);

    // 添加到结果数组
    SetLength(TempResult, Length(TempResult) + 1);
    TempResult[High(TempResult)] := MyWindow;

    Result := True; // 继续枚举下一个窗口
  end;
begin
  SetLength(TempResult, 0);
  EnumWindows(@WindowCallback, 0);
  Result := TempResult;
end;

关键修正点说明

  1. 窗口标题获取:先通过GetWindowTextLength获取当前窗口标题长度,再用SetLength分配字符串空间,避免内存错误。
  2. 进程ID获取:GetWindowThreadProcessId的返回值是线程ID,进程ID通过第二个参数输出,修正赋值逻辑。
  3. 结果数组更新:使用SetLength扩展数组并直接赋值,替代原错误的数组拼接方式。
  4. 窗口过滤:添加顶层窗口判断、可见性判断、工具窗口排除,避免枚举到系统后台或无用窗口。
  5. 应用名称获取:新增GetWindowAppName函数,通过进程ID获取可执行文件名称,补充更完整的应用信息。

使用示例(按钮点击事件)

procedure TForm1.Button1Click(Sender: TObject);
var
  Apps: TArray<TWindowObject>;
  WinObj: TWindowObject;
begin
  Memo1.Lines.Clear;
  Apps := GetListOfRunningGUIApps;
  for WinObj in Apps do
  begin
    Memo1.Lines.Add(Format('应用名称:%s | 窗口标题:%s | 句柄:%d', 
      [WinObj.AppName, WinObj.Caption, WinObj.Handle]));
  end;
end;

内容的提问来源于stack exchange,提问作者Shaun Roselt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 13:32:34