Embed external exe into interface (Windows) - Qt

Preface 

I have only done this before. To call up the external program exe in the interface, just start QProcess. Now, I try to embed an external exe into the Qt interface, and it is basically implemented. It feels quite magical!

renderings

I wrote a small demo, which is triggered through the menu bar

Instructions and code

The general idea of ​​implementation is : start the external program to be embedded, then obtain the handle HWND of the main interface, convert it into a QWidget, and add it to the interface for display.

Start external program

Starting an external program is quite simple. It can be implemented using Qt's QProcess, or it can be implemented using the Windows API method. Record it here, otherwise you will forget it (Note: Sleep is necessary, because if the subsequent operation is performed immediately , Since the process is not fully up yet, the form handle cannot be obtained ):

//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;

}

Get the main form handle

It is relatively simple to start an external program. Just use the process QProcess to start and run it. How to get the handle of the main interface? After checking the information, there are two ways:

Method 1 : Obtain the handle through FindWindow of Windows API  , but you need to know the main interface class name and title name of the external exe. If it is a self-developed program, these two properties are easy to know, but if not, you can use the tool spy++ to obtain it. . (ps: HWND is the same as WId, just named differently)

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

Method 2 : Get the handle of the form through EnumWindows of Windows API:

  • Get the form handles of all running processes through EnumWindows;
  • Use the specified process ID to filter out the form handle of this process and save it to the container;
  • To find out the handle of the main form, ask whether the parent class of the form handle is empty. If so, it is the handle of the main form.

The specific code is as follows

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;
}

Add to interface 

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);
}

Note: The Qt interface I wrote and the applications that come with Windows (Notepad, etc.) are both acceptable. However, I have tried several commonly used applications such as NetEase Cloud, WeChat, etc., but they cannot be embedded into the interface. During the process , although the handle is obtained (the value is not empty), the external program still pops up when displayed. It may be that the underlying layer is not written in standard Windows, so the conversion cannot be successful.

Source code

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

Conclusion

I feel like this attempt was quite rewarding!

Guess you like

Origin blog.csdn.net/xiaopei_yan/article/details/125931178