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),顯示如下:
在這裏插入圖片描述
Demo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章