基础
应用程序分类
控制台程序 Console
DOS程序,本身没有窗口,通过Windows DOS窗口执行
窗口程序
拥有自己的窗口,可以与用户交互
库程序
存放代码、数据的程序,执行文件可以从中取出代码执行和获取数据
静态库程序:扩展名LIB,在编译链接程序时,将代码放入到执行文件中。
动态库程序:扩展名DLL,在执行文件执行时从中获取代码。
应用程序对比
入口函数
控制台程序 - main
窗口程序 - WinMain
动态库程序 - DllMain
静态库程序 - 无入口函数
文件存在方式
控制台程序、窗口程序 - EXE文件
动态库程序 - DLL文件
静态库程序 - LIB文件
编译工具
编译器CL.EXE 将源代码编译成目标代码.obj
链接器LINK.EXE 将目标代码、库链接生成最终文件
资源编译器RC.EXE (.rc)将资源编译,最终通过链接器存入
最终文件路径 :C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin
库和头文件
Windows库
kernel32.dll - 提供了核心的API,例如进程、线程、内存管理等
user32.dll - 提供了窗口、消息等API
gdi32.dll - 绘图相关的API
路径:C:\Windows\System32
头文件
windows.h - 所有windows头文件的集合
windef.h - windows数据类型
winbase.h - kernel32的API
wingdi.h - gdi32的API
winuser.h - user32的API
winnt.h - UNICODE字符集支持
路径: C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include
相关函数
int WINAPI WinMain(
HINSTANCE hInstance,//当前程序的实例句柄
HINSTANCE hPrevInstance, //当前程序前一个实例句柄
LPSTR lpCmdLine,//命令行参数字符串
int nCmdShow //窗口的显示方式
);
int MessageBox(
HWND hWnd,//父窗口句柄
LPCTSTR lpText, //显示在消息框中的文字
LPCTSTR lpCaption, //显示在标题栏中的文字
UINT uType //消息框中的按钮、图标显示类型
);// 返回点击的按钮ID
程序编译过程
编译环境准备
VCVARS32.BAT
编译程序 - CL
CL.EXE –c xxx.c
链接程序 - LINK
LINK.EXE xxx.obj xxx.lib
执行
编写资源的文件 -.rc资源脚本文件
编译rc文件 - RC.EXE
将资源链接到程序中 - LINK.EXE
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mNa6tHpS-1691205905982)(image-20230804165452925.png)]
第一个windows窗口
窗口创建过程
定义WinMain函数
定义窗口处理函数 (自定义,处理消息)
注册窗口类(向操作系统写入一些数据)
创建窗口( 内存中创建窗口 )
显示窗口( 绘制窗口的图像 )
消息循环(获取/翻译/派发消息)
消息处理
代码
#include<windows.h>
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns,
HINSTANCE hPreIns,
LPSTR lpCmdLine,
int nCmdShow)
{
//注册窗口类
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统
//在内存创建窗口
HWND hWnd=CreateWindow("Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = { 0 };
while (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
WINDOWS窗口
注册窗口类
窗口类的概念
窗口类包含了窗口的各种参数信息的数据结构。
每个窗口都具有窗口类,基于窗口类创建窗口。
每个窗口类都具有一个名称,使用前必须注册到系统。
窗口类的分类
系统窗口类
系统已经定义好的窗口类,所有应用程序都可以直接使用。
不需要注册,直接使用窗口类即可。系统已经注册好了。
例如:
按钮 - BUTTON
编辑框 - EDIT
全局及局部窗口类
应用程序全局窗口类
由用户自己定义,当前应用程序所有模块都可以使用。
应用程序局部窗口类
由用户自己定义,当前应用程序中本模块可以使用。
注册窗口类的函数
ATOM RegisterClass(
CONST WNDCLASS *lpWndClass //窗口类的数据
); //注册成功后,返回一个数字标识。
注册窗口类的结构体
typedef struct _WNDCLASS {
UINT style; //窗口类的风格
WNDPROC lpfnWndProc; //窗口处理函数
int cbClsExtra; //窗口类的附加数据buff的大小
int cbWndExtra; //窗口的附加数据buff的大小
HINSTANCE hInstance; //当前模块的实例句柄
HICON hIcon; //窗口图标句柄
HCURSOR hCursor; //鼠标的句柄
HBRUSH hbrBackground; //绘制窗口背景的画刷句柄
LPCTSTR lpszMenuName; //窗口菜单的资源ID字符串
LPCTSTR lpszClassName; //窗口类的名称
} WNDCLASS, *PWNDCLASS;
style窗口类风格
微软建议不使用全局窗口类
应用程序全局窗口类的注册,需要在窗口类的风格中增加 CS_GLOBALCLASS。
例如:
WNDCLASS wce = {0};
wce.style = ….|CS_GLOBALCLASS;
应用程序局部窗口类:在注册窗口类时,不添加CS_GLOBALCLASS风格。
CS_HREDRAW - 当窗口水平变化时,窗口重新绘制
CS_VREDRAW - 当窗口垂直变化时,窗口重新绘制
CS_DBLCLKS - 允许窗口接收鼠标双击
CS_NOCLOSE - 窗口没有关闭按钮
窗口创建
创建窗口的函数
CreateWindow/CreateWindowEx
HWND CreateWindowEx(
DWORD dwExStyle, //窗口的扩展风格
LPCTSTR lpClassName, //已经注册的窗口类名称
LPCTSTR lpWindowName, //窗口标题栏的名字
DWORD dwStyle, //窗口的基本风格
int x, //窗口左上角水平坐标位置
int y, //窗口左上角垂直坐标位置
int nWidth, //窗口的宽度
int nHeight,//窗口的高度
HWND hWndParent,//窗口的父窗口句柄
HMENU hMenu,//窗口菜单句柄
HINSTANCE hInstance, //应用程序实例句柄
LPVOID lpParam //窗口创建时附加参数
); //创建成功返回窗口句柄
创建窗口的原理
1.系统根据传入的窗口类名称,在应用程序局部窗口类中查找,如果找到执行2,如果未找到执行3。
2.比较局部窗口类与创建窗口时传入的HINSTANCE变量。如果发现相等,创建和注册的窗口类在同一模块,创建窗口返回。如果不相等,继续执行3。
3.在应用程序全局窗口类,如果找到,执行4,如果未找到执行5。
4.使用找到的窗口类的信息,创建窗口返回。
5.在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败。此时不需要hInstance句柄。
子窗口的创建
创建时要设置父窗口句柄
创建风格要增加 WS_CHILD|WS_VISIBLE
#include<windows.h>
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
switch (msgID)
{
case WM_DESTROY://关闭窗口返回0
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns,
HINSTANCE hPreIns,
LPSTR lpCmdLine,
int nCmdShow)
{
//注册窗口类
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统
//在内存创建窗口
HWND hWnd=CreateWindowEx(0,"Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = "Child";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统
//创建子窗口
HWND hChile1 = CreateWindowEx(0, "Child", "c1", WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, hWnd, NULL, hIns, NULL);
HWND hChile2 = CreateWindowEx(0, "Child", "c2", WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 200, 0, 200, 200, hWnd, NULL, hIns, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = { 0 };
while (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
消息基础
消息的概念和作用
消息组成(windows平台下)
窗口句柄
消息ID
消息的两个参数(两个附带信息)
消息产生的时间
消息产生时的鼠标位置
消息的作用
当系统通知窗口工作时,就采用消息的方式派发给窗口。
窗口处理函数
每个窗口都必须具有窗口处理函数。
LRESULT CALLBACK WindowProc(
HWND hwnd, //窗口句柄
UINT uMsg, //消息ID
WPARAM wParam, //消息参数
LPARAM lParam //消息参数
);
当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。在窗口处理函数中,不处理的消息,使用缺省窗口处理函数。例如:DefWindowProc。
消息相关函数
GetMessage - 获取消息。
BOOL GetMessage(
LPMSG lpMsg, //存放获取到的消息BUFF
HWND hWnd, //窗口句柄
UINT wMsgFilterMin,//获取消息的最小ID
UINT wMsgFilterMax //获取消息的最大ID
);
//lpMsg - 当获取到消息后,将消息的参数存放到MSG结构中。
//hWnd - 获取到hWnd所指定窗口的消息。
//wMsgFilterMin和wMsgFilterMax -只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围
TranslateMessage - 翻译消息。将按键消息,翻译成字符消息。
BOOL TranslateMessage(
CONST MSG *lpMsg //要翻译的消息地址
);
检查消息是否是按键的消息,如果不是按键消息,不做任何处理,继续执行
DispatchMessage - 派发消息
LRESULT DispatchMessage(
CONST MSG *lpmsg //要派发的消息
);
将消息派发到该消息所属窗口的窗口处理函数上。
常见消息
WM_DESTROY
产生时间:窗口被销毁时的消息。
附带信息:wParam:为0。
lParam : 为0。
一般用法:常用于在窗口被销毁之前,做相应的善后处理,例如资源、内存等。
WM_SYSCOMMAND
产生时间:当点击窗口的最大化、最小化、关闭等。
附带信息:wParam : 具体点击的位置,例如关闭SC_CLOSE等.
lParam : 鼠标光标的位置。
LOWORD(lParam);//水平位置
HIWORD(lParam);//垂直位置
一般用法:常用在窗口关闭时,提示用户处理。
WM_CREATE
产生时间:在窗口创建成功但还未显示时。
附带信息:wParam : 为0。
lParam : 为CREATESTRUCT类型的指针。
通过这个指针可以获取CreatWindowEx中的全部12个参数的信息。
一般用法:常用于初始化窗口的参数、资源等等,包括创建子窗口等。
WM_SIZE
产生时间:在窗口的大小发生变化后。
附带信息:wParam : 窗口大小变化的原因。
lParam : 窗口变化后的大小。
LOWORD(lParam) //变化后的宽度
HIWORD(lParam) //变化后的高度
一般用法:常用于窗口大小变化后,调整窗口内各个部分的布局。
WM_QUIT
产生时间:程序员发送。
附带信息:wParam : PostQuitMessage 函数传递的参数。
lParam : 0。
一般用法:用于结束消息循环,当GetMessage收到这个消息后,会返回FALSE,结束while处理,退出消息循环。
消息循环的原理
消息循环的阻塞
GetMessage - 从系统获取消息,将消息从系统中移除,阻塞函数。当系统无消息时,会等候下一条消息。
PeekMessage - 以查看的方式从系统获取消息,可以不将消息从系统移除,非阻塞函数。当系统无消息时,返回FALSE,继续执行后续代码。
BOOL PeekMessage(
LPMSG lpMsg, // message information
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax, // last message
UINT wRemoveMsg //移除标识
PM_REMOVE / PM_NOREMOVE//一般是PM_NOREMOVE
);
代码
//while (GetMessage(&nMsg, NULL, 0, 0))
//{
// TranslateMessage(&nMsg);
// DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
//}
while (1)
{
if (PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE))
{//有消息
if (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}
else
{
return 0;
}
}
else
{//空闲处理
WriteConsole(g_hOutput, "OnIdle", strlen("OnIdle"), NULL, NULL);
}
}
发送消息
SendMessage - 发送消息,会等候消息处理的结果。
PostMessage - 投递消息,消息发出后立刻返回,不等候消息执行结果。
BOOL SendMessage/PostMessage(
HWND hWnd,//消息发送的目的窗口
UINT Msg, //消息ID
WPARAM wParam, //消息参数
LPARAM lParam //消息参数
);
//==PostQuitMessage( 0 );//可以使GetMessage函数返回0
PostMessage(hWnd, WM_QUIT, 0, 0);
//!=SendMessage( hWnd, WM_QUIT, 0, 0);
消息分类
系统消息 - ID范围 0 - 0x03FF
由系统定义好的消息,可以在程序中直接使用。
用户自定义消息 - ID范围 0x0400 - 0x7FFF(31743)
由用户自己定义,满足用户自己的需求。由用户自己发出消息,并响应处理。
自定义消息宏:WM_USER 0x400
总代码
#include<windows.h>
#include<stdio.h>
HANDLE g_hOutput;//接受标准输出句柄
#define WM_MYMESSAGE WM_USER+1001
void OnCreate(HWND hWnd, LPARAM lParam)
{
CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
char* pszText = (char*)pcs->lpCreateParams;
MessageBox(NULL, pszText, "Info", MB_OK);
CreateWindowEx(0, "EDIT", "hello", WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 0, 200, 200, hWnd, NULL, 0, NULL);
}
void OnSize(HWND hWdnd, LPARAM lParam)
{
short nWidth = LOWORD(lParam);
short nHight = HIWORD(lParam);
char szText[256] = { 0 };
sprintf(szText, "WM_SIZE:宽:%d,高:%d\n", nWidth, nHight);
WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}
void OnMyMessage(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
char szText[256] = { 0 };
sprintf(szText, "自定义消息被处理 wParam=%d,lParam=%d", wParam, lParam);
MessageBox(hWnd, szText, "Info", MB_OK);
}
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
switch (msgID)
{
case WM_MYMESSAGE:
OnMyMessage(hWnd, wParam, lParam);
break;
case WM_CREATE:
SendMessage(hWnd, WM_MYMESSAGE, 1, 2);
OnCreate(hWnd, lParam);
break;
case WM_DESTROY://关闭窗口返回0
//==PostQuitMessage( 0 );//可以使GetMessage函数返回0
PostMessage(hWnd, WM_QUIT, 0, 0);
//!=SendMessage( hWnd, WM_QUIT, 0, 0);
break;
case WM_SIZE:
OnSize(hWnd,lParam);
break;
case WM_SYSCOMMAND:
if (wParam == SC_CLOSE)
{
int nRet=MessageBox(hWnd, "WM_SYSCOMMAND是否退出", "Infor", MB_YESNO);
if (nRet == IDYES)
{
//什么都不写
}
else
{
return 0;
}
}
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns,
HINSTANCE hPreIns,
LPSTR lpCmdLine,
int nCmdShow)
{
AllocConsole();//增加DOS
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统
//在内存创建窗口
char pszText[] = "附加信息";
HWND hWnd=CreateWindowEx(0,"Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, pszText);
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = "Child";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统
//创建子窗口
//HWND hChile1 = CreateWindowEx(0, "Child", "c1", WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, hWnd, NULL, hIns, NULL);
//HWND hChile2 = CreateWindowEx(0, "Child", "c2", WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 200, 0, 200, 200, hWnd, NULL, hIns, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = { 0 };
//while (GetMessage(&nMsg, NULL, 0, 0))
//{
// TranslateMessage(&nMsg);
// DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
//}
while (1)
{
if (PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE))
{//有消息
if (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}
else
{
return 0;
}
}
else
{//空闲处理
WriteConsole(g_hOutput, "OnIdle", strlen("OnIdle"), NULL, NULL);
}
}
return 0;
}