windows程序基础

windows程序的进入点是WinMain函数,shell侦测到使用这想执行一个windows程序时,会调用加载器把该程序加载,然后调用C startup code,

后者再调用WinMain函数,开始执行程序,WinMain的四个参数由系统传递进来。

 

windows程序必须进行初始化工作,为的是产生应用程序的工作舞台:窗口。

API函数CreateWindow会完成这个任务。窗口产生之前的属性必须设定好,属性包括【外貌】和【行为】。外貌包括边框,颜色,标题等,行为

指窗口收到消息后的反应。

程序需要在产生窗口前先调用AOI函数RegisterClass设定属性(称为注册窗口类别)。

RegisterClass需要WNDCLASS数据结构作为参数

                                      

 

 

如上图所示,其中wc.lpfnWndProc所指定的函数就是窗口的行为中枢,即窗口函数。

 

CreateWindow只负责产生窗口,并不显示,

 

之后需要调用ShowWindow将其显示在屏幕上。然后调用UpdateWindow把WM_PAINT传给窗口,驱动窗口的绘图动作。

 

 

//入口函数
int
CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { if (!hPrevInstance) {
if
(!InitApplication(hInstance)) {
return
(FALSE);
}
}
if (!InitInstance(hInstance, nCmdShow)) {
return
(FALSE);
} ... }
/ //--------------------------------------------------// BOOL InitApplication(HINSTANCE hInstance) { WNDCLASS wc; ... return (RegisterClass(&wc)); }/ //--------------------------------------------------// BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hWnd = CreateWindow(...); ... }

 

早期窗口类别只需要注册一次,即可供同一程序的后续每一个执行实例使用(因为所有进程共用一个地址空间),所以把RegisterClass安排在InitApplication中(只有第一个进入的执行个体才会执行),

进程是否是某个程序的第一个执行实例,由WinMain的参数hPrevInstance判断,值由系统传入。

产生窗口是每一个执行实例都要做的工作,CreateWindow因此被放在InitInstance(任何执行实例都会进入)。

Win32程序已经有所变化,每个执行实例都有自己的地址空间,不会再共享同一窗口。Win32系统令hPrevInstance永远为0,故仍把CreateWindow和RegisterClass按旧习惯安排。

MFC把InitInstance和InitApplication两个函数包装成CWin.APP的两个虚函数成员。

 

 

初始化工作完成后,WinMain进入消息循环

while(GetMessage(&msg,...))
{ TranslateMessage(
&msg);//转换键盘消息 DispatchMessage(&msg);//分派消息 }
TranslateMessage将键盘消息转化,DispatchMessage将消息传给窗口函数去处理。消息发生时,操作系统已经根据当时状态表明了所属窗口,所属窗
口已经标明了窗口函数(wc.lpfnWndProc),
DispatchMessage就可以自己找到窗口函数。DispatchMessage经过USER模块 协助,才把消息交到窗
口函数手中。
窗口函数通常利用switch/case方式判断消息种类,已决定处理方式。由于他是被Windows系统调通的,所以这是一种回调函数。
程序进行过程中,消息由输入装置,经由消息循环的抓取,远远传送给窗口并进而传送到窗口函数。
窗口函数的形式:
LRESULT CALLBACK WndProc( HWND hwnd, 
                          UINT message, 
                          PWARAM wParam, 
                          LPARAM lParam);

 

不论什么消息,都必须被处理,所以switch/case中default处理必须调用DefWindowProc,这是Windows内部预设的而消息处理函数
wParam和lParam意义因消息不同而异。

窗口函数设计成回调函数的原因:除了程序员需要调用他,有时候操作系统也要调用窗口函数,设计成回调函数便能开放出一个接口给操作系统用。


把窗口函数的内容涉及的更模块化一些:(MFC消息映射表)

首先定义一个
MSGMAP_ENTRY结构和一个dim宏

MSGMAP_ENTRY{
             UINT nMessage;
             LONG(*pfn)(HWND,UINT,WPARAM,LPARAM);
              };

#define dim(x) (sizeof(x)/sizeof(x[0]))
MSGMAP_ENTRY第二个元素pfn是一个函数指针,要用此指针所指的函数处理nMessage

接下来,组织两个数组_messageEntries[]和_commandEntries[],把程序中欲处理的消息以及消息处理例程的关联性建立起来
// 消息与处理例程之对照表格
struct MSGMAP_ENTRY _messageEntries[] =
{
      WM_CREATE, OnCreate,
      WM_PAINT, OnPaint,
      WM_SIZE, OnSize,
      WM_COMMAND, OnCommand,
      WM_SETFOCUS, OnSetFocus,
      WM_CLOSE, OnClose,
      WM_DESTROY, OnDestroy,
} ; 
//Command-ID与处理例程之对照表格
struct
MSGMAP_ENTRY _commandEntries = { IDM_ABOUT, OnAbout, IDM_FILEOPEN, OnFileOpen, IDM_SAVEAS, OnSaveAs, } ;

 

这时窗口函数设计为:
//----------------------------------------------------------------------
//窗口函数
//----------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
         int i;
         for(i=0; i < dim(_messageEntries); i++) 
{ //消息对照表 if (message == _messageEntries[i].nMessage) {
return
((*_messageEntries[i].pfn)(hWnd, message, wParam, lParam));
} }
return(DefWindowProc(hWnd, message, wParam, lParam)); } //---------------------------------------------------------------------- // OnCommand --专门处理WM_COMMAND //---------------------------------------------------------------------- LONG OnCommand(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int i; for(i=0; i < dim(_commandEntries); i++)
{ //命令项目对照表 if (LOWORD(wParam) == _commandEntries[i].nMessage)
{
return((*_commandEntries[i].pfn)(hWnd, message, wParam, lParam));
} }
return(DefWindowProc(hWnd, message, wParam, lParam)); } //---------------------------------------------------------------------- LONG OnCreate(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam) { ... } //---------------------------------------------------------------------- LONG OnAbout(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam) {. .. }/ /----------------------------------------------------------------------

这样一来WndProc和Oncommand就不必再改变,每有新消息要处理,只要在_messageEntries[]和_commandEntries[]两个数组加上新元素,并针对
新消息撰写新的处理例程即可。这是MFC MessageMap的雏形。

 

posted @ 2020-03-29 19:38  坦坦荡荡  阅读(212)  评论(0编辑  收藏  举报