您的位置:首页 > 产品设计 > UI/UE

浅谈MFC多进程编程,ui卡死问题

2015-04-16 11:34 1201 查看
由于工作需求,最近需要把公司游戏登陆器改成用多进程实现,把主窗口和游戏窗口各自独立一个进程,目的是为了以后实现多开后界面不至于太卡且一个窗口崩掉后不至于影响其他游戏窗口。

以前从来没写过多进程,完全不懂怎么着手,只好拿着总监给的例子先认真研究了下,一边看例子一边问身边有过这方面经验的人。基本思路是,程序一启动在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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: