C++ Qt window API学习--窗口操作

42 阅读3分钟

一、前言

        获取程序窗口来管理该程序,例如微信、QQ。我想的是首先要能够操作窗口,然后再进行管理,例如预定时间自动发消息,自动回复等等,或者游戏中自动采集资源。然后决定使用window API来实现这些功能。

        每个窗口都有一个句柄,操作窗口就需要获取这个句柄(HWND),获取后就可以进行关闭、最小化、打开、移动等操作了。获取句柄需要先获取窗口名称或进程,建议是得到窗口名称然后来获取句柄。

        API 头文件:windows.h

二、获取窗口名称

        调用EnumWindows()来获取所有窗口的名称,调用时名称将会传入其对应的回调函数EnumWindowsProc()中,所以我们这里先实现回调函数在其中使用一个列表来接收EnumWindows()枚举出的所有窗口名称。实现方法如下:

//.h文件中:
#include <windows.h>  //Windows API

//使用一个全局变量来接收窗口名称
extern QStringList windowClassNames;
//EnumWindows 函数枚举所有顶层窗口,并为每个窗口调用 EnumWindowsProc 回调函数
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);

//.cpp文件中:
QStringList windowClassNames;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
    char className[256];
    if (GetClassNameA(hwnd, className, sizeof(className))) {
        windowClassNames << QString::fromLocal8Bit(className);
    }
    return TRUE;
}

//调用时,先封装一个函数来更新窗口列表:
QStringList getAllWindowClassNames()
{
    windowClassNames.clear();  // 清除之前的内容
    EnumWindows(EnumWindowsProc, 0);  // 枚举所有顶层窗口
    return windowClassNames;
}
QStringList windowClassNamesList = getAllWindowClassNames();

三、获取窗口句柄

        使用FindWindowA ()/ FindWindowW()来获取窗口句柄,两个函数功能相同,参数含义也相同,代码如下:

//根据窗口标题查找
HWND hwnd = FindWindowA(NULL, "无标题 - 记事本");
HWND hwnd = FindWindowW(NULL, "无标题 - 记事本");

//根据窗口类名查找
HWND hwnd = FindWindowA("ContactManagerWindow", NULL);
HWND hwnd = FindWindowW("ContactManagerWindow", NULL);

//通过判断返回是否是NULL来确认是否找到
if(!hwnd){
    qDebug() << "通讯录管理窗口 未找到!";
}

四、移动窗口位置和调整大小

        使用SetWindowPos来设定窗口左上角的坐标,该坐标的值和屏幕分辨率相关。

//函数一共7个参数
//依次为:句柄、显示层级、坐标X、坐标Y、宽、高、状态

//调用示例:
//保证窗口显示
ShowWindow(hWnd, SW_RESTORE);
//HWND_TOP置顶显示,SWP_SHOWWINDOW显示
SetWindowPos(hwnd, HWND_TOP, 0, 0, 1000, 700, SWP_SHOWWINDOW);

五、打开和关闭窗口

打开窗口这里分两种情况
1、程序在后台运行:使用ShowWindow(HWND, SW_RESTORE)或SetWindowPos(HWND, HWND_TOP, X, Y, CX, CY, SWP_SHOWWINDOW)显示。
2、程序没有打开:使用CMD命令行直接运行程序:

//使用CMD命令行直接运行程序
    QString command = "start \"\" \"" + programPath + "\"";
    std::string commandStr = command.toStdString();

    int result = std::system(commandStr.c_str());

关闭窗口可以使用SendMessage()向窗口发送事件;也可以用SetWindowPos()直接关闭,但是有负面影响。代码如下:

    //优先发送关闭事件,让程序自动关闭
    if (SendMessage(hWnd, WM_CLOSE, 0, 0)) {
        qDebug() << "Window close message sent successfully." << getWindowTitle(hWnd) << GetWindowClassName(hWnd);
    } else {
        qDebug() << "Failed to send window close message. Error:" << GetLastError();
        //补救措施,直接关闭
        SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_HIDEWINDOW);
    }

六、已知句柄获取串口标题或类名

        如果需要确定该句柄是否是目标窗口,直接输出其标题和类名是一个不错的方法,这里都转为Qt的QString类型输出:

QString getWindowTitle(HWND hWnd)
{
    // 获取窗口标题长度
    int length = GetWindowTextLengthW(hWnd);
    if (length == 0) {
        return QString();
    }

    // 创建一个缓冲区来存储窗口标题
    wchar_t *buffer = new wchar_t[length + 1];
    GetWindowTextW(hWnd, buffer, length + 1);

    // 将缓冲区内容转换为 QString
    QString windowTitle = QString::fromWCharArray(buffer);
    delete[] buffer;

    return windowTitle;
}

QString GetWindowClassName(HWND hWnd)
{
    const int bufferSize = 256;
    wchar_t className[bufferSize];

    // 获取窗口类名
    int result = GetClassNameW(hWnd, className, bufferSize);
    if (result == 0) {
        std::cerr << "Failed to get window class name." << std::endl;
        return QString();
    }

    // 将 wchar_t 转换为 QString
    return QString::fromWCharArray(className);
}