Windows程序内部运行原理简介
Windows应用程序,操作系统,计算机硬件之间的相互关系如下图:
向下的箭头③表示应用程序可以通知操作系统执行某个具体的动作;
向上的箭头④表示操作系统能够将输入设备的变化上传给应用程序。
每个应用程序都维护一个消息队列(严格来说应该是每个GUI线程维护一个各自的消息队列)。
大致运行流程应该是这样的:当输入输出设备产生变化时(如用户在某个程序活动时按了一下键盘),操纵系统会马上感知到这一事件,并且能够知道用户按下的是哪一个键,
操作系统并不决定对这一事件如何作出反应,而是将这一事件转交给应用程序,这时应用程序可以通知操作系统执行某个具体的动作(如操作系统能够控制声卡发出声音),
但它并不知道应该何时发出何种声音,需要应用程序告诉操作系统该发出什么样的声音。(顺序为②―〉④―〉③―〉①)
下面贴一个win32下完整的创建窗口过程代码:
#include <Windows.h> #include <stdio.h> /************************ typedef struct tagMSG { HWND hwnd; //代表消息所属的窗口 UINT message; //消息代号 WPARAM wParam; //unsigned int类型,对消息进行补充说明 LPARAM lParam; //long类型,对消息进行补充说明 DWORD time; //发出消息的时间 POINT pt; //鼠标的当前位置 } MSG; ************************/ LRESULT CALLBACK //实际上是__stdcall。__stdcall与__cdecl是两种不同的函数调用习惯,定义了参数的传递顺序、堆栈清除等 //由于VC++程序默认的编译选项是__cdecl,所以在VC++中调用这些__stdcall习惯的API函数,必须在声明这些函数的原型时加上__stdcall修饰符 winPanProc( HWND hwnd, //代表消息所属的窗口 UINT uMsg, WPARAM wParam, LPARAM lParam ); /************************************ 这个函数是应用程序的基础,当Windows操作系统启动一个程序时, 它调用的就是该程序的Winmain函数, 用户的操作所产生的消息正是经过这个函数的处理派送到对应的对象中进行处理。 当WinMain函数结束或返回时,Windows应用程序结束 *************************************/ int WINAPI WinMain( HINSTANCE hInstance, // 当前运行的实例句柄 HINSTANCE hPrevInstance, // 当前实例的上一个正在运行的,由同一个应用程序所产生的实例的句柄 LPSTR lpCmdLine, // 一个字符串,里面包含有传递给应用程序的参数串 int nCmdShow // 程序的窗口应该如何显示,如最大化,最小化,隐藏等 ) { /********************************************************************************************************** 1、产生并显示程序的主窗口。 窗口创建并显示后,用户便可以在窗口上进行各种操作了, 用户的操作及程序状态的变化都以消息的形式放到了应用程序的消息队列中。 */ /* 设计一个窗口类; window的10个属性,缺一则窗口无法显示*/ WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wndcls.hCursor=LoadCursor(NULL,IDC_CROSS); wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); wndcls.hInstance = hInstance; //指定提供回调函数的程序实例句柄 wndcls.lpfnWndProc = winPanProc; //指定这一类型窗口的过程函数,也称回调函数 wndcls.lpszClassName = L"panWnd"; //指定这一类型窗口的名称,是字符串变量。 wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW; //指定这一类型窗口的样式,针对一个大类 /*注册窗口类;*/ RegisterClass(&wndcls); /*创建窗口;*/ HWND hwnd; hwnd = CreateWindow( L"panWnd", //WNDCLASS的lpszClassName成员里指定的名称 L"UESTC", //指定产生的窗口实例上显示的标题文字 WS_OVERLAPPEDWINDOW,//窗口实例的样式,针对个别 0,0, 500, 500, //窗口的大小、宽度、高度 NULL, //设置桌面成为当前窗口的父窗口 NULL, //指定了窗口的菜单或子窗口句柄 hInstance, //窗口所属的应用程序的句柄 NULL //窗口附加补充信息 ); /*显示及刷新窗口.*/ ShowWindow(hwnd, SW_SHOWNORMAL); //ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); /********************************************************************************************************** 2、从消息队列循环取走消息,并将消息派发到窗口过程函数中去处理。 当消息循环取到一条WM_QUIT消息时,将结束循环,WinMain函数返回,结束整个程序的运行。 如果WinMain在消息循环之前返回,程序没有正常运行,返回值为0。 如果在消息循环之后返回,返回值为WM_QIUT消息的wParam参数。 */ /* Windows中有两种类型的消息队列: 系统消息队列: 这是一个系统唯一的Queue,设备驱动(mouse, keyboard)会把操作输入转化成消息存放在系统队列中, 然后系统会把此消息放到目标窗口所在的线程的消息队列(thread-specific message queue)中等待处理 线程消息队列: 每一个GUI线程都会维护这样一个线程消息队列。(这个队列只有在线程调用GDI函数时才会创建,默认不创建)。 然后线程消息队列中的消息会被送到相应的窗口过程(WndProc)处理. 注意: 线程消息队列中WM_PAINT,WM_TIMER只有在Queue中没有其他消息的时候才会被处理, WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。 */ /* 队列消息(Queued Messages):消息会先保存在消息队列中,消息循环会从此队列中取消息并分发到各窗口处理。 如鼠标,键盘消息。 非队列消息(NonQueued Messages):消息会绕过系统消息队列和线程消息队列直接发送到窗口过程被处理。 如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_WINDOWPOSCHANGED */ MSG msg; /* GetMessage:从队列中直接取走消息 PeekMessage:只取消息的一个副本,并不将消息从队列中取走 */ while(GetMessage( &msg, //接受消息的变量的指针 NULL, //指定了接收属于哪个窗口的消息 0, 0 //指定了接受某一范围内的消息 )) { /* TranslateMessage: 把一个virtual-key消息转化成字符消息(character message),并放到当前线程的消息队列中,消息循环下一次取出处理。 TranslateAccelerator: 将快捷键对应到相应的菜单命令。它会把WM_KEYDOWN 或 WM_SYSKEYDOWN转化成快捷键表中相应的WM_COMMAND 或WM_SYSCOMMAND消息, 然后把转化后的 WM_COMMAND或WM_SYSCOMMAND直接发送到窗口过程处理, 处理完后才会返回。 */ //将msg结构传给Windows,对取到的消息进行转换 TranslateMessage(&msg); //又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。 DispatchMessage(&msg); } return 0; } LRESULT CALLBACK winPanProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { /* 消息类型: 系统定义消息:在SDK中事先定义好的消息,非用户定义的,其范围在[0x0000, 0x03ff]之间, 可以分为以下三类: 窗口消息:与窗口的内部运作有关,如创建窗口,绘制窗口,销毁窗口等 命令消息:与处理用户请求有关, 如单击菜单项或工具栏或控件时,就会产生命令消息。 WM_COMMAND, LOWORD(wParam)表示菜单项,工具栏按钮或控件的ID。 如果是控件, HIWORD(wParam)表示控件消息类型 控件通知:控件通知消息,这是最灵活的消息格式 其Message, wParam, lParam分别为:WM_NOTIFY, 控件ID,指向NMHDR的指针。 NMHDR包含控件通知的内容, 可以任意扩展。 程序定义消息:用户自定义的消息,对于其范围有如下规定: WM_USER: 0x0400-0x7FFF (ex. WM_USER+10) WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4) RegisterWindowMessage: 0xC000-0xFFFF */ switch(uMsg) { case WM_CHAR: break; case WM_LBUTTONDOWN: break; case WM_PAINT: break; case WM_CLOSE: if(IDYES == MessageBox(hwnd, L"确定退出?", L"panWnd", MB_YESNO)) { DestroyWindow(hwnd); } break; case WM_DESTROY: /* PostMessage:发送的消息是队列消息,它会把消息直接Post到应用程序的消息队列中,不等程序返回就退出 SendMessage:发送的消息是非队列消息, 被直接送到窗口过程处理,应用程序处理完此消息后,它才返回 PostThreadMessage:用于向线程发送消息 */ PostQuitMessage(0); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; }