外部程序嵌入到Qt进程界面---附源码

1 篇文章 0 订阅

 项目需要将一个外部软件嵌入自己编写的qt界面,类似于将外部程序当作自己软件的一个插件,以起到集成的目的。笔者这里用电脑自带的计算器为例。

传统做法:
1)首先打开需要的外部程序;
2)通过spy+获取的外部程序类名;
3)将类名当作函数FindWindow的第一个参数(字符串);
代码:

    //创建进程
    QString cmd="C:/Windows/system32/calc.exe";
    STARTUPINFO si={sizeof(si)};
    PROCESS_INFORMATION pi;
    si.dwFlags=STARTF_USESHOWWINDOW;
    si.wShowWindow=true;

    bool bRet=CreateProcess(
                NULL,
                (LPWSTR)cmd.toStdWString().c_str(),
                NULL,
                NULL,
                FALSE,
                CREATE_NEW_CONSOLE,
                NULL,
                NULL,&si,&pi);

    Sleep(50);
    //CalcFrame 是需要打开程序的类名,需要用VS中的Spy++工具,在VS菜单“工具”中打开
    WId wid = (WId)FindWindow(L"CalcFrame",NULL);
    QWindow *window = QWindow::fromWinId(wid);

    QWidget *widget = QWidget::createWindowContainer(window,ui->centralWidget);
    widget->setMinimumSize(581,571);

上面代码中的函数FindWindow第一个参数为外部程序类名,需要借助vs菜单栏 工具——Spy++(+)获取得到,打开之后如下图所示:

 然后Ctr+F,进行搜索,出现如下界面:

然后长按上图中红框里面的地方,将靶心移到计算器的界面上,将出现下图:

 从上图中,就可以得到计算器的类名“CalcFrame”。

上面的传统做法是利用vs自带工具spy++得到已打开程序的类名或窗口名,进而通过FindWindow函数得到句柄。然而,每次打开的外部程序类名或窗口名都会改变,另外也不可能要求用户通过第三方工具得到这两个值中的一个,所以该方法不大可行。

改进做法
1)根据exe路径启动程序,并得到进程id号;
2)根据进程id号得到主窗口句柄:通常情况下一个进程内有多个窗口句柄,还需要从得到的N个句柄中找到主窗口句柄(下面会统一讲到);
3)将HWND转为WId,进而将外部程序嵌入QWindow、widget:

代码:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    m_pProcess(NULL)
{
    ui->setupUi(this);
    m_pProcess = new QProcess(this);
    connect(m_pProcess, &QProcess::started, this, &MainWindow::on_processStarted);

    //查找程序是否已经运行
    m_pProcess->start("tasklist");
    m_pProcess->waitForFinished(); //等待命令执行结束
    QByteArray result = m_pProcess->readAllStandardOutput();
    if(-1 == result.indexOf("calc.exe"))
    {
        QString cmd="C:/Windows/system32/calc.exe";
        m_pProcess->start(cmd);
    }
}

//嵌入外部程序
void MainWindow::on_processStarted()
{
    if(m_pProcess == NULL)
    {
        return;
    }

    qint64 id = m_pProcess->processId();//如果程序没有运行,将会返回0
    if (id == 0)
    {
        //QMessageBox::information(NULL, "提示", "程序没有启动");
        return;
    }

    Sleep(2500);
    qDebug() << "Status: " << m_pProcess->state();
    HWND mainwindowHwnd = FindMainWindow(id);
    qDebug() << "mainwindowHwnd: " << mainwindowHwnd;
    if (!mainwindowHwnd)
        return;


    WId wid = (WId)mainwindowHwnd;
    QWindow *window = QWindow::fromWinId(wid);
    QWidget *widget;
    widget = QWidget::createWindowContainer(window,ui->centralWidget);
    //要设置大小,不然可能只显示很小的一块
    widget->setMinimumSize(800,700);
}

其中,获取主窗口句柄的函数 FindMainWindow 代码如下:
函数声明


BOOL IsMainWindow(HWND handle);
BOOL CALLBACK EnumWindowsCallback(HWND handle, LPARAM lParam);
HWND FindMainWindow(unsigned long process_id);//通过进程id号获取主窗口句柄

定义

BOOL IsMainWindow(HWND handle)
{
    return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
}

BOOL CALLBACK EnumWindowsCallback(HWND handle, LPARAM lParam)
{
    handle_data& data = *(handle_data*)lParam;
    unsigned long process_id = 0;
    GetWindowThreadProcessId(handle, &process_id);
    if (data.process_id != process_id || !IsMainWindow(handle)) {
        return TRUE;
    }
    data.best_handle = handle;
    return FALSE;
}

//通过进程id号获取主窗口句柄
HWND FindMainWindow(unsigned long process_id)
{
    handle_data data;
    data.process_id = process_id;
    data.best_handle = 0;
    EnumWindows(EnumWindowsCallback, (LPARAM)&data);
    return data.best_handle;
}

完整项目代码见

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值