淺談MFC多進程編程,ui卡死問題

        由於工作需求,最近需要把公司遊戲登陸器改成用多進程實現,把主窗口和遊戲窗口各自獨立一個進程,目的是爲了以後實現多開後界面不至於太卡且一個窗口崩掉後不至於影響其他遊戲窗口。

        以前從來沒寫過多進程,完全不懂怎麼着手,只好拿着總監給的例子先認真研究了下,一邊看例子一邊問身邊有過這方面經驗的人。基本思路是,程序一啓動在OnInitDialog函數調用CreateProcess創建子進程,並把父窗口的窗口句柄和url通過命令行傳給子進程。在app實例InitInstance接口處添加命令行參數的解析,通過解析出來的啓動參數就知道是否是子進程啓動。子進程啓動後把子進程的窗口和父進程的窗口設成父子關係(SetParent),這樣父窗口移動子窗口就自然跟着移動了,而進程間的通信則可以通過發送消息的方式來傳遞。以下是關鍵代碼(項目是居於對話框的):

//InitInstance解析命令行參數
BOOL CMultiProcTest2App::InitInstance()
{
	HWND frameWnd = NULL;
	CString gameUrl;
	CString cmd = GetCommandLine();;
	int pos = cmd.Find(_T("-frameWnd="));
	if (pos != -1)
	{	
		cmd = cmd.Mid(pos+10);
		pos = cmd.Find(_T(" "));
		if (pos != -1)
		{
			CString temp(cmd);
			temp = temp.Left(pos);
			frameWnd = (HWND) _ttol(temp.GetBuffer());			
		}	
		cmd = cmd.Mid(pos);
	}
	pos = cmd.Find(_T("-gameurl="));
	if (pos != -1)
	{
		gameUrl = cmd.Mid(pos + 9);		
	}
	if (frameWnd)
	{
		CGameWndDlg dlgGame(frameWnd);
		m_pMainWnd = &dlgGame;
		dlgGame.DoModal();			
	}
	else
	{	
		CLander_G2Dlg dlg;
		m_pMainWnd = &dlg;
		dlg.DoModal();
	}	
	return FALSE;
}

//創建子進程
void CreateChildProcess(HWND parentHwnd, CString gameUrl)
{
	TCHAR path[1024] = {0}; 
	TCHAR cmd[1024] = {0};
	GetModuleFileName(NULL, path, sizeof(path));
	wsprintf(cmd, _T("\"%s\" -frameWnd=%ld -gameurl=%s"), path, (long)parentHwnd, gameUrl);

	STARTUPINFO si; 
	PROCESS_INFORMATION pi; 
	memset(&si, 0, sizeof(si));
	si.cb = sizeof(si);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = TRUE;
	BOOL f = CreateProcess( NULL, cmd, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS/*CREATE_NO_WINDOW*/, NULL, NULL, &si, &pi );			 
	if (!f){
		return ;
	}	
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);	
}

一切時候都那麼的順利,而且子窗口裏只有一個IE控件用於打開url。

寫好後,運行起來,發現這裏有兩個問題,1、登陸器窗口非頂層窗口,如別的窗口在登陸器窗口上面時鼠標點擊遊戲窗口(即子進程的子窗口)整個登陸器窗口不會置頂。2、點擊主窗口上的關閉/最小化等按鈕時,整個登陸器卡死(只能用任務管理器關掉進程了)。

第一個問題我找我哥(同行)幫忙解決了,第二個問題,一直沒找到解決方案,也不理解爲什麼會卡死,網上也搜到了很多這樣的帖子,有人說要用鉤子或子類化的方法攔截消息,也有人說用sdk和多文檔就不存在這個問題,但都沒找到解決方案。經過了兩個多星期的糾結,幾乎要絕望了,難道我要基於sdk重寫嗎?根本沒時間了。

我已經放棄了,直接跟總監說我搞不定,但心裏還是很不舒服(我想是程序員都能理解這個感受)。後來不經意間我想到了我的大學老師,於是我打電話給他滿聊聊,聽聽他的看法。過了兩天,他發q跟我說解決了,內心由衷的高興啊,感激涕零。

把子窗口的Style屬性改成Child,Visible屬性改成True,創建子窗口的時候改成非模式的方式創建子窗口,所有的問題就這麼迎刃而解了。

       雖然爲什麼這麼改就解決了,且第一個問題也都自然解決了,我不知道是爲什麼,但一定跟DoModal機制有很大的關係,歡迎高手指教!

	HWND frameWnd = NULL;
	CString gameUrl;
	CString cmd(str);
	int pos = cmd.Find(_T("-frameWnd="));
	if (pos != -1)
	{	
		cmd = cmd.Mid(pos+10);
		pos = cmd.Find(_T(" "));
		if (pos != -1)
		{
			CString temp(cmd);
			temp = temp.Left(pos);
			frameWnd = (HWND) _ttol(temp.GetBuffer());			
		}	
		cmd = cmd.Mid(pos);
	}
	pos = cmd.Find(_T("-gameurl="));
	if (pos != -1)
	{
		gameUrl = cmd.Mid(pos + 9);		
	}
	if (frameWnd)
	{
		CGameWndDlg* dlgGame = new CGameWndDlg(frameWnd, gameUrl);
		dlgGame->Create(IDD_DLG_GAME_WND,CWnd::FromHandle(frameWnd));
		dlgGame->ShowWindow(SW_SHOW);
		m_pMainWnd = dlgGame;
	}
	else
	{	
		CLander_G2Dlg dlg;
		m_pMainWnd = &dlg;
		dlg.DoModal();
	}	

	if(frameWnd)
		return TRUE;
	else
		return FALSE;

ps:

    1、 窗口句柄跨進程傳送是可行的,因爲窗口句柄是全局的,所以跨進程傳送也是安全的

    2、在父窗口中最好不要直接通過子窗口句柄操作子窗口,比如MoveWindow改變子窗口大小,應該發消息到子窗口移動,因爲窗口對象不是多線程安全的,多個線程同時訪問可能會導致數據被誤讀或破壞


源碼下載例子:

http://download.csdn.net/detail/huasonl88/8600603




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