简体   繁体   English

WinApi消息循环,Postmessage就像SendMessage一样

[英]WinApi message loop, Postmessage works like SendMessage

Hello can Somebody answer me why when I run this program the order of MessageBoxes is 1,2,4,3 instead of 1,2,3,4. 你好有人回答我为什么当我运行这个程序时,MessageBoxes的顺序是1,2,4,3而不是1,2,3,4。 In my opinion program should end executing WM_PAINT procedure before start WM_USER+11, why it isn't? 在我看来,程序应该在启动WM_USER + 11之前结束执行WM_PAINT程序,为什么不呢?

// Win32Project6.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "Win32Project6.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
TCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);


DWORD thread(LPVOID lpdwThreadParam);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ 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_WIN32PROJECT6, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT6));

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT6));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_WIN32PROJECT6);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }
//   PostMessage(hWnd, WM_USER + 11, 0, 0);
   MessageBox(0,"1","Message",0);
   MessageBox(0, "2", "Message", 0);
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_USER+11:
        MessageBox(hWnd,"4","Message",0);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&thread, &hWnd, 0, 0);
        MessageBox(hWnd, "3", "Message", 0);
        // TODO: Add any drawing code here...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}


DWORD thread(LPVOID lpdwThreadParam)
{

    PostMessage(*(HWND*)(lpdwThreadParam), WM_USER + 11, 0, 0);

    return 0;
}

Some notes about this code: 关于此代码的一些注意事项

  • You should NOT create threads in WM_PAINT . 您不应该在WM_PAINT创建线程。 Nor should you call MessageBox in WM_PAINT . 你也不应该在WM_PAINT调用MessageBox WM_PAINT is only for painting your window; WM_PAINT仅用于绘制窗口; no other logic should be executed here. 这里不应该执行其他逻辑。 The system optimizes WM_PAINT calls and behavior can get really tricky when you start overstaying your welcome in this handler. 系统优化了WM_PAINT调用,当您在此处理程序中开始过度使用欢迎时,行为会变得非常棘手。
  • You should also never cast a function call like you do with (LPTHREAD_START_ROUTINE)&thread . 你也不应该像使用(LPTHREAD_START_ROUTINE)&thread一样进行函数调用。 If your function is not the right type, change the function prototype; 如果您的函数类型不正确,请更改函数原型; don't just try and hide the compiler's warnings. 不要只是尝试隐藏编译器的警告。
  • You should probably use _beginthread , because the CRT has some initialization to perform. 你应该使用_beginthread ,因为CRT有一些初始化要执行。
  • You should pass hWnd as the thread parameter, not &hWnd . 你应该传递hWnd作为线程参数,而不是&hWnd The pointer can go out of scope and become invalid. 指针可能超出范围并变为无效。 This is a critical bug. 这是一个严重的错误。

To really see what's going on, you should fix these errors first. 要真正了解正在发生的事情,您应该首先修复这些错误。

To answer your question, the different threads have no guarantee of speed. 要回答你的问题,不同的线程无法保证速度。 After you split off a thread, there is now no telling what will happen next. 拆分线程后,现在无法告诉接下来会发生什么。 They are asynchronous. 它们是异步的。 Maybe the thread code runs faster than your main thread, maybe vice-versa. 也许线程代码比你的主线程运行得更快,反之亦然。

Keep in mind that MessageBox has its own internal message loop. 请记住, MessageBox有自己的内部消息循环。 So the callstack at WM_USER+11 (MessageBox4) is going to look something like this: 所以WM_USER+11 (MessageBox4)的callstack看起来像这样:

MessageBox (4)
WndProc (WM_USER+11)
DispatchMessage
MessageBox (3), before it's actually shown
WndProc (WM_PAINT)
DispatchMessage
WinMain

So you can see that if the thread posts the message quickly enough, it will be processed before MessageBox(3) will be shown. 所以你可以看到,如果线程足够快地发布消息,它将在MessageBox(3)显示之前被处理。

My guess is if you use a lighter debugging technique ( OutputDebugString for example), you'll observe more predictable behavior. 我的猜测是,如果你使用较轻的调试技术(例如OutputDebugString ),你会发现更可预测的行为。

WM_PAINT is one of the special low-priority messages. WM_PAINT是特殊的低优先级消息之一。

The WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, [...] are kept in the queue and are forwarded to the window procedure only when the queue contains no other messages. WM_PAINT消息, WM_TIMER消息和WM_QUIT消息[...]保留在队列中,仅当队列不包含其他消息时才转发到窗口过程。

MSDN - Queued Messages . MSDN - 排队的消息

See also The Old New Thing - Paint messages will come in as fast as you let them . 另请参阅The Old New Thing - Paint消息将以您允许的速度进入

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM