文章目录
一、Win32是什么?
Win32编程,指的是用Window提供的API(Application Programming Interface)为Window编写应用程序。由于Windows是不开源的,所以程序员在Windows上做开发必须基于Window提供的接口完成。
二、基础
1.数据类型
当然,微软的大佬们不可能生生造出来一些奇怪的数据类型,也没必要,因此这些数据类型本质上要么是结构体,要么是对基本数据类型如int、long、short、char等的再命名。
现在仅对由char衍生出来的一些数据类型作出澄清,(为什么捏,因为这些数据类型决定了在调用一些函数时你必须额外处理类型是否相匹配)其他数据类型后续慢慢介绍。
//在Visual Studio 2013中,新建一个win32项目,默认是使用Unicode字符集
//那么什么是Unicode字符集呢?实际上是一个将所有字符(包括汉字)编码的一个字符集
//所以毫无疑问,char是不够用了,于是衍生出了宽字符wchar_t,顾名思义,它比普通字符要宽。
//它还有个特殊的标识符L"....",使用这种格式才能确保编译器认你这个宽字符
//事实上sizeof(wchar_t)=2,而与char相关的打印等操作也不可以套用上去,需要特殊的函数
如wprintf(L"%s",buff)等//使用Unicode字符集等价于在程序开头添加#define UNICODE,这将导致,如下结果
#ifdef UNICODE
typedef wchar_t TCHAR;
#define __TEXT(quote) L##quote
#else
typedef char TCHAR;
#define __TEXT(quote) quote
#endif
//换句话说,是否选用Unicode,TCHAR代表的意义不同
//而TCHAR的意义在于,可以让你不用太过在意是wchar_t还是char。
//现在来看看其它char相关数据类型
LPSTR == char* LPCSTR == const char*
LPWSTR == wchar_t* LPCWSTR ==const wchar_t*
LPTSTR == TCHAR* LPCTSTR == conts TCHAR*
2.win32框架
当你使用VS2013创建一个项目时,会给出如下框架,看着很吓人,不过也确实如此。。。
#include "stdafx.h"
#include "Win32Project1.h"
#define MAX_LOADSTRING 100 /
// 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,//应用程序实例句柄
_In_opt_ HINSTANCE hPrevInstance,//父应用程序句柄
_In_ LPTSTR lpCmdLine,//命令行参数
_In_ int nCmdShow)//窗口显示风格
//
{
//表示安全属性等级 可有可无。
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
MSG msg;//消息结构体
HACCEL hAccelTable;//快捷键资源
// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WIN32PROJECT1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
//加载快捷键资源
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT1));
// 主消息循环:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;//注册信息结构体
wcex.cbSize = sizeof(WNDCLASSEX);//记录结构体的大小
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT1));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;//窗口句柄
hInst = hInstance; // 将实例句柄存储在全局变量中
hWnd = CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPEDWINDOW, //样式
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, //窗口规格
NULL, //父窗口句柄
NULL, //菜单
hInstance,
NULL);//窗口辅助信息
if (!hWnd)
{
return FALSE;
}
//绘制窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);//低地址
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);//默认处理
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
精简一下可以得到如下程序,上面的可以不用管
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM iParam)
{
return DefWindowProc(hWnd, msgID, wParam, iParam);
}//消息处理函数
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR LpCmdLine, int nCmdShow)
{
//注册窗口类
/*窗口类的概念
包含了窗口属性的结构体
每个窗口都1具有窗口类,基于窗口类创建窗口
每个窗口类都用名称创建前要注册到系统*/
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(wc.lpszClassName, "windows", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = { 0 };
while (GetMessage(&nMsg, nullptr, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息给窗口处理函数来处理
}
return 0;
依次解释一下这里出现的多种数据结构和他们的关系。
WNDCLASS 看着名字小伙伴可能比较熟,class,不就类吗,看下文中对它的赋值行为,也妥妥表明它是一个类,这个类叫窗口类,里面包含的信息如下
typedef struct tagWNDCLASSW {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
} WNDCLASSW;
所谓style就是这个窗口类的风格,lpfnWndProc就是消息处理函数的名称,就是这个类是怎么处理消息的。两个extra分别是窗口类缓冲区大小,窗口缓冲区大小,hinstance是当前实例句柄,可以视作指针,实际上是对这个窗口类进行主权宣誓,表示这个窗口类是属于本模块的(为什么这么处理,因为类的信息都是要存到内存中,多个线程可能都有叫做a的窗口类,这时hinstance的作用就凸显出来了)。hIcon是图标的句柄,就是所有界面左上角都有的那玩意儿。hCursor鼠标的样式。剩下的我相信英文好的诸位应该都可以理解,就不赘述了。
接下来的一个RegisterClass(...),实际上就是将上述信息存入内存,用以作为模板,创造窗口。
typedof unsigned short ATOM;
ATOM
RegisterClass(CONST WNDCLASS *lpWndClass);
而createwindow(其实是一个宏)就是将类,实例化,而HWND就是窗口句柄,代表本窗口所指向的内存,怎么确认的呢?CreateWindow()的第一个参数是这个窗口类的名称,倒数第二个是实例句柄hinstance由此二者确定它到底要以什么为模板创造窗口。
HWND
CreateWindowEx(
DWORD dwExStyle,//窗口的扩展风格
LPCWSTR lpClassName,//已经注册的窗口类的名称
LPCWSTR lpWindowName,//窗口标题栏的名字
DWORD dwStyle,//窗口的基本风格
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,//窗口菜单句柄
HINSTANCE hInstance,
LPVOID lpParam//附加参数
);//创建成功返回窗口句柄
接下来是显示窗口showwindow,hWnd作为句柄决定了这个窗口的属性,然后后一个参数决定了这个窗口怎么显示,是按照最大化还是最小化,还是原样显示(x,y,width,height)。而updatewindow本质上就是将其再画一遍。
此后是MSG这个数据类型。消息的数据类型如下
typedef struct tagMSG {
HWND hwnd;//窗口句柄
UINT message;//消息内容
WPARAM wParam;//消息类别(附带信息
LPARAM lParam;//消息类别
DWORD time;//消息产生时间
POINT pt;//鼠标所在位置
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
接下来的while循环就是获取消息(getmessage),翻译消息(translatemessage)与处理消息(dispatchmessage),这时可能会有些困惑,怎么没看见消息处理函数呢?其实消息处理函数已经被刻到窗口类中了,所以诞生出来的窗口也会按照你的意愿去处理消息。而dispatchmessage本身就是在调用这个函数罢了。