原创 窗口处理函数

2012-12-2 22:00 1443 14 14 分类: 工程师职场

在win32程序的消息循环函数中

  while (GetMessage (&msg, NULL, 0, 0))
  {   
  TranslateMessage (&msg) ;   
  DispatchMessage (&msg) ;   
  }

DispatchMessag()会调用WndProc()窗口过程函数,直到WndProc()处理完后DispatchMessag()才返回,继续消息循环检索下一条消息,在此之前while()消息循环会停顿掉

在WndProc()函数处理过程中如果又产生了另一个消息B,则必须等这个消息B处理完后wndProc()函数才会返回,而这个消息B也是要由WndProc()函数处理的,即在WndProc()调用的过程中系统接受到消息B后嵌套调用了WndProc(),但此时while消息循环已经停顿了


根据试验,在WndProc()处理过程中发出消息B后,系统通过某种方式跳过while()消息循环获取了消息B,并且又调用了一个WndProc()来处理消息B,等消息B处理完后第二个WndProc()才返回,然后第一个WndProc()继续执行,等第一个WndProc()执行完毕后DispatchMessage()才会返回,然后继续下一轮while()循环


那么,系统是怎样接受到这个消息B的呢?系统是怎样绕过while消息循环获取消息B并且调用第二个WndProc()的,这中间具体发生了哪些事情?网上和书上都没找到详细的处理过程……
请记住这关键的一点:
WndProc()是可重入的,就是说:
WndProc()函数虽然没结束,但一个
API调用(SendMessage()等)会立即
再次调用WndProc()。
明白这一点对于理解windows的消息循环
很重要

getmessage取完之后就返回,peekmessage会挂着直到结束

SendMessage是直接发送到窗口过程的,借助系统的User32模块,并不进入队列

我不知道你是怎么试的
你可以运行下面的代码

C/C++ code??
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include 
#include 
#include
#define msgv (WM_USER+123)
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    switch(uMsg)
    {
        case msgv:
        puts("enter msgv");
        PostMessageW(hwnd,msgv+1,0,0);
        //SendMessageW(hwnd,msgv+1,0,0);
        puts("leave msgv");
        return 0;
        case msgv+1:
        puts("enter msgv+1");
        puts("leave msgv+1");
        return 0;
        case WM_CLOSE:DestroyWindow(hwnd);
        return 0;
case WM_DESTROY:PostQuitMessage(0);
        return 0;
        default:return DefWindowProcW(hwnd,uMsg,wParam,lParam);
    }
}
 
int main()
{
 
    static WNDCLASS.EXW WindowClass={sizeof(WNDCLASS.EXW),0,WindowProc,0,0,0,0,0,0,0,L"aa",0};MSG msg;HWND hwnd;
    WindowClass.hInstance=GetModuleHandleW(0);
    hwnd=CreateWindowExW(0,(wchar_t*)(unsigned long)RegisterClass.ExW(&WindowClass),0,WS_VISIBLE|WS_OVERLAPPEDWINDOW,200,200,200,200,0,0,0,0);
    PostMessageW(hwnd,msgv,0,0);
    while(GetMessageW(&msg,0,0,0))DispatchMessageW(&msg);
    return 0;
}



PostMessage时输出是
enter msgv
leave msgv
enter msgv+1
leave msgv+1

SendMessage时输出是
enter msgv
enter msgv+1
leave msgv+1
leave msgv

1、在win32程序的while消息循环中,DispatchMessag()会调用WndProc()窗口过程函数,直到WndProc()处理完后DispatchMessag()才返回,继续消息循环检索下一条消息,在这之前while()消息循环会停顿掉

2、在WndProc()函数处理过程中又产生了另一个消息B,如果消息B是由PostMessage()发送的,则消息B会发送到消息队列中,然后WndProc()继续执行,执行完毕后返回到DispatchMessag()函数,继续消息循环检索下一条消息,检索到消息B后才开始执行消息B,

3、在WndProc()函数处理过程中又产生了另一个消息B,如果消息B是由SendMessage()发送的,则必须等这个消息B处理完后wndProc()函数才会返回,这时SendMessage()会调用 SendMessageWorker(),SendMessageWorker()判断目标窗口的线程是不是当前线程,如果不是就进入内核调用NtUserMessageCall(),否则就在用户模式调用窗口过程函数WndProc()

虽然此时while()消息循环已经停顿了,但在WndProc()调用的过程中通过SendMessageWorker()间接嵌套调用了WndProc()


测试代码如下:

#include 

#define MSG_A (WM_USER+123)
#define MSG_B (WM_USER+321)

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   PSTR szCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("Hello Windows");
    HWND         hwnd;
MSG          msg;
WNDCLASS     wndclass;

wndclass.style         = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc   = WndProc;
wndclass.cbClExtra    = 0;
wndclass.cbWndExtra    = 0;
wndclass.hInstance     = hInstance;
wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName  = NULL;
wndclass.lpszClassName = szAppName;

if(!RegisterClass(&wndclass))
{
        MessageBox(NULL, TEXT("Register Class Failed !"), "Error", MB_ICONERROR);
    return 0;
}

    hwnd = CreateWindow( szAppName,
TEXT("Hello Windows"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{    
HDC hdc;
static int i;

switch(message)
{
case WM_LBUTTONDOWN:

 i = 0;
 hdc=GetDC(hwnd);
 TextOut(hdc,100, 100 + (i++)*20, TEXT("Use SendMessage( )  Result :"),strlen(TEXT("Use PostMessage( )  Result :")));
 i++;

 TextOut(hdc,100, 100 + (i++)*20, TEXT("LBUTTONDOWN Start"),strlen(TEXT("LBUTTONDOWN Start")));
 SendMessage(hwnd, MSG_A, 0, 0);
 TextOut(hdc,100, 100 + (i++)*20, TEXT("LBUTTONDOWN Over"),strlen(TEXT("LBUTTONDOWN Over")));
   ReleaseDC(hwnd,hdc);  


 return 0;

case MSG_A:

 hdc=GetDC(hwnd);
 TextOut(hdc,100, 100 + (i++)*20, TEXT("MSG_A Start"),strlen(TEXT("MSG_A Start")));
 SendMessage(hwnd, MSG_B, 0, 0);
 TextOut(hdc,100, 100 + (i++)*20, TEXT("MSG_A Over"),strlen(TEXT("MSG_A Over")));
 ReleaseDC(hwnd,hdc); 

 return 0;

case MSG_B:

 hdc=GetDC(hwnd);
 TextOut(hdc,100, 100 + (i++)*20, TEXT("MSG_B Start"),strlen(TEXT("MSG_B Start")));
 TextOut(hdc,100, 100 + (i++)*20, TEXT("MSG_B Over"),strlen(TEXT("MSG_B Over")));
 ReleaseDC(hwnd,hdc); 
 return 0;

case WM_DESTROY:
 PostQuitMessage(0);
 return 0;
}

return DefWindowProc(hwnd, message, wParam, lParam);
}
 

测试结果一:

Use SendMessage() Result :

LBUTTONDOWN Start
MSG_A Start
MSG_B Start
MSG_B Over
MSG_A Over
LBUTTONDOWN Over



测试结果二:

Use PostMessage() Result :

LBUTTONDOWN Start
LBUTTONDOWN Over
MSG_A Over
MSG_A Start
MSG_B Start
MSG_B Over
 

DispatchMessage只分发队列消息

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
14
关闭 站长推荐上一条 /3 下一条