Windows程序内部运行原理
一、基本原理
1、Windows应用程序,操作系统,计算机硬件之间的相互关系
一个例子:
汽车厂家生产汽车好比应用程序创建窗口,用户使用汽车好比操作系统管理窗口,某种汽车在销售前就指定好了修理站(类似回调函数),当用户的汽车出现故障后(类似窗口收到消息),汽车用户(类似操作系统)自己直接找到修理站去修理,不用厂家(类似应用程序)亲自将车送到修理站去修理,但修理站还得由厂家事先建造好。
2、API
应用程序是以函数调用的方式来通知操作系统执行相应的功能的。操作系统所能够完成的每一个特殊功能通常都有一个函数与其对应。操作系统把它所能够完成的功能以函数的形式提供给应用程序使用,应用程序对这些函数的调用就叫做系统调用,这些函数的集合就是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称Windows API。如CreateWindow就是一个API函数,应用程序中调用这个函数,操作系统就会按照该函数提供的参数信息产生一个相应的窗口。
3、Windows程序的入口函数
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
//在这里,句柄是一个标识符,用来标识资源,类似指针,一般以H**开头//的标识符都是句柄
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line//传递给程序的命令行//参数
int nCmdShow // show state
);
4、创建一个完整的窗口需要经过下面四个操作步骤:
1)设计一个窗口类;//WNDCLASS wndcls;
typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc;//函数指针,指向本窗口的处理函数,//也称为窗口过程函数
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
2)注册窗口类;//RegisterClass(&wndcls);
3)创建窗口;//hwnd=CreateWindow(//...);
4)显示及更新窗口。
//ShowWindow(hwnd,SW_SHOWNORMAL);
//UpdateWindow(hwnd);
基于一个窗口类创建的窗口使用同一个窗口过程。
5、消息和消息队列
Windos程序是一种事件驱动方式和程序设计模式,主要是基本消息的。
操作系统将每个感知的事件都包装成一个称为消息的结构体MSG来传递给应用程序。MSG结构如下示:
typedef struct tagMSG {
HWND hwnd; //一个消息总是与某个窗体相关联的
UINT message; //消息标识符,数值,对应WM_XXX的宏
WPARAM wParam; //附加消息
LPARAM lParam; //附加消息
DWORD time; //投递时间
POINT pt; //鼠标当前位置
} MSG;
而应用程序则循环不断地从消息队列中取出消息,并进行响应,这就是消息机制。注意到,操作系统为不同类型的应用程序维护不同的消息队列。消息也分进队和不进队消息,进队的消息由系统放入到应用程序的消息队列中,不进队的消息在系统调用窗口过程时直接发送给窗口。不管是进队还是不进队消息,最终都由系统调用窗口过程函数对消息进行处理。
发送消息可以用SendMessage和PosMessage,前者为不进队消息,将消息直接发送到窗口,并调用窗口过程函数处理,处理完后,返回。后者是将消息放入与创建窗口相关联的消息队列后立即返回。
6、完整的Win32程序
1)WinMain函数的定义
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
);
2)创建一个窗口
HWND CreateWindow(
LPCTSTR lpClassName, // registered class name
LPCTSTR lpWindowName, // window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // menu handle or child identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam // window-creation da
);
3)进行消息循环
通常如下表示:
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);//将虚拟键转换成字符消息,并投放到调用
//线程的消息队列中,不改变原来的消息
DispatchMessage(&msg);//分派一个消息到窗口函数,实际上就是//将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理//响应
}
其中GetMessage原型为:
BOOL GetMessage(
LPMSG lpMsg, // message information,是个指针,指向消息结构体
HWND hWnd, // handle to window,与该消息相对应的窗口句柄,//NULL,则为所有窗口
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax // last message
);
If the function retrieves a message other than WM_QUIT, the return value is nonzero.
If the function retrieves the WM_QUIT message, the return value is zero. If there is
an error(比如无效的窗口句柄或lpMsg是无效的指针时), the return value is -1.
4)窗口过程函数
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window,要处理的窗口类
UINT uMsg, // message identifier,要处理的消息类型
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
6、其它
1)用HGDIOBJ GetStockObject(int fnObject // stock object type);
来得到系统的标准画涮。
2)Win32 Application 与win32 console application的区别
win32 console application基于命令行,一般用于教学,类dos界面
win32 application基于窗口,常用win32 sdk或mfc等开发,实际应用的都是win32 application
3)__stdcall与__cdecl
__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能使用该调用约定。由于每一个使用__cdecl约定的函数都要包含清理堆栈的代码,所以产生的可执行文件大小会比较大。__cdecl可以写成_cdecl。
__stdcall调用约定用于调用Win32 API函数。采用__stdcal约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。__stdcall可以写成 _stdcall。
__fastcall约定用于对性能要求非常高的场合。__fastcall约定将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。 __fastcall可以写成_fastcall。
特别说明
(1)在默认情况下,采用__cdecl方式,因此可以省略.
(2)WINAPI一般用于修饰动态链接库中导出函数
(3)CALLBACK仅用于修饰回调函数
4)用WM_PAINT消息来刷新窗口,UpdateWindow将WM_PAINT消息直接发送给了窗口过程函数进行处理。
参考:
1、VC++深入详解
2、http://big5.china.com/gate/big5/ghostdepth.blog.china.com/200807/2929745.html