首页| 论坛| 消息
主题:Qt指定应用程序窗口采集录屏和推流/自动罗列窗口标题/实时性好延迟极低
liudianwu发表于 2025-06-10 08:31
## 一、前言说明
之前已经实现了通过ffmpeg桌面采集,也支持指定应用程序的标题来采集,但是需要用户手动输入程序窗口标题,这个就很不友好了,有几个用户知道如何才是完整的正确的标题呢,所以近期特意单独写了个类,专门用来获取当前环境中打开的应用程序标题,以便供用户直接下拉选择,obs推流工具也是这样处理的,通过向著名的软件参考和学习,不断的改进代码。在windows中,通过EnumWindows函数可以枚举所有窗口,然后在回调函数EnumWindowsProc中调用GetWindowText读取标题,发现读取出来的非常多,一大堆,大部分其实是不需要的,所以先要过滤掉不可见窗体的,也就是已经最小化的,这种窗体是无法采集的,obs也是无法采集最小化的窗体,原因未知,可能是系统的特性,不允许采集最小化窗口。后面还发现还有一些系统层的窗口也读取到了,需要用GetWindowLong函数来排除工具窗口和系统窗口,自此基本干净了,那还有没有完善的空间呢,有的,因为还有些应用程序的标题可能重复,那如何区分呢,所以还需要获取程序的完整路径及程序名称,也就是xxx.exe,下拉框中要做出对应的区分,比如obs的格式就是 xxx ,这样就一目了然,知道当前窗口属于哪个程序。

其实Qt6.6版本开始已经提供了类QWindowCapture::capturableWindows(),用来枚举所有窗口,估计后期还需要不断的完善,因为列举出来的窗口没有过滤,一堆都是工具窗口和系统窗口,要么无法采集,要么没有意义,不知道有没有谁去提点建议。好处是这个类跨平台,在其他系统都能完美的枚举窗口,可以学习其中的源码来参考。

下面是从Qt6.6中抠出来的代码

```cpp
static QString windowTitle(HWND hwnd) {
// QTBUG-114890
// TODO: investigate the case when hwnd is inner and belows to another thread.
// It might causes deadlocks in specific cases.
auto titleLength = ::GetWindowTextLengthW(hwnd);
std::wstring buffer(titleLength + 1, L'\0');
titleLength = ::GetWindowTextW(hwnd, buffer.data(), titleLength + 1);
buffer.resize(titleLength);

return QString::fromStdWString(buffer);
}

QList QWinCapturableWindows::windows() const
{
QList result;

auto windowHandler = [](HWND hwnd, LPARAM lParam) {
if (!canCaptureWindow(hwnd))
return TRUE; // Ignore window and continue enumerating

auto& windows = *reinterpret_cast(lParam);

auto windowData = std::make_unique();
windowData->id = reinterpret_cast(hwnd);
windowData->description = windowTitle(hwnd);
windows.push_back(windowData.release()->create());

return TRUE;
};

::EnumWindows(windowHandler, reinterpret_cast(&result));

return result;
}
```

## 二、效果图

## 三、相关代码
```cpp
#include "applicationwindow.h"
#include "qdebug.h"

#ifdef Q_OS_WIN
#include "windows.h"
#include "psapi.h"
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
//检查窗口是否可见或者最小化
if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) {
return TRUE;
}

//检查窗口是否有标题
int length = GetWindowTextLength(hwnd);
if (length == 0) {
return TRUE;
}

//排除工具窗口和系统窗口
LONG style = GetWindowLong(hwnd, GWL_STYLE);
LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
if (!(style & WS_OVERLAPPEDWINDOW) || (exStyle & WS_EX_TOOLWINDOW)) {
return TRUE;
}

//排除被其他窗口拥有的窗口
if (GetWindow(hwnd, GW_OWNER) != NULL) {
return TRUE;
}

//获取进程ID和名称
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (hProcess == NULL) {
return TRUE;
}

wchar_t exePath;
GetModuleFileNameEx(hProcess, NULL, exePath, MAX_PATH);
CloseHandle(hProcess);
QString name = QString::fromWCharArray(exePath);
name = name.split("\\").last();

//过滤不需要的/可以自行增加其他的
static QStringList names = QStringList() addItem(text, title);
return TRUE;
}
#endif

void ApplicationWindow::getApplicationWindow(QComboBox *cbox)<

浏览大图

浏览大图

浏览大图
下一页 (1/4)
回帖(0):

全部回帖(0)»
最新回帖
收藏本帖
发新帖