以编程方式获取WM_CLOSE时,对话框不会被破坏 [英] Dialog box doesn't get destroyed when programatically gets WM_CLOSE

查看:87
本文介绍了以编程方式获取WM_CLOSE时,对话框不会被破坏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

介绍及相关信息:



我在资源编辑器中做了一个简单的对话。



它有一个按钮。



按下时,按钮激活一个使用OLE填充Word文档的线程函数自动化。



我需要正确处理 WM_CLOSE ,因为用户可以在Word填充期间关闭对话框。



我设计了一条自定义消息( WM_THREAD_OK ),该线程在完成时发送,或者在完成清理时发送接收到中止信号后的COM对象。



对话框是无模式的。



我的代码的概念:



在我的对话框中,我有一个标志,指示用户点击了关闭按钮。



这是一个 bool 变量,只是在对话框程序的标题下定义。



在我的 WM_INITDI ALOG 我将其设置为false。



当用户按下关闭按钮或ESC键时,此标志设置为true。



现在是时候检查线程是否已经完成执行。



在两个处理程序中(关闭按钮/ ESC键)检查线程句柄,如果线程句柄是NULL,那么我们进行适当的清理,然后我们销毁窗口。



如果线程仍在工作,则发送中止信号线程,我们从那些处理程序返回,等待处理程序线程自定义消息关闭对话框。



此外,在线程流产期间以编程方式隐藏对话框,所以用户可以得到预期行为的错觉。



以下是相关代码片段:



INTRODUCTION AND RELEVANT INFORMATION:

I have made a simple dialog in the resource editor.

It has a button.

When pressed, button activates a thread function that populates Word document using OLE automation.

I need to handle WM_CLOSE properly, because user can close dialog box during the population of Word.

I have designed a custom message ( WM_THREAD_OK ), which thread sends when finishes, or when finishes with cleaning up of the COM objects after it receives a signal to abort.

Dialog box is modeless one.

The concept of my code:

In my dialog, I have a flag that indicates that user clicked on close button.

It is a bool variable defined just bellow the dialog procedure's header.

In my WM_INITDIALOG I set it to false.

When user presses close button or ESC key, this flag is set to true.

Now is the time to check if thread has finished execution.

In both handlers ( close button/ESC key ) thread handle is checked, and if thread handle is NULL then we do a proper cleanup and we destroy the window.

If thread is still working, abortion signal is sent to the thread and we return from those handlers, waiting for handler to threads custom message to close the dialog.

Also, dialog box is programatically hidden during the abortion of the thread, so the user can get the illusion of the expected behavior.

Here are relevant code snippets:

// static bool closeDlg;

case WM_INITDIALOG:
     {
          closeDlg = false;

          // other initialization ...

     }
     return TRUE;

case IDCANCEL:
     {

        ShowWindow( hwnd, SW_HIDE ); // hide window

        if( threadHandle ) // thread is running, abort it
        {

           data.bContinue = false; // set abortion flag for thread

           closeDlg = true; // set auto exit flag

           break; // just return, thread message will close dialog

        }

        DestroyWindow(hwnd);

        hDlgTSO = NULL; // set dialogs global handle to NULL

        break;
     }

  // other WM_COMMAND messages...

  case WM_THREAD_OK: // custom message sent by a thread just before it exits

        if( threadHandle )
        {		
           WaitForSingleObject( threadHandle, INFINITE );
   
           CloseHandle( threadHandle );

           threadHandle = NULL;
        }

        ShowWindow( GetDlgItem( hwnd, IDOK ), SW_SHOW );

        if( closeDlg )
           PostMessage( hwnd, WM_CLOSE, 0, 0 );

        return TRUE;
        break;

  case WM_CLOSE:

       ShowWindow( hwnd, SW_HIDE );

       if( threadHandle ) // thread is running, abort it
       {

           data.bContinue = false; // set abortion flag for thread

           closeDlg = true; // set auto exit flag

           return TRUE; // just return, thread's custom message handler
                        // will take care of closing
       }

       DestroyWindow(hwnd);

       hDlgTSO = NULL; // set dialog's global handle to NULL

       return TRUE;

       break;

   // other code for dialog ... 





对话框是作为对点击的响应创建的,如下所示:





Dialog box is created as a response to the click like this:

case 4001:

  if( !IsWindow( hDlgTSO ) )
 
     hDlgTSO = CreateDialog( hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, Print );
        
  else
            
     MessageBox( hWnd, L"Already open!", L"Info", MB_ICONINFORMATION );

  break;





这是主窗口的消息循环:





And this is main window's message loop :

while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
    if( ( !IsWindow( hDlgTSO ) ) || ( !IsDialogMessage( hDlgTSO, &Msg ) ) )
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
}





问题:



当我在Word文档填充期间尝试关闭对话框时出现问题:



我点击关闭按钮(右上角的X)对话框不会从屏幕上消失,但保持在那里(记住我已通过 ShowWindow <隐藏它/ code>)。



我在此之后立即采取任何行动(点击桌面,或在我的应用程序主窗口,或切换到一个不同的窗口...),它消失。



线程功能正常退出。



似乎好像永远不会从 WM_THREAD_OK 处理程序发送WM_CLOSE消息。



然而,我的 IDCANCEL 处理程序运行良好,但它遵循相同的算法。



我的努力:



此刻,我正在阅读MSDN文档f或 WM_CLOSE 和消息循环。



我真的无法弄清楚问题,所以我不知道知道在哪里搜索。



问题:



如何修改上面的代码,以便正确关闭对话框?



如果需要额外的代码,请索取。为了使问题简短,省略了。



谢谢。



问候。



编辑:



我的代码中的问题是存在多个无模式对话框。主窗口消息循环未正确实现导致故障。我接受了下面的答案,因为没有研究那段代码我就找不到问题的原因。



PROBLEM:

The problem occurs when I try to close dialog box during the population of the Word document:

When I click on the close button ( X in the top right ) the dialog box doesn't disappear from the screen, but stays there ( remember that I have hidden it via ShowWindow ).

As soon as I take ANY action after that( clicking on desktop, or on my applications main window, or if I switch to a different window ... ), it disappears.

Thread function exits properly.

It seems as if never gets WM_CLOSE message sent from the WM_THREAD_OK handler.

However, my IDCANCEL handler works well, yet it follows the same algorithm.

MY EFFORTS:

At this moment, I am reading the MSDN documentation for WM_CLOSE and for message loops.

I really can't figure out the problem, so I do not know where to search.

QUESTION:

How can I modify the above code so dialog box gets properly closed ?

If additional code is required, ask for it. It is omitted to keep the question short.

Thank you.

Regards.



The problem in my code was the existence of multiple modeless dialog boxes. The main window message loop was not implemented properly which caused the malfunction. I have accepted the answer bellow because without studying that code I would not be able to find the cause of my problem.

推荐答案

这是一个超级丑陋的测试代码,你可以欺骗它。将其编译为ANSI(而非UNICODE)控制台程序。



您可以调试此程序和程序以比较不同的程序。你也可以复制粘贴部分。



DialogTest.cpp:

Here is a super ugly testcode that works, you can cheat from it. Compile it as an ANSI (not UNICODE) console program.

You can debug both this and your program to compare what is different. You can also copy paste parts.

DialogTest.cpp:
#include "stdafx.h"
#include "resource.h"
#include <windows.h>

const UINT WM_THREAD_OK = WM_USER + 10;
const int SHOW_DLG_BUTTON_ID = 2;
const int SIMULATE_THREAD_MESSAGE_BUTTON_ID = 3;

HINSTANCE g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
HWND g_hThreadFinishSimulateWnd = NULL;
HWND g_hMainWnd = NULL;
HWND g_hDialog = NULL;
HWND g_hShowDlgButton = NULL;
HWND g_hSimulateThreadFinishButton = NULL;

bool g_ThreadRunning = false;
bool g_CloseDialogRequest = false;
bool g_CloseMainWndRequest = false;

void StartThread()
{
    // TODO: actually create and start the thread...
    g_ThreadRunning = true;
    SetWindowText(GetDlgItem(g_hDialog, IDC_STATIC_THREAD_STATE), "yes");
    EnableWindow(GetDlgItem(g_hDialog, IDC_START_THREAD), FALSE);
    EnableWindow(g_hSimulateThreadFinishButton, TRUE);
}

void OnThreadFinished()
{
    // TODO: wait for thread handle and close it
    g_ThreadRunning = false;
    // The if is just cosmetics, we don't enable the disabled button
    // on the window/dialog if it is about to be closed.
    if (!g_CloseDialogRequest)
    {
        SetWindowText(GetDlgItem(g_hDialog, IDC_STATIC_THREAD_STATE), "no");
        EnableWindow(GetDlgItem(g_hDialog, IDC_START_THREAD), TRUE);
    }
    EnableWindow(g_hSimulateThreadFinishButton, FALSE);
}

void RequestThreadExit()
{
    // TODO: Send a cancel request to the thread somehow
}


void DisableAllChildControls(HWND wnd)
{
    HWND child = GetWindow(wnd, GW_CHILD);
    while (child)
    {
        EnableWindow(child, FALSE);
        child = GetWindow(child, GW_HWNDNEXT);
    }
}

void RequestThreadExitAndDialogClose()
{
    if (g_CloseDialogRequest)
        return;
    RequestThreadExit();
    g_CloseDialogRequest = true;
    SetWindowText(g_hDialog, "Dialog close requested...");
#if 0
    // Well, disabling the currently focused dialog is not a good practice...
    EnableWindow(g_hDialog, FALSE);
#elif 0
    // In some situations this may be correct, but often it isn't... If you want to
    // lie to the user that the cancel has finished then it may be OK but in this case
    // what do you lie to the user if she wants to start the thread again and the previous
    // thread is still in progress???
    ShowWindow(g_hDialog, SW_HIDE);
#elif 1
    // Disabling all child controls and informing the user that the close request
    // has been processed - we did the feedback by setting the dialog title but
    // we could use a label too with "cancelling..." text.
    DisableAllChildControls(g_hDialog);
#endif
}

void MyLog(const char* msg)
{
    printf("- %-50s thread_running:%d dlg_close_req:%d main_wnd_close_req:%d\n",
        msg,
        g_ThreadRunning ? 1 : 0,
        g_CloseDialogRequest ? 1 : 0,
        g_CloseMainWndRequest ? 1 : 0);
}

INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_INITDIALOG:
        MyLog("Dialog: WM_INITDIALOG");
        g_CloseDialogRequest = false;
        EnableWindow(g_hShowDlgButton, FALSE);
        return TRUE;
    case WM_CLOSE:
        MyLog("Dialog: WM_CLOSE");
        if (g_ThreadRunning)
        {
            RequestThreadExitAndDialogClose();
        }
        else
        {
            // The if is just cosmetics, we don't enable the disabled button
            // on the window/dialog if it is about to be closed.
            if (!g_CloseMainWndRequest)
                EnableWindow(g_hShowDlgButton, TRUE);
            EndDialog(hwndDlg, 0);
            g_hDialog = NULL;
        }
        return TRUE;
    case WM_THREAD_OK:
        MyLog("Dialog: WM_THREAD_OK");
        OnThreadFinished();
        if (g_CloseDialogRequest)
            PostMessage(hwndDlg, WM_CLOSE, 0, 0);
        if (g_CloseMainWndRequest)
            PostMessage(g_hMainWnd, WM_CLOSE, 0, 0);
        return TRUE;
    case WM_COMMAND:
        {
            int ctrl_id = LOWORD(wParam);
            if (ctrl_id == IDC_START_THREAD)
            {
                MyLog("Dialog: WM_COMMAND/ID_START_THREAD");
                if (!g_ThreadRunning)
                    StartThread();
                return TRUE;
            }
            else if (ctrl_id == IDCANCEL)
            {
                MyLog("Dialog: WM_COMMAND/IDCANCEL");
                PostMessage(hwndDlg, WM_CLOSE, 0, 0);
                return TRUE;
            }
        }
        break;
    }
    return FALSE;
}


LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        MyLog("MainWnd: WM_CREATE");
        g_hShowDlgButton = CreateWindowEx(0, "Button", "Create/show my test dialog", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
            0, 0, 200, 35, hWnd, (HMENU)SHOW_DLG_BUTTON_ID, g_hInstance, NULL);
        break;
    case WM_CLOSE:
        MyLog("MainWnd: WM_CLOSE");
        if (g_ThreadRunning)
        {
#if 1
            if (!g_CloseMainWndRequest)
            {
                g_CloseMainWndRequest = true;

                SetWindowText(hWnd, "Main window close requested...");
                DisableAllChildControls(hWnd);

                RequestThreadExitAndDialogClose();
            }
#else
            // A very simple variation to handle a running thread
            MessageBox(hWnd, "Sorry, can't exit while the thread is running!", "INFO", MB_OK|MB_ICONHAND);
#endif
            return 0;
        }

        // If we close the main window when the dialog is open but
        // the thread isn't running...
        if (g_hDialog)
            EndDialog(g_hDialog, 0);

        // Letting DefWindowProc to destroy the main window.
        break;
    case WM_DESTROY:
        MyLog("MainWnd: WM_DESTROY");
        PostQuitMessage(0);
        break;
    case WM_COMMAND:
        {
            int ctrl_id = LOWORD(wParam);
            if (ctrl_id == SHOW_DLG_BUTTON_ID)
            {
                MyLog("MainWnd: WM_COMMAND/SHOW_DLG_BUTTON_ID");
                if (!g_hDialog)
                {
                    g_hDialog = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DialogProc);
                    ShowWindow(g_hDialog, SW_SHOW);
                }
                return 0;
            }
        }
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}


bool CreateMainWnd()
{
    static const char CLASS_NAME[] = "MainWndClass";
    WNDCLASS wc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hInstance = g_hInstance;
    wc.lpfnWndProc = &MainWndProc;
    wc.lpszClassName = CLASS_NAME;
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    if (!RegisterClass(&wc))
        return false;
    g_hMainWnd = CreateWindowEx(
        0,
        CLASS_NAME,
        "Main Window",
        WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
        0, 150, 250, 100,
        NULL,
        NULL,
        g_hInstance,
        NULL
        );
    return true;
}

LRESULT CALLBACK SimulateThreadFinishWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        g_hSimulateThreadFinishButton = CreateWindowEx(0, "Button", "Simulate thread finish", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|WS_DISABLED,
            0, 0, 200, 35, hWnd, (HMENU)SIMULATE_THREAD_MESSAGE_BUTTON_ID, g_hInstance, NULL);
        break;
    case WM_CLOSE:
        PostMessage(g_hMainWnd, WM_CLOSE, 0, 0);
        return 0;
    case WM_COMMAND:
        {
            int ctrl_id = LOWORD(wParam);
            if (ctrl_id == SIMULATE_THREAD_MESSAGE_BUTTON_ID)
            {
                MyLog("MainWnd: WM_COMMAND/SIMULATE_THREAD_MESSAGE_BUTTON_ID");
                if (g_hDialog && g_ThreadRunning)
                    PostMessage(g_hDialog, WM_THREAD_OK, 0, 0);
            }
        }
        break;
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

// Creates a utility window for us. We need this "debug" window because
// in case of main window close request both the main window and the
// dialog is disabled so we wouldn't be able to press a button on them.
bool CreateSimulateThreadFinishWindow()
{
    static const char CLASS_NAME[] = "SimulateWndClass";
    WNDCLASS wc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hInstance = g_hInstance;
    wc.lpfnWndProc = &SimulateThreadFinishWndProc;
    wc.lpszClassName = CLASS_NAME;
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    if (!RegisterClass(&wc))
        return false;
    g_hThreadFinishSimulateWnd = CreateWindowEx(
        0,
        CLASS_NAME,
        "Thread finish simulate window",
        WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
        0, 0, 250, 100,
        NULL,
        NULL,
        g_hInstance,
        NULL
        );
    return true;
}

int main()
{
    if (!CreateMainWnd() || !CreateSimulateThreadFinishWindow())
        return -1;

    ShowWindow(g_hThreadFinishSimulateWnd, SW_SHOW);
    UpdateWindow(g_hThreadFinishSimulateWnd);

    ShowWindow(g_hMainWnd, SW_SHOW);
    UpdateWindow(g_hMainWnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!g_hDialog || !IsDialogMessage(g_hDialog, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    DestroyWindow(g_hThreadFinishSimulateWnd);

    printf("Program finished, press ENTER to exit.\n");
    getchar();
    return (int)msg.wParam;
}





resource.h:



resource.h:

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Test.rc
//
#define IDD_DIALOG1                     101
#define IDC_START_THREAD                1001
#define IDC_STATIC_THREAD_STATE         1002

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1003
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

< br $>


Test.rc:



Test.rc:

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// Hungarian (Hungary) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_HUN)
LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT

/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_DIALOG1 DIALOGEX 0, 0, 191, 48
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    PUSHBUTTON      "Start thread",IDC_START_THREAD,7,27,50,14
    PUSHBUTTON      "IDCANCEL: simulating ESC",IDCANCEL,63,27,119,14
    LTEXT           "Thread running:",IDC_STATIC,7,7,52,8
    LTEXT           "no",IDC_STATIC_THREAD_STATE,63,7,76,8
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_DIALOG1, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 182
        TOPMARGIN, 7
        BOTTOMMARGIN, 41
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // Hungarian (Hungary) resources
/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////
// English (United States) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

#endif    // English (United States) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


这篇关于以编程方式获取WM_CLOSE时,对话框不会被破坏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆