c++窗口嵌入第三方进程窗口

现在的大型软件启动之后,很可能存在多个进程。如浏览器,每打开一个新的页面,就会启动一个新的进程。为什么会使用多进程,而不是全部使用多线程呢?因为多进程,可以做到完全的隔离,这样的好处是:如果一个页面卡死了,不会干扰到其他页面;在代码层,也少了多页面之间线程变量安全考虑的顾忌了,不用考虑同步异步等操作。
我们在MainUI中嵌入ThirdUI窗口,主要有以下步骤:

1. 打开第三方进程窗口

主要通过CreateProcess函数来打开第三方进程,并获取进程ID。

HWND CNestWndDlg::OpenProcess()
{
	// 进程启动信息
	STARTUPINFO si;
	memset(&si, 0, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = SW_SHOW;

	// 进程信息
	PROCESS_INFORMATION pi = {0};

	// 创建进程
	if (CreateProcess("C:\\WINDOWS\\system32\\notepad.exe", NULL,  NULL, NULL, false, 0, NULL, NULL, &si, &pi))
	{
		PROCESS_INFO procwin;
		procwin.dwProcessId = pi.dwProcessId;
		procwin.hWnd = NULL;

		// 等待新进程初始化完毕
		WaitForInputIdle(pi.hProcess, 5000);

		EnumWindows(EnumWindowCallBack, (LPARAM)&procwin);
		if (NULL == procwin.hWnd)
		{
			Sleep(200);
			EnumWindows(EnumWindowCallBack, (LPARAM)&procwin);
		}

		return procwin.hWnd;
	}

	return NULL;
}

2. 通过进程ID找到对应窗口句柄

主要通过EnumWindows来枚举所有窗口,并通过GetWindowThreadProcessId来获取窗口的进程ID,并与1中打开的进程ID作比较来获取对应窗口的句柄。

// 查找进程主窗口的回调函数
BOOL CALLBACK EnumWindowCallBack(HWND hWnd, LPARAM lParam)
{
	PROCESS_INFO *pProcessWindow = (PROCESS_INFO *)lParam;

	DWORD dwProcessId;
	GetWindowThreadProcessId(hWnd, &dwProcessId);

	if (pProcessWindow->dwProcessId == dwProcessId && IsWindowEnabled(hWnd) && GetParent(hWnd) == NULL)
	{
		pProcessWindow->hWnd = hWnd;
		return FALSE;
	}

	return TRUE;
}

3. 设置第三方窗口相关属性

为了让thirdUI内嵌入MainUI中,需要以下设置:

  • 修改thirdUI的属性,通过SetWindowLong清除WS_POPUP风格。
  • 修改thirdUI的属性,通过SetWindowLong添加WS_CHILD风格。
  • 通过SetParent来将MainUI设置为thirdUI的父窗口,设置完之后,thirdUI的消息循环就进入到了MainUI的消息队列,这样thirdUI就和MainUI的一个子窗口类似。
  • 通过GetClientRect来获取MainUI的客户区大小,通过MoveWindow将thirdUI移到MainUI的客户区。
  • 调用InvalidateRect将thirdUI设置为整个区域无效区域。
  • UpdateWindow强制刷新thirdUI,因为InvalidateRect只是将消息放入消息队列并不定立即响应,所以可能导致thirdUI更新不正常,此时需要UpdateWindow来强制刷新无效区域。

4. 切换窗口之后的更新设置

当窗口最小化或者切换到主进程的其他页面后,再切换回来,此时也需要刷新设置。否则会更新错误。重点,必须再次设置SetParent,否则无法正确传递消息。(具体原因未知)

  • 通过SetParent来将MainUI设置为thirdUI的父窗口,设置完之后,thirdUI的消息循环就进入到了MainUI的消息队列,这样thirdUI就和MainUI的一个子窗口类似。
  • 调用InvalidateRect将thirdUI设置为整个区域无效区域。
  • UpdateWindow强制刷新thirdUI,因为InvalidateRect只是将消息放入消息队列并不定立即响应,所以可能导致thirdUI更新不正常,此时需要UpdateWindow来强制刷新无效区域。

5. 示例

示例是一个多tab窗口,调用记事本(notepad),显示如下:
c++窗口嵌入第三方进程窗口_第1张图片
Demo

你可能感兴趣的:(VC++)