欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Window程序内部机制(上)

程序员文章站 2024-03-21 17:06:58
...

Window程序内部机制

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

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

Window程序内部机制(上)

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

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

Window程序内部机制(上)

句柄:
句柄有两种含义:
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