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的雏形。