zoukankan      html  css  js  c++  java
  • Qt实战11.进程窗口集成之假装写了个第三方软件

    1 需求描述

    有这样一种情况,明明人家已经把软件实现好了,自己又想将其集成到自己开发的软件中,如果有源码,一般做法是将源码拿过来自己改吧改吧最后再编译,但是如果没有源码呢?只有可执行程序咋办?岂不是只能干瞪眼,其实,在windows下Qt提供了将进程窗口集成到本地应用的方法,而且很简单。

    2 设计思路

    三步走:

    • 获取窗口句柄WId
    • 根据WId创建QWindow
    • 调用QWidget::createWindowContainer,将QWindow参数传入即可

    2.1 动态获取WId

    1. 在windows下每个窗口都有窗口句柄的概念,但是程序每次运行窗口句柄WId都会改变,为了使WId可控,这里使用win函数获取:
    FindWindowA(
        _In_opt_ LPCSTR lpClassName,
        _In_opt_ LPCSTR lpWindowName);
    

    这里窗口类名lpClassName、窗口标题lpWindowName是不变的,这样每次都可以精准定位窗口了。

    1. 获取窗口类名和标题
      获取窗口信息的工具:ViewWizard 2.72-句柄查看精灵.rar
      使用也很简单,将放大镜拖拽到需要查询的窗口,相关信息就出来了,如下图所示:

    2.2 调用fromWinId创建QWindow

    WId获取了,然后可以调用[static] QWindow *QWindow::fromWinId(WId id)函数创建QWindow。

    2.3 调用createWindowContainer创建QWidget

    Qt提供的方法如下:

    暴力翻译:创建一个QWidget,使将窗口嵌入到基于QWidget的应用程序成为可能。窗口容器作为父容器的子容器创建,并带有窗口标志。一旦窗口被嵌入到容器中,容器将控制窗口的几何形状和可见性。不推荐在嵌入式窗口上显式调用QWindow::setGeometry(), QWindow::show()或QWindow::hide()。容器接管窗口的所有权。可以通过调用QWindow::setParent()从窗口容器中移除窗口。

    这里将QWindow指针传入即可。

    3 代码实现

    3.1 启动进程

    对于想要集成的窗口,首先得要先将进程启动起来:

        QProcess *process = new QProcess(this);
        connect(qApp, &QApplication::aboutToQuit, [process]() {
            process->kill();
            process->waitForFinished();
        });
        process->start(processName);
        process->waitForStarted();
        process->waitForFinished(400);
    

    waitForFinished是有必要的,需要等待窗口完全启动才行,不然集成不进去;当软件退出时这里会自动关闭相关进程。

    3.2 窗口集成

    上面的设计思路已经说得很清楚了,三步走,直接上代码:

        WId wid = (WId)FindWindowA(lpClassName, lpWindowName);
        QWindow *window = QWindow::fromWinId(wid);
        QWidget *widget = QWidget::createWindowContainer(window, ui->tabWidget);
    

    到这里,集成完毕。

    3.3 完整代码

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private:
        void addWindow(QString processName, const char *lpClassName, const char *lpWindowName);
    
    private:
        Ui::MainWindow *ui;
    };
    #endif // MAINWINDOW_H
    
    #include "MainWindow.h"
    #include "ui_MainWindow.h"
    
    #include <QWindow>
    #include "qt_windows.h"
    
    #include <QProcess>
    #include <QHBoxLayout>
    #include <QFileInfo>
    #include <QApplication>
    #include <QDebug>
    #include <QSysInfo>
    
    #pragma comment(lib, "User32.lib")
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        setWindowTitle(QStringLiteral("Qt小罗 进程窗口集成示例"));
    
        if ("7 SP 1" == QSysInfo::productVersion()) {
            addWindow("calc.exe", "CalcFrame", "计算器");
        }
        addWindow("mspaint.exe", "MSPaintApp", "无标题 - 画图");
        addWindow("./qtapp/collidingmice.exe", "Qt5QWindowIcon", "Colliding Mice");
        addWindow("./qtapp/hellogles3.exe", "Qt5QWindowOwnDCIcon", "hellogles3");
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::addWindow(QString processName, const char *lpClassName, const char *lpWindowName)
    {
        QProcess *process = new QProcess(this);
        connect(qApp, &QApplication::aboutToQuit, [process]() {
            process->kill();
            process->waitForFinished();
        });
        process->start(processName);
        process->waitForStarted();
        process->waitForFinished(400);
    
        WId wid = (WId)FindWindowA(lpClassName, lpWindowName);
        QWindow *window = QWindow::fromWinId(wid);
        QWidget *widget = QWidget::createWindowContainer(window, ui->tabWidget);
    
        QFileInfo info(processName);
        ui->tabWidget->addTab(widget, QStringLiteral("进程:") + info.fileName());
    
        ui->tabWidget->setCurrentWidget(widget);
    }
    

    4 总结

    这种方式并不是对所有应用都有较好的集成效果,例如如果是自定义的无边框窗口集成进来的话,会出现一些古怪的现象;大多数普通的窗口是没有问题的,例如一些windows自带的一些小应用,一些串口工具,调试工具,以及绝大多数Qt应用等,这方面本人也没做过多的测试,有兴趣的朋友可以自行验证,这里只是一个引导作用。

    拓展下思维,如果集成的窗口中有一个出现异常崩溃了,那么整个程序会崩溃么?答案是不会,它们的运行互不影响,可自行测试,如果再引入QtRO进程间通信的话,如此延伸下去感觉妙用多多、牛逼哄哄、前途不可限量啊,是不是突然感觉又找到了一种新的开发模式。

    5 下载

    示例代码

    © 版权声明
    文章版权归作者所有,未经允许请勿转载。【QQ交流:115124903】
    THE END
  • 相关阅读:
    简单跟跟spring源码
    java.lang.UnsupportedOperationException mybatis
    通过自定义注解校验后台接口请求参数
    java的修饰符 public --> protected -->default --> private
    Ubuntu安装google-chrome
    git设置core.autocrlf
    时钟时间,系统cpu时间,用户cpu时间
    推荐-Everything搜索工具
    Ubuntu no such file or directory
    centos安装docker
  • 原文地址:https://www.cnblogs.com/luoxiang/p/14392650.html
Copyright © 2011-2022 走看看