代码说明:
Widget 类:
Widget(QWidget *parent = nullptr): 构造函数,设置窗口标题和大小,启动外部程序,并创建WindowChecker对象和子线程。
~Widget(): 析构函数,终止外部程序并清理资源。
onWindowChanged(HWND hwnd): 槽函数,当检测到窗口变化时调用,嵌入新的外部窗口。
embedExternalWindow(HWND hwnd, QWidget *parentWidget): 将外部窗口嵌入到Qt窗口中,设置外部窗口的父窗口和大小、位置。
WindowChecker 类:
WindowChecker(DWORD processId): 构造函数,初始化processId和currentHwnd。
~WindowChecker(): 析构函数。
checkWindows(): 检查窗口函数,调用EnumWindows枚举所有顶层窗口。
EnumWindowsProc(HWND hwnd, LPARAM lParam): 回调函数,用于枚举所有顶层窗口,检查窗口是否属于目标进程并发出信号。
QThread:
创建QThread对象并将WindowChecker对象移动到子线程。
使用QTimer定期触发子线程的窗口检查。
.pro文件
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
widget.cpp \
windowchecker.cpp
HEADERS += \
widget.h \
windowchecker.h
FORMS += \
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
win32:LIBS += -luser32
RESOURCES += \
descr.qrc
.h文件
windowchecker.h
#ifndef WINDOWCHECKER_H
#define WINDOWCHECKER_H
#include <Windows.h>
#include <QObject>
#include <QTimer>
#include <QDebug>
class WindowChecker : public QObject
{
Q_OBJECT
public:
WindowChecker(DWORD processId);
~WindowChecker();
public slots:
void checkWindows();
signals:
void windowChanged(HWND hwnd);
private:
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
DWORD processId;
HWND currentHwnd;
};
#endif // WINDOWCHECKER_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QProcess>
#include <QThread>
#include <QVector>
#include "windowchecker.h"
#include <QMetaType>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
// void embedExternalWindow(HWND hwnd, QWidget *parentWidget);
public slots:
void onWindowChanged(HWND hwnd);
private:
void embedExternalWindow(HWND hwnd, QWidget *parentWidget);
Ui::Widget *ui;
QProcess *process;
HWND currentHwnd;
QThread *checkerThread;
WindowChecker *windowChecker;
DWORD processId;
};
#endif // WIDGET_H
.cpp
windowchecker.cpp
#include "windowchecker.h"
// WindowChecker 构造函数
WindowChecker::WindowChecker(DWORD processId)
: processId(processId)
, currentHwnd(nullptr)
{
}
// WindowChecker 析构函数
WindowChecker::~WindowChecker() {}
// 回调函数,用于枚举所有顶层窗口
BOOL CALLBACK WindowChecker::EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
WindowChecker *checker = reinterpret_cast<WindowChecker*>(lParam);
DWORD windowProcessId;
GetWindowThreadProcessId(hwnd, &windowProcessId);
// 如果窗口属于目标进程,并且窗口是可见的
if (windowProcessId == checker->processId && hwnd != checker->currentHwnd && IsWindowVisible(hwnd))
{
checker->currentHwnd = hwnd;
emit checker->windowChanged(hwnd);
return FALSE; // 停止枚举窗口
}
return TRUE; // 继续枚举窗口
}
// 检查窗口函数
void WindowChecker::checkWindows()
{
EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(this));
}
whidgt.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QApplication>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
, process(nullptr)
, currentHwnd(nullptr)
, checkerThread(new QThread(this))
, windowChecker(nullptr)
, processId(0)
{
ui->setupUi(this);
setWindowTitle("Qt Main Window");
//resize(1200, 800);
// 启动外部程序
process = new QProcess(this);
// 替换为实际的外部程序路径
process->start("C:\\Users\\ROG10030\\Desktop\\NetAssist.exe");
// 获取进程ID
processId = process->processId();
qDebug() << "进程ID:\n" << processId <<"\n";
// 创建 WindowChecker 对象
windowChecker = new WindowChecker(processId);
// 将 WindowChecker 移动到子线程
windowChecker->moveToThread(checkerThread);
// 将 HWND 注册为元类型。让 Qt 的元对象系统识别并正确处理 HWND 类型的参数。
qRegisterMetaType<HWND>("HWND");
// 连接信号和槽
connect(checkerThread, &QThread::started, windowChecker, &WindowChecker::checkWindows);
connect(windowChecker, &WindowChecker::windowChanged, this, &Widget::onWindowChanged);
// 使用定时器定期触发子线程的窗口检查
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, windowChecker, &WindowChecker::checkWindows);
timer->start(1000); // 每秒检查一次
checkerThread->start();
}
/*
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置主窗口的大小
setWindowTitle("Qt Main Window");
resize(800, 600);
// 创建 QWidget 作为外部程序窗口的容器
//QWidget *container = new QWidget(this);
//container->setGeometry(10, 10, 780, 580);
// 启动外部程序
process = new QProcess(this);
process->start("D:\\Program Files (x86)\\Everything\\Everything.exe"); // 替换为实际的外部程序路径
// 使用定时器等待外部程序启动
QTimer::singleShot(400, this,[this]() {
// 外部程序窗口的句柄(需要根据实际情况获取)
HWND hwnd = FindWindow(NULL, L"Everything"); // 替换为实际窗口标题
if (hwnd) {
embedExternalWindow(hwnd, ui->widget);
} else {
qWarning() << "无法找到外部程序窗口";
}
});
}
*/
Widget::~Widget()
{
delete ui;
if (process) {
process->terminate(); // 终止外部程序
process->waitForFinished(); // 等待外部程序退出
}
checkerThread->quit();
checkerThread->wait();
delete windowChecker;
}
// 当窗口变化时的槽函数
void Widget::onWindowChanged(HWND hwnd)
{
qDebug() << "当窗口变化时的槽函数";
currentHwnd = hwnd;
embedExternalWindow(hwnd, ui->widget);
}
void Widget::embedExternalWindow(HWND hwnd, QWidget *parentWidget)
{
if (hwnd == nullptr || !IsWindow(hwnd)) {
qWarning() << "无效的外部窗口句柄\n";
return;
}
// 设置外部窗口的父窗口为 Qt 窗口
SetParent(hwnd, (HWND)parentWidget->winId());
// 设置外部窗口的大小和位置
RECT rect;
GetClientRect((HWND)parentWidget->winId(), &rect);
MoveWindow(hwnd, 0, 0, rect.right, rect.bottom, TRUE);
qDebug() << "嵌入成功";
// 使外部窗口响应 Qt 窗口的消息
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_CHILD);
// 确保外部窗口能够接收鼠标输入
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT);
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
目前上述程序调用绿色软件没有问题,调用安装程序,微信,向日葵等在有的电脑上不行,原因未知