在windows下创建窗口的整个过程
1告诉系统,我们要创建一个怎样的窗口
通过WNDCLASS这个结构来告诉他,我们设置这个结构的不同字段,设置好字段后
调用registerClass函数来告诉系统,我们要注册一个这样的窗口类型
2 创建窗口
窗口类型可以理解为一个模板,接下来我们要告诉操作系统,我们要以XX模板创建一个窗口,怎么告诉他呢,就是调用函数CreateWindow
3 显示窗口
创建好窗口之后,默认是没有显示的,我们还需要让他显示出来,同样是调用ShowWindow来显示,调用这个函数来更新窗口UpdateWindow
4消息循环
创建好窗口之后,不是要等待用户的操作吗?比如鼠标单击,键盘按键等等
这些东西在window系统中都是一种消息
用Wndproc这个函数来专门处理这些消息
消息机制怎么循环的(重点)
一个消息的产生到结束是怎样的一个过程,就以用户点击鼠标为例
1用户在应用程序A上面单击了左键
2操作系统捕捉到这个单击左键的消息,就把它放在操作系统管理的消息队列中
3我们的程序创建好之后就通过GetMessage函数一直不停的去检测消息队列中是否有自己关心的消息(判断交给操作系统去管)
4获取到消息之后,操作系统(可以看到我们并没有在我们代码中直接调用)就调用窗口过程函数来处理,窗口过程函数就是WndProc,他怎么知道要调用这个函数来处理呢?是因为我们在注册窗口类的时候通过这段代码wc.lpfnWndProc=Wndproc;
5 如此循环,一直不停的处理消息,直到程序结束
准备工作
1 创建变量wc用于设置窗口类的信息,如窗口的样式,设置程序图标,设置鼠标,背景颜色等等
WNDCLASS wc;
style:指定类风格,这些风格可通过按位或操作组合起来
CS_BYTEALIGNCLIENT:在字节边界上(x方向上)定位窗口的用户区域位置
CS_BYTEALLGNWINDOW:在字节边界上(x方向上)定位窗口的位置
CS_CLASSDC;该窗口类的所有窗口实例都共享一个窗口类DC;
CS_DBLCLKS:允许向窗口发送双击鼠标键的消息
CS_GLOBALCLASS:当调用CreateWindow或者CreateWindowEx函数来创建窗口时允许他的hInstance参数和
注册窗口类CS_GLOBALCLASS: 当调用CreateWindow 或 CreateWindowEx 函数来创建窗口时允许它的hInstance参数和注册窗口类时传递给
RegisterClass 的 hInstance参数不同。如果不指定该风格,则这两个 hInstance 必须相同。
CS_HREDRAW : 当水平长度改变或移动窗口时,重画整个窗口
CS_NOCLOSE : 禁止系统菜单的关闭选项
CS_OWNDC : 给予每个窗口实例它本身的DC。注意,尽管这样是很方便,但它必须慎重使用,因为每个DC大约要占800个字节的内存。
CS_PARENTDC : 将子窗口的裁剪区域设置到父窗口的DC中去,这样子窗口便可以在父窗口上绘制自身。注意,这是子窗口还是从系统缓存中获取DC,而不是使用父窗口的DC。使用该风格可以提高系统性能。
CS_SAVEBITS : 以位图形式保存被该窗口遮挡的屏幕部分,这样当给窗口移动以后,系统便可以用该保存的位图恢复屏幕移动的相应部分,从而系统不用向被该窗口遮挡的窗口发送 WM_PAINT 消息。该特性对于菜单类型的窗口比较合适,因为它通常是简短的显示一下之后便消失。设置该特性将增加显示该窗口的时间,因为它通常要先分配保存位图的内存。
CS_VREDRAW : 当垂直长度改变或移动窗口时,重画整个窗口
lpfnWndProc:指向窗口过程
cbClsExtra:指定紧随在WNDCLASS数据结构后分配的字节数。系统将其初始化为零
cbWndExtra:指定紧随在窗口实例之后分配的字节数,系统将其初始化为零,如果程序正在用WNDCLASS结构
注册一个在RC资源描述文件中用CLASS指令创建的对话框,它必须设置这个字段为DLGWINDOWEXTRA
hInstance:标识了该窗口类的窗口过程所在的模块实例的句柄,不能为NULL;
hIcon:标识了该窗口的图标,hIcon字段必须是一个图标的句柄,若hIcon字段为NULL,则无论如何用户把应用程序缩至最小时,
应用程序必须画一个图标。
hCursor:标识该窗口类的光标,hCursor必须是一个光标资源的句柄,若hCursor字段为NULL,则无论何时用户把应用程序缩至最小时,应用程序必须显示成设置光标形状
hbrBackground:标识了该窗口类的背景画笔,如果给出了颜色值,他必须时转换成下列的
HBRUSH类型之一的颜色 :
COLOR_ACTIVEBORDER
COLOR_ACTIVECAPTION
COLOR_APPWORKSPACE
COLOR_BACKGROUND
COLOR_BTNFACE
COLOR_BTHSHADOW
COLOR_BTNTEXT
COLOR_CAPTIONTEXT
COLOR_GRAYTEXT
COLOR_HIGHLIGHT
COLOR_HIGHLIGHTTEXT
COLOR_INACTIVEBORDER
COLOR_INACTIVECAPTION
COLOR_MENU
COLOR_MENUTEXT
COLOR_SCROLLBAR
COLOR_WINDOW
COLOR_WINDOWFRAME
COLOR_WINDOWTEXT
当hbrBackground字段为NULL时,每当需要绘制其用户区域时,应用程序必须自己来绘制其背景。应用程序可以通过处理WM_ERASEBKGND 消息或检查由 BeginPaint 函数填写的 PAINTSTRUCT 结构的fErase 字段来确定背景什么时候需要着色。
lpszMenuName:指向NULL结束的字符串,该字符串描述菜单的资源名,如同在资源文件里显示的名字一样。若使用一个整数标识菜单,可以使用MAKEINTRESOURCE宏。如果lpszMenuName为NULL,
那么该窗口类的窗口将没有默认菜单。
lpszClassName:指向NULL结束的字符串,或者是一个原型(atom)。若该参数是一个原型,它必须是一个有先前调用RegisterClass或者 RegisterClassEx函数产生的类原型。类原型必须作为lpszClassName的低字,高字必须为0.若lpszClassName是一个字符串,它描述了窗口类名。这个类名可以是由RegisterClass或者RegisterClassEx注册的名字,或者是任何预定义的控件类名。
WNDCLASS wc;//定义窗口类结构体变量
static TCHAR* szAppName=TEXT("noxue);//定义窗口类名称
HWND hwnd=NULL;//设置窗口句柄
MSG msg;//消息结构
//下面填充窗口类信息,如图标、鼠标样式、背景、过程函数等
wc.style=CS_HRENRAW|CS_VREDRAW;//窗口样式
wc.lpfnWndProc=WndProc;//过程函数
wc.cbClsExtra=0;//扩展字段
wc.cbWndExtra=0;//扩展字段
wc.hInstance=hlntance;//当前实例句柄
wc.hIcon=LoadIcon(hlnstance,IDI_APPLICATION);//设置程序图标
wc.hCursor=LoadCursor(NULL,IDC_ARROW);//设置鼠标
wc.hbrBackground=(HBURSH)GetstockObject(WHITE_BRUSH)://用白色填充背景
wc.lpszMenuName=NULL;//菜单
wc.lpszClassName=szAppName;
2设置完后就向操作系统注册窗口类
while(!RegisterClass(&wc))//如果注册失败,发出警告
{
MessageBox(NULL,TEXT("程序只能在windowNT下运行),szAppName,MB_ICONERROR);
return 0;
}
3 开始创建窗口
hwnd代表窗口句柄,或者说是我们现在创建窗口的一个编号,程序运行的时候会有一个变量保存当前正操作的窗口编号,如果这个变量与hwnd的值相同,说明用户正在操作该窗口
//创建窗口
hwnd=CreateWindow(szAppName,TEXT("c语言创建的第一个窗口“),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDFAULT,600,300,NULL,NULL,hlnstance,NULL);
//要注册的窗口类名,窗口标题,窗口的风格,窗口距离屏幕左上角的横纵坐标,窗口的宽度】高度】夫窗口句柄、当前实例句柄,值向一个值的指针,该值传递给窗口WM_CREATE消息,一般为NULL;
//显示窗口
ShowWindow(hwnd,iCmdshow);
//更新窗口
UpdateWindow(hwnd);
4 消息循环
//消息循环,一直停在这里,退出消息循环就表示程序结束了
while(Getmessage(&msg),NULL,0,0)//GetMessage()函数是从调用线程的消息队列中取出一条消息;对于每一个应用程序窗口线程,操作系统都会为其建立一个消息队列,当我们的窗口有消息时(即所有与这个窗口线程相关的消息),操作系统会把这个消息放到该线程的消息队列当中,我们的窗口程序就通过这个函数从自己的消息队列中取出一条一条具体的消息并进行相应操作。
{
TranslateMessage(&msg);//翻译消息,对“消息对”的转化,如对键盘的WM_KEYDOWN和WM_KEYUP消息对
//转化为WM_CHAR消息,并且将转化后的新消息投递到我们的消息队列中去,这个转化操作不会影响原来的消
//息,只会产生一个新得消息
DispatchMessage(&msg);//分发消息,函数将我们取出的消息传到窗口的回调函数去处理,可以理解为该函数将取出的消息路由给操作系统,然后操作系统去调用我们的窗口回调函数对这个消息进行处理。
}
return msg.wParam;//消息循环结束,即程序结束时 将信息返回系统
5 创建按钮
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
窗口过程函数决定了当一个窗口从外界接受到不同的信息时,所采取的不同的反应,即主要用于处理发送窗口的信息
hwnd 是处理窗口的句柄,message是消息ID,代表不同的消息类型,wParam和lParam代表了消息的附加信息,附加信息会随着消息类型的不同而不同
当键盘消息发出时,wParam的值为按下按键的虚拟键码,IParam则存储按键相关状态信息。因此如果窗口对传入的键盘消息处理时,只需要判断wParam的值即可
当鼠标消息发出时,wParam的值为鼠标按键的信息,IParam则存储鼠标的坐标,高字节代表y坐标,低字节代表x坐标,即
g_y=HIWORD(IParam),g_x=LOWORD(IParam);
在Win32API中,WPARAM和LPARAM都是32位,习惯上,我们用LPARAM传递地址,而WPARAM传递参数
如果我要判断鼠标左键是否按下,用WParam==WM_LBUTTONDOWN判断
IPARAM是PMouseHooKStruct类型,主要是获得发送窗口句柄,鼠标坐标,以及其他信息
IPARAM用的时候需要强制转换,转换成PMouseHookStruct类型
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_DESTROY://发送结束请求,里面的参数为退出码
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd,message,wParam,lParam);//调用默认的过程函数
}
win32窗口程序,基本框架
#include<windows.h>
int WINAPI WinMain(HINSTANCE hlnstance,HINSTANCE hPrevinstance,PSTR szCmdLine,int iCmdShow){
return 0;
}
int :代表返回值是整形
WINAPI:这个是调用预定
WinMain:这个是函数名,就像是main
HINSTANCE:自定义数据类型,是句柄型数据类型,相当于装入内存的资源的ID,比如我们的程序被加载到内存中,就是一个资源,就有一个编号,WinMain函数的第一个参数就是表示我们当前运行这个程序本身的资源id
hlstance:应用程序当前实例的句柄,这个值其实就是程序加载到内存空间后的首地址
hPrevInstance:应用程序的先前实例的句柄,对于同一个程序打开两次,出现两个窗口第一次打开的窗口就是先前实例的窗口
PSTR szCmdLine:
typedef char CHAR;
typedef CHAR* LPSTR,*PSTR;
这就说明PSTR就是个char类型的指针而已
那么这就是这个参数的作用就是接受命令行参数
比如 启动程序的时候用如下命令:zixue7.exe.admin password
那么这个szCmdLine中保存就是admin password这个字符串
这个值可以直接使用,也可以通过GetCommandLine()函数获取到
int iCmdShow:这个是控制窗口的显示状态,比如最大化,最小化,隐藏。
#include<Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevlnstance, PSTR szCmdLine, int iCmdShow)
{
WNDCLASS wc;
static TCHAR* szAppName = TEXT("zixue7");
HWND hwnd = NULL;
MSG msg;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbWndExtra = 0;
wc.cbClsExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszClassName = szAppName;
wc.lpszMenuName = NULL;
if (!RegisterClass(&wc))
{
MessageBox(NULL, TEXT("程序只能在windowsNT下运行"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd=CreateWindow(szAppName,TEXT("c语言编写的第一个窗口程序-www.zixue7.com"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
400,
300,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
}
return DefWindowProc(hwnd, message, wParam, lParam);
}