Window程序内部机制(上)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chen1083376511/article/details/89970499

Window程序内部机制

APIWindows操作系统本身提供各种各样的函数,而这些函数是应用程序开发人员编程时调用的接口,即应用程序接口(APIApplication Programming Interface)。
SDK软件开发包(Software Development Kit),包含了API函数库、帮助文档、使用手册、辅助工具等资源。

窗口:
1.一个应用程序至少要有一个窗口,称为主窗口。
2.一个应用程序窗口通常包含:标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框,滚动条。(注:包含的这些不能说是窗口)
窗口分为:客户区(windows应用程序管理)和非客户区(应用程序管理外观与操作)。

3.电脑开机启动,进入windows系统后,显示的桌面,是一个桌面窗口。

4.父窗口与子窗口,子窗口的形式有按钮、单选按钮、复选框、组框、文本框等。

句柄:
句柄有两种含义:
1.是一个特殊的智能指针,当一个应用程序引用其他系统的内存块或者对象的时候,就用到了这种句柄。
2.我们现在所讲编写Win32窗口程序的这种句柄,它不是一个智能指针。它是用来标识应用程序中的不同对象和同类中的不同的实例,是windows用来标识应用程序中使用唯一的整数值,即大小为4字节(在64位程序中是8字节)。windows系统大量使用了句柄来标识对象,比如:一个窗口、按钮、图标、滚动条、输出设备、控件、文件等对象,均是通过句柄来访问相应的对象的信息。

消息:

typedef struct tagMSG{

    HWND hwnd;         //标识窗口,标识某个活动窗口

    UINT messge;       //消息的标识符,不同消息对应的数值不同,数值一般为WM_XXX

    WPARAM wParam;    //消息的附加消息

    LPARAM lParam;

    DWORD time;

    POINT pt;

}MSG;


 

详细说明各个参数:
------------------
HWND一个消息一般都是和某一个窗口有关系的,用来标识窗口。发送一个消息, 就表明该消息由指定的窗口接受。
比如有ABC三个窗口。如果A窗口发送给C窗口,就标识一个窗口进行传送数据,而不是B窗口去接收数据。。

-----------------
UINT messge;消息的标识符,即消息的名称。不同消息对应的数值不同,数值由于不方便记忆,所以一般定义为为WM_XXX宏(XXX是相应的字母),在查看定义中有各自确切的数值。比如WM_CHAR,表示消息是字符消息,在VS查看定义中的确切数值为0x0102
------------------
WPARAMLPARAMWPARAM16位短整数(查看定义为WPARAM->ULONG_PTR- >unsigned int,LPARAM32位整型变量(查看定义:LPARAM->LONG_PTR->long )。这两个都是Win16系统遗留下来的产物,到了Win32API,都是32,大小是一 样的。
两者分别代表不同的含义:
在孙鑫教程中是这样说的:
WPARAM消息投递到消息队列中的时间(通常也代表控件的ID
LPARAM:消息投递到消息队列中的鼠标的当前位置
比如:某控件的通知消息、可接受消息,习惯上用LPARAM
使用自定义消息时,程序员可以任意指定这两个参数代表各自不同的含义。

--------------------

time:指定的时间发布的消息,即消息存放消息队列的时间。

--------------------

pt消息放入消息队列的鼠标坐标。

-------------------

消息队列:

含义:应用程序执行之后,系统会为该程序创建一个消息队列,用来存放程序创建 的窗口的消息。即:执行某操作(比如按下鼠标左键)-->WM_LBUTTONDOWN消息存放在消息队列--> 应用程序取出消息并作出响应。

进队消息与不进队消息:
进队消息:由系统存放到应用程序的消息队列中,然后由应用程序取出并发送。
不进队消息:系统调用窗口过程时直接发送给窗口。

WinMain函数

含义:Windows程序的入口点函数,与main函数作用相同。

 

int WINAPI WinMain(

    HINSTANCE hInstance,        //该程序当前运行的实例的句柄,是个数值。

    HINSTANCE hPrevInstance,    //当前的实例的前一个实例的句柄

    LPSTR lpCmdLine,            //指向应用程序命令行的字符串的指针,不包括执行文件名。

    int nCmdShow                //指明窗口如何显示

);


 

该函数的功能是被系统调用,作为一个32位应用程序的入口点。系统调用WinMain函数时,把这4个参数传递给应用程序。
参数说明如下:
-------------------------
hInstance该程序当前运行的实例的句柄,是个数值。(注意只有运行才算是) 。一个应用程序可以有多个实例,每一个实例都有一个句柄值。
-------------------------
hPrevInstance当前的实例的前一个实例的句柄。在Win32环境下,不起作用,数值为NULL
-------------------------
lpCmdLine是一个以空终止的字符串,指定传递给应用程序的命令行参数。例如:在D盘下有一个sunxin.txt文件,当我们用鼠标双击 这个文件时将启动记事本程序(notepad.exe),此时系统会将D:\sunxin.txt作为 命令行参数传递给记事本程序的WinMain函数,记事本程序在得到这个文件的全路 径名后,就在窗口中显示该文件的内容。
-------------------
nCmdShow指明窗口如何显示。比如最大化、最小化、隐藏等。
例如:
SW_HIDE:隐藏窗口并且激活另外一个窗口。
SW_SHOW:激活一个窗口并以原来的尺寸和位置显示窗口。

窗口的创建

创建一个窗口的步骤:
1.设计一个窗口类
2.注册窗口类
3.创建窗口
4.显示以及更新窗口

1.设计一个窗口类:

 

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;


 

style:指定窗口样式。
如:CS_NOCLOSE(禁用系统菜单的Close命令,这将导致窗口没有关闭按钮)。即命令为CS_XXX。(Class style类样式,16位常量,都只有某位为1
----------------------
lpfnWndProc函数指针,指向窗口过程函数(即回调函数),你必须使用CallWindowProc函数调用窗口过程。

窗口过程函数被调用的过程:
1)在设计窗口类时,将窗口过程函数的地址赋值给IpfnWndProc成员变量,主要作用是保存窗口过程函数的地址。
2)调用RegsiterClass(&wndclass)注册窗口类,从而系统便知道窗口过程函数的地址。(因为窗口类中包含了这个函数的地址)
3)当应用程序接收某一窗口的消息时,调用DispatchMessge(&msg)将消息回传给系统。系统则利用先前注册窗口类时得到的函数指针,调用窗口过程函数对消息进行处理。
即以上3点可这样简单的描述:
窗口类(IpfnWndProc保存窗口过程函数的地址)-->RegsiterClass(注册窗口类,间接知道窗口过程函数的地址)-->调用窗口过程函数对消息进行处理。

           Visual StudioS中,选择代码“WENDCLASS”转到“typedef WNDCLASSA WNDCLASS;”,选择“WNDCLASSA ”转到这个结构体“WNDCLASSA ”,对该结构体一个参数为“ WNDPROC lpfnWndProc;”选择“WNDPROC”转到有以下定义:
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

LRESULT是一个long类型
CALLBACK_stdcall类型。
从这可知,WNDPROC是一个CALLBACK类型的函数指针,即被定义为指向窗口过程函数的指针类型。

------------------------

cbClsExtra:WNDCLASS结构体分配和追加一定字节数的附加内存空间,称为类附加内存,用于存储类的附加信息。一般初始化为0,我们通常把该参数设置为0

------------------------

cbWndExtra窗口附加内存。应用程序可用想和部分内存存储窗口特有的数据。内存初始化为0.

------------------------

hInstance:包含窗口过程的程序的实例句柄。
-----------
hIcon:指定窗口的图标句柄。如果设置为NULL,即系统提供默认的图标。自定义图标时,可调用LoadIcon函数加载一个图标资源,返回系统分配给该图标的句柄。

HICON LoadIcon(

HINSTANCE hInstance, // handle to application instance

LPCTSTR lpIconName // name string or resource identifier

);

hInstance:图标句柄。如果加载系统的标准图标,该参数为NULL
lpIconNameLPCTSTR实际上定义为“CONST CHAR*”,即指向字符常量的指针。图标的ID是一个整数,我们需要用MAKEINTRESOURCE宏把资源ID标识符转换为LPCTSTR类型,因 此我们编写LoadIcon函数的第二参数,输入字符串即可,即"ID_IXXX"

-----------------
hCursor:指定窗口类的光标句柄。如果该参数为NULL,也将会有光标的形状。通过 LoadCursor函数加载光标,即定义如下:

HCURSOR LoadCursor(

HINSTANCE hInstance, // handle to application instance

LPCTSTR lpCursorName // name or resource identifier

);

作用和用法与hIcon相同。
-----------------
hbrBackground:指定窗口类的背景画刷句柄。通过调用GetStockObject函数得到系统的 标准画刷。定义如下:

HGDIOBJ GetStockObject(

int fnObject // stock object type

);

fnObject获取对象的类型,即比如背静画刷这个对象。
GetStockObject也可以运用到其他句柄,比如:获取画笔句柄,字体句柄等。
------------------
lpszMenuName:以空终止的字符串,指定菜单资源的名字。设置为NULL时,系统默认该窗口没有菜单。
------------------
lpszClassName:以空终止的字符串,指定窗口类的名字。

2.注册窗口类:

注册窗口类的定义如下:
ATOM RegisterClass( CONST WNDCLASS *lpWndClass // class data);
lpWndClass:指向窗口类对象的指针。

----------------------

3.创建窗口:

 

HWND CreateWindow(

LPCTSTR lpClassName,      //注册的窗口类

LPCTSTR lpWindowName,  //窗口的名字,显示在标题栏

DWORD dwStyle,    // 窗口的样式,而注意WNDCLASS中style是窗口类的样式

int x,    //窗口坐标x,CW_USEDEFAULTM默认左上角并忽略y参数

int y,    // 窗口坐标y

int nWidth,    // 窗口的宽度,CW_USEDEFAULTM默认宽度和高度

int nHeight,  // 窗口的高度

HWND hWndParent,  // 当前被创建的父窗口,子窗口有WS_CHILD样式

HMENU hMenu,       //窗口菜单的句柄

HINSTANCE hInstance, //窗口所属的应用程序实例的句柄。(是应用程序的句柄)

LPVOID lpParam    // WM_CREATE消息的附加参数传入的数据指针。创建多文档界面的客户窗口时,
                  //该参数必须指向CLIENTCREATESTRUCT结构体。其他窗口设置为NULL。

);

 


dwStyle 窗口的样式。该参数的值比如下:
WS_OVERLAPPED:层叠的窗口,具有一个标题栏和一个边框。
WS_CAPTION创建一个标题栏的窗口。
WS_SYSMENU:创建一个带有系统菜单的窗口,必须同时设定WS_CAPTION
--------------------
如果CreateWindow函数返回系统为该窗口分配的句柄,否则,返回NULL

4.显示以更新窗口:

1)显示窗口 

BOOL ShowWindow(

HWND hWnd, // handle to window

int nCmdShow // show state,比如隐藏该窗口,SW_HIDE

);

--------------------
2)更新窗口 

BOOL UpdateWindow(

HWND hWnd // handle to window

);

UpdateWindow函数发送WM_PAINT消息刷新窗口,UpdateWindowWM_PAINT消息直接发送给了窗口过程函数进行处理,并没有放入消息队列中。

消息循环

 

BOOL GetMessage(

LPMSG lpMsg, // message information

HWND hWnd, // handle to window

UINT wMsgFilterMin, // first message

UINT wMsgFilterMax // last message

);



GetMessage从线程的消息队列中取出的消息信息将保存在该结构体对象中。
---------------
lpMsg指向消息(MSG)结构体
---------------
hWnd指定接受属于哪一个窗口的消息。通常设置为NULL,用于接收属于调用线程的所有窗口的窗口消息。 
---------------

wMsgFilterMin指定要获取的消息的最小值,通常设置为0.
wMsgFilterMax指定要获取的消息的最大值。
如果wMsgFilterMinwMsgFilterMax都设置为0,则接收所有消息。
-----------------------------
GetMessage
的返回值:
1.返回值为-1:意思是接收出现了错误。比如hWnd是无效的窗口句柄或者lpMsg是无效的指针。 
2.
返回值为0:接收到WM_QUIT的消息并结束消息循环。
3.返回值为正整数:正常接受消息,保证程序处于运行状态
------------------------------------------------------------
编写的消息循环代码如下:

 

MSG msg;

while(GetMessge(&msg,NULL,0,0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

 

TranslateMessage函数:用于将虚拟键消息转换成字符消息。将虚拟键(比如WM_KEYDOWN)转换成一条WM_CHAR消息。
即调用该函数,字符被取出的过程:
1.调用GetMessge函数并在while判断其返回值是否为真。
2.若为真,执行TranslateMessage函数,转换成字符消息。
3.下一while循环,调用GetMessge函数时,字符消息被取出,把字符消息放入消息队列中。。
-------------------
DispatchMessage
函数分派一个消息到窗口过程(实际上是DispatchMessage函数将消息回传给操作系统,而操作系统调用窗口过程函数对消息进行处理),由窗口过程函数对消息进行处理。
DispatchMessage函数对消息处理的详细步骤:
1.操作系统接收到应用程序的窗口消息,将消息放入该应用程序的消息队列中。
2.应用程序在消息循环中调用GetMessge函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理。例如:放弃对某些消息的响应,或者调用TranslateMessage产生新的消息。
3.调用DispatchMessage将消息回传给操作系统。
4.系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理(即系统给应用程序发送了消息)。
-------------------
GetMessge实现相似的功能的PeerMessage函数

 

BOOL PeekMessage(

LPMSG lpMsg, // message information

HWND hWnd, // handle to window

UINT wMsgFilterMin, // first message

UINT wMsgFilterMax, // last message

UINT wRemoveMsg // removal options

);

 

前面4个参数和GetMessage函数的4个参数作用相同。
wRemoveMs指定消息获取的方式。若设置PM_NOREMVE,消息不会从消息队列中被移除。
-----------------------
SendMessage
函数和PostMessage函数的区别:
SendMessage函数:SendMessage函数将消息直接发送给窗口,并调用该窗口的窗口过程进行处理。在窗口过程对消息处理完毕之后,该函数才返回(所以SendMessage发送的消息为不进队消息)。
PostMessage函数:PostMessage将消息放入与创建窗口的线程相关联的消息队列后立即返回。
PostThreadMessage函数:用于向线程发送消息,MSG结构体中的hwndNULL

猜你喜欢

转载自blog.csdn.net/chen1083376511/article/details/89970499