将外部exe嵌入到界面中(Windows)——Qt

前言 

之前只做过,在界面中调起外部程序exe,只用将QProcess启动即可。如今,尝试将外部的exe嵌入到Qt的界面中,基本实现了,感觉挺神奇的!

效果图

我写了一个小demo,是通过菜单栏触发的

说明和代码

实现的大体思路是:将要嵌入的外部程序启动运行,然后获取主界面的句柄HWND,将其转化为QWidget,将其加到界面上显示。

启动外部程序

启动外部程序,挺简单的,可以用Qt的QProcess来实现,也可以用Windows API的方法来实现,在这里记录下,不然老忘(:Sleep 是必须的,因为如果立即执行后续操作的话,由于进程还没有完全起来,是获取不到窗体句柄的):

//Qt 
qint64 MainWindow::startProcess(QString cmd)
{
    QProcess* process=new QProcess(this);
    process->setProgram(cmd);
    process->setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments *args)
    {
    args->startupInfo->wShowWindow = SW_HIDE;
    args->startupInfo->dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    }
    );
    process->start();
    Sleep(500);
    return process->processId();
}

//--------------------------------------------------------------------------

// Windows API
qint64 MainWindow::startProcess(QString cmd)
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = SW_HIDE;

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

    Sleep(500);
    return (qint64)pi.dwProcessId;

}

获取主窗体句柄

外部程序启动比较简单,用进程QProcess启动运行即可,就是怎么获取主界面的句柄?查了查资料,有两种方式:

方式一:通过Windows API 的 FindWindow来获取句柄,不过需要知道外部exe的主界面类名和标题名,若是自己开发 的程序,这两个属性很容易知道,但是如果不是,则可以借助工具spy++来获取。(ps: HWND 与 WId一样,只是叫法不同 )

WId winId=(WId)FindWindow(L"无标题 - 记事本",L"Notepad");

方式二:通过Windows API  的 EnumWindows来获取窗体的句柄:

  • 通过EnumWindows获取所有运行进程的窗体句柄;
  • 用指定进程ID 筛选出此进程的窗体句柄,保存到容器;
  • 查找出主窗体的句柄,即询问窗体句柄是否父类为空,是的话,即为主窗体句柄。

具体代码如下

typedef struct EnumHWndsArg
{
	std::vector<HWND>* vecHWnds;
	DWORD dwProcessId;
}EnumHWndsArg, *LPEnumHWndsArg;


BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam)
{
	EnumHWndsArg* pArg = (LPEnumHWndsArg)lParam;
	DWORD  processId;
	GetWindowThreadProcessId(hwnd, &processId);
	if (processId == pArg->dwProcessId)
	{
		pArg->vecHWnds->push_back(hwnd);
	}
	return TRUE;
}

void GetHWndsByProcessID(DWORD processID, std::vector<HWND>& vecHWnds)
{
	EnumHWndsArg wi;
	wi.dwProcessId = processID;
	wi.vecHWnds = &vecHWnds;
	EnumWindows(lpEnumFunc, (LPARAM)&wi);
}


WId MainWindow::getProcessWid(qint64 pid)
{
    if (pid != 0)
    {
        std::vector<HWND> vecHWnds;
        GetHWndsByProcessID((DWORD)pid, vecHWnds);
        for (const HWND& h : vecHWnds)
        {
            HWND parent = GetParent(h);
            if (parent == NULL)
            {
                return (WId)h;
            }
        }
    }
    return 0;
}

添加到界面 

void MainWindow::addTabOfWindow(WId wid, QString tabName,QString path)
{
    if(wid==0)
        return;

    QWindow *pWindow = QWindow::fromWinId(wid);

    pWindow->setFlags(pWindow->flags()|Qt::CustomizeWindowHint |Qt::WindowTitleHint );
    QWidget *pWidget = QWidget::createWindowContainer(pWindow);
    pWidget->setProperty("path",path);
    ui->tabWidget->addTab(pWidget, tabName);
}

注意:我写的Qt界面和windows自带的应用(记事本等)都是可以的,但是试过好几个常用的应用比如网易云、微信等,都是没办法嵌入到界面内的,在过程中,虽然获取到了句柄(值不为空),但是显示的时候外部程序还是弹出来显示了。可能是底层不是标准的Windows 编写的,所以没办法转化成功。

源码

https://download.csdn.net/download/xiaopei_yan/88214817

结束语

感觉这一次尝试,还是蛮有收获的!

猜你喜欢

转载自blog.csdn.net/xiaopei_yan/article/details/125931178