原文地址: https://blog.csdn.net/ling_xiao007/article/details/51720652
1.1 API与SDK
操作系统把它所能够完成的功能以函数的形式提供给应用程序使用,应用程序对这些函数的调用就叫做系统调用,这些函数的集合就是Windows应用程序编程接口(Application ProgrammingInterface),简称Windows API。API大多是意义的单词的组合,其精确的拼写及调用语法可以在MSDN中查询。
SDK全称是Software Development Kit,软件开发包。实际是开发所需资源。
1.2窗口与句柄
利用窗口可以接受用户输入以及显示输入。通常包括标题栏、菜单栏、系统菜单、最小化框、最大化框、滚动条。可以分为客户区与非客户区。客户区用于显示与绘制。窗口可以有父窗口、子窗口。同时对话框、消息框也是窗口。
Windows应用程序中,句柄(HWND)来标识窗口。各种各样的资源系统创建时都会分配内存,并返回一个标识这些资源的标识号即句柄。
1.3消息与消息队列
1)消息:
Windows程序设计是一种事件驱动方式的模式,主要基于消息。消息其实由一个结构体MSG表示。
MSG结构定义如下:
typedefstruct tagMSG {
HWND hwnd; // 消息所属窗口
UINT message; // 消息的标识符
WPARAMwParam; // 附加消息
LPARAMlParam; // 附加消息
DWORD time; // 投递到消息队列的时间
POINT pt; // 鼠标当前坐标
} MSG;
2)消息队列:
应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息(消息通常与窗口相关)。消息队列等待应用程序响应,应用程序对事件作出反应的过程就是消息响应,各种消息响应放在一起组成了窗口过程。这种消息机制就是Windows程序运行的机制。
3)进队消息和不进队消息
进队消息由操作系统放入消息队列,应用程序取出并发送。不进队消息在系统调用窗口过程是直接发送给窗口。最终都由系统调用窗口过程函数对消息进行处理。
1.4 WinMain函数
完整的Win32程序步骤如下:
1)入口函数
2)创建窗口
3)消息循环与发送消息
4)窗口过程与消息响应
1)入口函数:
intWINAPI WinMain(
HINSTANCE hInstance, // 当前应用程序实例句炳
HINSTANCE hPrevInstance, //永远为NULL(当前句柄的前一个句柄)
LPSTR lpCmdLine, // 命令行参数
int nCmdShow // 窗口显示时的状态
);
2)创建窗口:
Step1: 设计一个窗口类;
Step2: 注册窗口类;
Step3: 创建窗口;
Step4: 显示及更新窗口。
Step1. 设计窗口类:
typedef struct _WNDCLASS {
UINT style; //窗口类样式(CS_XXX)
WNDPROC lpfnWndProc; //窗口过程函数指针(*)
int cbClsExtra; //窗口类附加内存字节数,通常为0
int cbWndExtra; //窗口附加内存字节数,通常为0
HANDLE hInstance; //应用程序实例句柄
HICON hIcon; //标题栏图标(IDI_XXX, NULL)
HCURSOR hCursor; //光标
HBRUSH hbrBackground; //窗口背景颜色
LPCTSTR lpszMenuName; //菜单资源名称
LPCTSTR lpszClassName; //窗口类名称
} WNDCLASS;
About lpfnWndProc.指向窗口过程函数(回调函数:特定事件或条件发生受另一方调用)。窗口过程函数被调用过程如下:(1)设计窗口类时,地址复制给lpfnWndProc;(2)调用RegisterClass(&wndclass),系统有了窗口过程函数地址;(3)应用程序接收一个消息时,调用DispatchMessage(&msg)将消息回传给系统。系统利用指针,调用窗口过程函数处理。
About hIcon. 可以调用LoadIcon加载一个图标资源。HICON LOADICON( HINSTANCE hInstance, LPCTSTR lpIconName),加载标准图标第一个参数NULL,也可以加载自定义图标。
About hCursor. 可以调用LoadCursor加载一个光标。同LoadIcon。
About hbrBackground. 调用GetStockObject得到系统标准画刷。
详见MSDN。
Step2. 注册:
ATOMRegisterClass(CONST WNDCLASS *lpWndClass)
Step3. 创建:
HWND CreateWindow(
LPCTSTRlpClassName, // 已注册的窗口类名称
LPCTSTRlpWindowName, // 窗口标题栏中显示的文本
DWORDdwStyle, //窗口样式(WS_XXX | WS_XXX)
intx, // 水平坐标
inty, // 垂直坐标
intnWidth, // 宽度
intnHeight, //高度
HWNDhWndParent, //父窗口句柄
HMENUhMenu, //菜单句柄
HINSTANCEhInstance, // 应用程序实例句柄
LPVOIDlpParam // 用于多文档程序的附加参数,单文档为NULL
);
Step4. 显示与更新:
BOOL ShowWindow(HWNDhWnd, int nCmdShow) // nCmdShow(SW_XXX)
BOOL UpdataWindow(HWND hWnd);
3)消息循环与发送消息:
MSG msg;
while (GetMessage(&msg,NULL,0,0)) // 从消息队列获取消息
{
TranslateMessage(&msg); //消息解释
DispatchMessage(&msg); //将消息发送到“窗口过程”
}
TranslateMessage函数用于将虚拟键消息转换为字符消息。将WM_KEYDOWN和WM_KEYUP消息的组合转换为一条WM_CHAR消息,该消息的WParam附加参数包含了字符的ASCII码。并将转换后的新消息投递到调用线程的消息队列中。注意,Translate函数并不会修改原有的信息,他只是产生新的消息并投递到消息队列中。
DispatchMessage分派一个消息到窗口过程。由窗口过程函数对消息进行处理。DispatchMessage实际上是将消息回传给操作系统,再由操作系统调用窗口过程函数对消息进行处理。
BOOL GetMessage(
LPMSGlpMsg, // 消息结构体指针,返回消息信息
HWND hWnd, //窗口句柄 ,通常设为NULL
UINT wMsgFilterMin, //消息过滤最小值
UINT wMsgFilterMax //消息过滤最大值
);
WM_QUIT返回0,错误返回-1。
4)窗口过程与消息响应:
LRESULT CALLBACK MyWndProc(
HWNDhwnd, // handle to window
UINTuMsg, // message identifier
WPARAMwParam, //first message parameter
LPARAMlParam // second message parameter
)
{
switch(uMsg)
{
case WM_PAINT: //响应消息
caseWM_KEYDOWN: //响应消息
caseWM_LBUTTONDOWN: //响应消息
…
default:
returnDefWindowProc(hwnd,uMsg,wParam,lParam);
}
return0;
}
About WM_PAINT. Windows 把一个最小的需要重绘的正方形区域叫做“无效区域”。发现了一个“无效区域“后,它就会向该应用程序发送一个 WM_PAINT 消息,通知应用程序重新绘制窗口。当窗口从无到有、改变尺寸、最小化后再恢复、被其他窗口遮盖后再显示时,窗口的客户区都将变为无效。
About DefWindowProc. 不感兴趣的、不需要我们处理的消息,交还给Windows操作系统由DefWindowProc函数来实现。在销毁窗口后,发送WM_DESTROY(响应方式就是调用PostQuitMessage函数,该函数会在消息队列中添加一个WM_QUIT消息,准备让由消息循环中的GetMessage取得)
1.5 测试程序
VS2015下面测试一下程序。新建Win32 项目,编写代码后调试,发现一堆error2664,解决方法是项目属性,字符集更改为使用多字节字符型。之后出现链接错误,解决方法是添加附加依赖项。
#pragma comment (lib,"User32.lib")
#pragma comment(lib, "gdi32.lib")
程序运行界面
#pragma warning (disable:4996)
#pragma comment (lib,"User32.lib")
#pragma comment(lib, "gdi32.lib")
#include
#include
LRESULT CALLBACK MyWinProc(
HWND hwnd, //handle to window
UINT uMsg, //message identifier
WPARAM wParam, //first message parameter
LPARAM lParam //second message parameter
);
int WINAPI WinMain(
HINSTANCE hInstance, // 当前应用程序实例句炳
HINSTANCE hPrevInstance, // 永远为NULL(当前句柄的前一个句柄)
LPSTR lpCmdLine, // 命令行参数
int nCmdShow // 窗口显示时的状态
)
{
//Step1. 设计窗口类
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hIcon = LoadIcon(NULL, IDI_QUESTION);
wndcls.hInstance = hInstance;
wndcls.lpfnWndProc = MyWinProc;
wndcls.lpszClassName = ("WindowsTest");
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;
//Step2. 注册窗口类
RegisterClass(&wndcls);
//Step3. 创建窗口类
HWND hwnd;
hwnd = CreateWindow("WindowsTest", "MyFirst Windows App", WS_OVERLAPPEDWINDOW,
0, 0, 600, 400, NULL, NULL, hInstance, NULL);
//Step4. 显示及刷新窗口
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
// 定义结构体,开始消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK MyWinProc(
HWND hwnd, //handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam //second message parameter
)
{
switch (uMsg)
{
case WM_CHAR:
charszChar[20];
sprintf(szChar, "char code is %d", wParam);
MessageBox(hwnd,szChar, "char", 0);
break;
case WM_PAINT:
HDChDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hwnd, &ps);
TextOut(hDC,0, 0, "HelloWorld!", strlen("Hello World!"));
EndPaint(hwnd,&ps);
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd, "mouse clicked", "message", 0);
HDChdc;
hdc = GetDC(hwnd);
TextOut(hdc,0, 50, "HelloWin32!", strlen("Hello Win32!"));
ReleaseDC(hwnd,hdc);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CLOSE:
if (IDYES == MessageBox(hwnd, "是否真的结束?", "message", MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}