Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Kết quả 1 đến 3 của 3
  1. #1
    Ngày tham gia
    Sep 2015
    Bài viết
    0

    [API] WM_QUIT, WM_CLOSE và WM_DESTROY

    Bài toán "Viết một ứng dụng, có khả năng thông báo xác nhân khi người dùng thoát ứng dụng"

    Nếu anh em đã học API thì có thể sẽ thấy bài này ko khó phải nói là dễ, nhưng mà có nhiều vấn đề phải nói ở đâ.

    Nhìn vào bài toán ban đầu ý tưởng thoáng đến sẽ là đặt cái MessageBox trong cái WM_DESTROY, để kiểm tra người dùng có muốn thoát ứng dụng ko

    Mã:
        case WM_DESTROY:
            int Thoat;
            Thoat = MessageBox(hWnd, _T("Ban co muon thoat chuong trinh khong ???"), _T("Thong Bao"), MB_OKCANCEL);
            
            if(Thoat == IDOK)
                PostQuitMessage(0);
            else
                return 0;
    
            break;
    Kết quả nhận đc là khi bấm thoát chương trình, 1 cái MesageBox sẽ hiện lên hỏi bạn có muốn thoát chương trình không. Nếu bạn chọn OK, thì ko có gì xảy ra, còn nếu chọn NO, thì chương trình cũng tự mất tiêu !!! . chạy 1 lần nữa thì ứng dụng báo là đang chạy xin tắt cái cũ đi, vào TaskManager thì thấy ứng dụng vẫn còn lù lù đó chưa tắt. Tại sao vậy

    Vì khi bạn đã nhận đc WM_DESTROY có nghĩa là ít nhiều chương trình đã đc giải phóng vùng nhớ , chỉ còn chờ PostQuitMessage() cho 1 cái WM_QUIT để nó tắt cái vòng lặp và tắt luôn cái ứng dụng

    Mã:
    int APIENTRY _tWinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPTSTR    lpCmdLine,
                         int       nCmdShow)
    {
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
    
         // TODO: Place code here.
        MSG msg;
        HACCEL hAccelTable;
    
        // Initialize global strings
        LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadString(hInstance, IDC_C4W_P1_BT3_EVENT, szWindowClass, MAX_LOADSTRING);
        MyRegisterClass(hInstance);
    
        // Perform application initialization:
        if (!InitInstance (hInstance, nCmdShow))
        {
            return FALSE;
        }
    
        hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_C4W_P1_BT3_EVENT));
    
        // Main message loop:
        while (GetMessage(&msg, NULL, 0, 0))
        {
            if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    
        return (int) msg.wParam;
    }
    Nếu nhận đc WM_QUIT thì GetMessage ở trên sẽ trả về 0 và thoát khỏi while và thoát ứng dụng

    Vậy tóm lại ko thể đặt cái MessageBox ở WM_DESTROY vì lúc nhận đc nó thì ứng dụng đã "dọn dẹp sạch sẽ, giấy nháp vào sọt rác" chỉ chờ "mở cửa ra khỏi công ty", mà đã "dọn dẹp" rồi thì làm sao mà để lại nguyên si như ban đầu đc [IMG]images/smilies/11.gif[/IMG]


    You can use the DestroyWindow function to destroy a window. Typically, an application sends the WM_CLOSE message before destroying a window, giving the window the opportunity to prompt the user for confirmation before the window is destroyed. A window that includes a window menu automatically receives the WM_CLOSE message when the user clicks Close from the window menu. If the user confirms that the window should be destroyed, the application calls DestroyWindow. The system sends the WM_DESTROY message to the window after removing it from the screen. In response to WM_DESTROY, the window saves its data and frees any resources it allocated. A main window concludes its processing of WM_DESTROY by calling the PostQuitMessage function to quit the application.
    Xem thêm : http://msdn2.microsoft.com/en-us/library/ms632598(VS.85).aspx

    Giải pháp cho việc này là để cái MessageBox vào WM_CLOSE. Vì khi bạn bấm Close 1 ứng dụng nó sẽ đi theo thứ tự


    WM_CLOSE > DestroyWindow() > WM_DESTROY > PostQuitMessage() > WM_ QUIT
    Xem thêm ở đây
    http://www.daniweb.com/forums/thread43819.html

    vậy phải chận nó trước khi nó gọi DestroyWindow để "dọn dẹp"

    Mã:
        case WM_CLOSE:
            int Thoat;
            Thoat = MessageBox(hWnd, _T("Ban co muon thoat chuong trinh khong ???"), _T("Thong Bao"), MB_OKCANCEL);
    
            if(Thoat == IDCANCEL)
            {
                return 0;
            }
            else
                DestroyWindow(hWnd); // Chỉ "dọn dẹp" khi người dùng click vào OK
    
            break;
    
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    Ok kết quả thật là mĩ mãn, nhưng vấn đề mắc phải là khi nhấp vào nút Exit thì nó đi luôn ko hỏi han gì cả khác với việc nhấn vào nút X hay Alt + F4, tìm cái code chỗ Exit của menu thì thấy nó gọi luôn DestroyWindow(), khi đó nó sẽ đi tới bước WM_DESTROY mà ko qua bước WM_CLOSE.

    Giải pháp : Ép nó phải qua WM_CLOSE ko cho DestroyWindow()

    Mã:
        case WM_COMMAND:
            wmId    = LOWORD(wParam);
            wmEvent = HIWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_EXIT:
                //DestroyWindow(hWnd);
                SendMessage (hWnd, WM_CLOSE, 0, 0) ; // Gửi Message kêu nó phải thoát bằng cách qua WM_CLOSE
                break;
    
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
            break;
    Theo một số người khác nói thì WM_QUIT nằm ở khoảng giữa WM_DESTROY và WM_CLOSE (thật vậy sao [IMG]images/smilies/daydreaming.gif[/IMG])

    http://www.3dbuzz.com/vbforum/showthread.php?t=135789

    Nên tui đặt câu hỏi : Tại sao ko đặt MessageBox chận nó lại hỏi nó thử xem

    Mã:
        case WM_QUIT:
            MessageBox(hWnd, _T("WM_QUIT"), _T("Thong Bao"), MB_OK);
            break;
    Nhưng cho dù bạn có đặt BreakPoint để Debug thì ko cách chi vào đc. Do


    The WM_QUIT message indicates a request to terminate an application and is generated when the application calls the PostQuitMessage function. It causes the GetMessage function to return zero.

    Remarks

    The WM_QUIT message is not associated with a window and therefore will never be received through a window's window procedure. It is retrieved only by the GetMessage or PeekMessage functions.
    Xem thêm : http://msdn2.microsoft.com/en-us/library/ms632641(VS.85).aspx

    Vậy là bạn ko thể chận nó đc vì khi nó xuất hiện thì nó đã làm vòng While bị vỡ (GetMessage trả về 0) và thoát chương trình.

    Và do câu nói "generated when the application calls the PostQuitMessage" nên tôi nghĩ chính xác nó phải nằm cuối chứ ko nằm giữa [IMG]images/smilies/2.gif[/IMG]

    Tóm lại :

    WM_CLOSE nhận đc khi người dùng close chương trình (Alt + F4 hay là click vào dấu x)

    WM_DESTROY nhận đc khi DestroyWindow(), khi đó dữ liệu đã đc xóa, chỉ còn đợi PostQuitMessage(0) post WM_QUIT để Window gỡ nó ra khỏi hàng đợi, nếu bỏ qua bước PostQuitMessage , thì chương trình sẽ vẫn còn chạy trong TaskManager

    WM_QUIT xảy ra sau khi PostQuitMessage, ko nhận đc trong WndProc, chỉ nhận đc khi GetMessage trả về 0, nó làm cho vòng lặp nhận thông điệp ngưng trước khi thông điệp đến đc WndProc của ứng dụng

    WM_CLOSE > DestroyWindow() > WM_DESTROY > PostQuitMessage(0) > WM_ QUIT

    -------------------------------------------------------

    Vấn đề này tuy ko lớn lắm nhưng cũng đưa ra để mọi người cùng thảo luận xem có phải nó như thế ko. Mọi người xem thử nếu có sai xót hay có gì khác thì nhắn liền để anh em cùng biết cùng thảo luận tiếp về WM_QUIT, WM_DESTROY, WM_CLOSE . Cái này thì tui thấy 1 số sách tui đã xem qua, nó ko nói kĩ phần này lắm nên tức quá phải mò cho bằng đc

    Cái này ko khó để code, nhưng cũng để luôn nếu ai muốn tham khảo thử

    MessageBox nằm trong WM_DESTROY, ứng dụng vẫn còn trong TaskManager khi đã tắt :

    http://www.ziddu.com/downloadlink.ph...hkZSnZqugmZSm9
    MessageBox nằm trong WM_CLOSE :

    http://www.ziddu.com/downloadlink.ph...JGlaaeam5w%3D7

  2. #2
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Thêm 1 chút
    WM_CLOSE > DestroyWindow() > WM_DESTROY > WM_NCDESTROY > PostQuitMessage(0) > WM_ QUIT

    Bài viết của bạn làm tôi phải tiến hành thử nghiệm 1 chút.
    Theo msdn thì WM_DESTROY dc post khi nó tiến hành phá hủy window.Lúc đầu tôi nghĩ nó sẽ thông báo trước, chắc mọi chuyện có thể cứu dc, tôi thử xài ShowWindow thử khi nhận wm_destroy, nhưng trả về false, nghĩa là ko dc nữa rồi.
    Cũng theo msdn thì sau khi các thành phần con của window bị phá hủy,nó sẽ post tiếp 1 msg là WM_NCDESTROY đến winpprog.
    Còn về WM_QUIT, đó là msg đế thoát khỏi vòng lặp msg, ko dc winprg xử lý.
    Kết luận : Chỉ có thể chặn WM_CLOSE
    Và mình đồng ý với giải pháp của bạn.

  3. #3
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Tuyệt vời! Bài viết rất hay, rất hữu ích đối với những người bắt đầu. Cảm ơn bạn nhiều nhé! Hãy cố gắng phát huy!

 

 

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •