win32开发(消息机制)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
win32真正的本质其实就是消息机制。大家如果调试win32程序,就会发现win32的应用其实是一个单线程代码,这至少说明windows提供的demo code是单线程应用。既然是单线程应用,那么基本上说明这个应用是一个dead loop代码。作为循环代码来说,它不可能一直在运行,那么势必会阻塞在某一个地方,或者在某一个函数里面,否则cpu占有率会一直保持在100%了。
想到这,大家或许就清楚了,win32其实就是一个消息处理的应用。所有的处理都是通过消息回调的形式进行的。换句话说就是,你只要处理好系统发给你的消息就可以了,剩下的就是你把对应的回调函数准备好就ok了,大体就是这个意思。
// tt.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "resource.h" #define MAX_LOADSTRING 100 // Global Variables: HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text // Foward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. MSG msg; HACCEL hAccelTable; // Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_TT, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_TT); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } // // FUNCTION: MyRegisterClass() // // PURPOSE: Registers the window class. // // COMMENTS: // // This function and its usage is only necessary if you want this code // to be compatible with Win32 systems prior to the "RegisterClassEx" // function that was added to Windows 95. It is important to call this function // so that the application will get "well formed" small icons associated // with it. // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_TT); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = (LPCSTR)IDC_TT; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex); } // // FUNCTION: InitInstance(HANDLE, 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; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // // 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; TCHAR szHello[MAX_LOADSTRING]; LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: Add any drawing code here... RECT rt; GetClientRect(hWnd, &rt); DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // Mesage handler for about box. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; }这个代码大体出现过很多次。其实最重要的地方就三个,一个是MyRegisterClass函数,它最重要的意义就是定义了WndProc这个回调入口,它告诉os所有的消息响应,你直接call这个函数就可以了。当然,如果是mdi程序,还是要定义多个WNDCLASSEX的。第二个函数就是InitInstance,它的意义就是创建了一个句柄为hWnd的窗口,一个app可以创建很多个窗口,但是所有的窗口消息入口都是WndProc。这里的多窗口指的是多窗口控件。注意这里szWindowClass参数的意义。第三个函数毫无疑问,就是WndProc。它是真正处理消息的地方,这些消息有些只会出现一次,比如WM_DESTROY。有些会不停出现,比如WM_PAINT、WM_SIZE这些。至于About函数,其实它就是DialogBox的回调入口,本质上和WndProc是一样的。如果大家有兴趣,会发现About函数处理的时候,回溯堆栈里面就有WndProc,这说明他们其实都是在处理消息而已。
至于消息是什么时候获取的,大家看这个循环就可以了,
// Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
有消息的时候就处理消息,如果系统没有给app发送消息,那么GetMessage会被block住。如果换成另外一个函数PeekMessage,那基本上就是deadloop了,所以两个函数其实是不一样的。windows的消息大体分成三种,一种是WM_COMMAND,这种处理菜单、工具栏等消息。一种是WM_NOTIFY,它处理组件送过来的信息,比如list控件、tree控件等等。第三种就是系统消息,这包括WM_PAINT,WM_CREATE,WM_SIZE,WM_DESTROY等等。学到什么消息,就用什么消息就可以了。
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。