前言
之前只做过,在界面中调起外部程序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
结束语
感觉这一次尝试,还是蛮有收获的!