我在最近的项目中发现个问题,我要load一个文件,而这个load的过程可能需要很长时间,因为load过程中包括了dll的加载,界面的创建(不同的文件可能加载不同的dll,创建不同子窗口,产生不同界面),因此这个load过程只能由主界面的线程去做了,这样的话主界面就会暂时失去响应,但一个优秀的程序不应该如此。所以我想弹出一个模态对话框来提示用户“Please Wait...”,模态对话框也能暂时阻止了用户对主界面的操作,但模态对话框如果不返回,就相当于阻挡了主界面线程进一步执行load操作了,所以必须要用多线程,由于load过程必须由主界面线程执行(原因前面说了),那么我只能创建一个新的线程,由这个线程创建一个主界面的模态对话框,当主界面线程执行完load动作之后,向这个对话框发一个消息,告诉它可以EndDialog了,这样应该是没问题了吧……你们认为呢?可实际的情况可能是这个模态对话框根本show不出来,整个过程可以这么描述:主界面的线程调用CreateThread,新线程的Run函数如下:
DWORD WINAPI ThreadDialogWait(LPVOID lpParameter)
{
return DialogBox(hInst, MAKEINTRESOURCE(IDD_PLEASE_WAIT), g_hwndMain, (DLGPROC)WaitDlgProc);
}
非常简单,(其中hInst和g_hwndMain是全局的)后来我想那是因为对话框弹出的时候,主窗口是没响应的,它在忙load动作,可能对话框弹出时候发给主窗口的一些消息没有得到返回,所以导致了对话框线程也是处于堵塞状态,show不出来。OK,我现在就用一些手段了,改进改进。我让主窗口线程不要在CreateThread之后立即往下执行,而是等待对话框Post给自己的一个消息,才继续往下,而这个时候对话框已经show出来了,因为我可以确保对话框在post这个消息的时候,自己已经show出来了。LRESULT CALLBACK WaitDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{
    switch (message) 
    {
case WM_INITDIALOG:
SetTimer(hwndDlg, 1, 500, NULL);
SetTimer(hwndDlg, 2, 300, NULL);
g_hwndWaitDlg = hwndDlg;
break;
case WM_USER_END_DIALOG:
EndDialog(hwndDlg, 0);
break;
case WM_TIMER:
if(wParam==1)
{
PostMessage(g_hwndMain, WM_USER_MAIN_WORK, NULL, NULL);
KillTimer(hwndDlg, 1);
}
if(wParam==2)
OutputDebugString(TEXT("|"));
break;
    }
    return FALSE;
}Timer1就是起到这个作用,让对话框完全show出来,才让主窗口执行下一步的load动作,(如果在WM_INITDIALOG中post这个消息,可能还是会有问题,对话框show不出),可能你注意到了,我搞了两个Timer,Timer2起啥作用?这就是我的问题了,按照这种做法,对话框是show出来了,可通过timer2很容易就观察到,在主窗口load过程中,界面还是处于无响应状态,这就不符合我的设计初衷了,这是为什么?

解决方案 »

  1.   

    把这个问题归结为:让主线程创建一个新的线程,新的线程创建一个窗口,这个窗口的所有者为主窗口,然后让主线程处于堵塞状态(可以使用Sleep来模拟),那么这个新线程创建的窗口为什么也处于堵塞状态?它可是有自己独立的消息线程的啊?
      

  2.   

    具有父子关系的窗口之间会使用SendMessage来相互通讯,当父子窗口不属于同一线程时,SendMessage发送的消息要等到对方线程取消息时才会被响应,消息被响应后,SendMessage函数才会返回,所以当目标窗口所在线程没有正常处理消息时,SendMessage会一直等待,造成死锁。
    对于这种情况,你可以用EnableWindow把主线程窗口禁用,而不要阻塞消息循环;或者窗口间不要有父子关系。
      

  3.   

    回zhoujianhei,这跟模态不模态,没关系的,从代码上看,都是“模态”的,要非模态的话,我只要不把主窗口disable掉就可以了,但问题还是我提的问题。回cnzdgs,从代码上看,我并没有使用SendMessage,取而代之的是PostMessage,但还是有这个问题,恐怕窗口是自动自发地,在我不知情的情况下调用了SendMessage,这样我就没辙了;你说的只是把主窗口disable掉,当然可行,只不过就不能完全做到“随时响应用户请求”这个优秀软件的要求了,(不过想想看,微软的Excel,word都没做到,尝试打开一个巨大的文件,或者网速慢的情况下打开一个远程的文件,都会使得word,excel的界面暂时失去响应)。再有就是如果不给新创建的窗口设置一个Owner,就会在任务栏上多出一个按钮出来,而且也起不到暂时阻挡用户操作主窗口的功能。还有别的办法吗?
      

  4.   

    我前面说的SendMessage指的就是窗口自动执行的,与你的代码没有关系。
    很多软件在执行耗时较长的操作时是在消息响应过程中直接处理的,所以会导致界面暂时失去响应,这并不是好的做法。
    不让用户操作最好的做法就是把窗口disable掉,而不阻塞消息循环。如果想显示一个提示框,通常是在主线程用一个模态对话框,然后另开线程做后台处理,处理完后把模态对话框关掉。