如何使用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;
关键修正点说明
- 窗口标题获取:先通过
GetWindowTextLength获取当前窗口标题长度,再用SetLength分配字符串空间,避免内存错误。 - 进程ID获取:
GetWindowThreadProcessId的返回值是线程ID,进程ID通过第二个参数输出,修正赋值逻辑。 - 结果数组更新:使用
SetLength扩展数组并直接赋值,替代原错误的数组拼接方式。 - 窗口过滤:添加顶层窗口判断、可见性判断、工具窗口排除,避免枚举到系统后台或无用窗口。
- 应用名称获取:新增
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
相关产品推荐
相关产品推荐

