简介
Windows编码约定
整数类型
Windows头文件包含了许多变量类型的typedefine,很多都定义在WinDef.h中,例如:
Data type | Size | Signed? |
---|---|---|
BYTE | 8 bits | Unsigned |
DWORD | 32 bits | Unsigned |
INT32 | 32 bits | Signed |
INT64 | 64 bits | Signed |
LONG | 32 bits | Signed |
LONGLONG | 64 bits | Signed |
UINT32 | 32 bits | Unsigned |
UINT64 | 64 bits | Unsigned |
ULONG | 32 bits | Unsigned |
ULONGLONG | 64 bits | Unsigned |
WORD | 16 bits | Unsigned |
如你所见,有很多替换是重复的,这些是历史遗留造成的。
以上列举的类型都是固定大小,例如DWORD,无论在32位系统还是64位系统上,总是32位宽度。
Boolean类型
Bool类型也是定义在WinDef.h中,是用整数替换的:
#define FALSE 0
#define TRUE 1
尽管定义了TRUE,但是很多程序都会返回一个非零整数作为“真”,因此使用时你应该这样:
// Right way.
BOOL result = SomeFunctionThatReturnsBoolean();
if (result)
{
...
}
而不是这样:
// Wrong!
if (result == TRUE)
{
...
}
请注意:BOOL是整型,不能和C++中的bool类型通用。
指针类型
Windows定义了很多指针类型,最通用的是P-
前缀和LP-
前缀,例如LPRECT是一个返回类型的指针,它描述了一个返回值矩阵:
RECT* rect; // Pointer to a RECT structure.
LPRECT rect; // The same
PRECT rect; // Also the same.
历史上,P代表指针,LP代表长指针,长指针用来兼容16位程序的,但是现在没有这种区别了,指针就是指针。
Historically, P stands for "pointer" and LP stands for "long pointer". Long pointers (also called far pointers) are a holdover from 16-bit Windows, when they were needed to address memory ranges outside the current segment. The LP prefix was preserved to make it easier to port 16-bit code to 32-bit Windows. Today there is no distinction — a pointer is a pointer.
指针精度
以下数据总是与指针大小相同,在32位机器上是32位,在64位机器上是64位,这些检测是在编译期完成的。
当32位应用运行在64位机器上时,指针仍是4字节,但是64位应用不能运行在32位机器上。
- DWORD_PTR
- INT_PTR
- LONG_PTR
- ULONG_PTR
- UINT_PTR
这些类型用于整形强制转化为指针类型时等等。
匈牙利命名法
尽管匈牙利命名法有利有弊,但是MSDN用了大量的匈牙利命名法,所以也要熟悉一下。
略。
字符串处理
Windows原生支持unicode字符串,因为他支持所有的字符类型和语言,
并且Windows更喜欢使用UTF-16,也叫做宽字符,以区别8位的ANSI字符。
VC++支持内置的wchar_t
宽字符类型,在WinNT.h 中:
typedef wchar_t WCHAR;
在MSDN样例中,你会看到使用宽字符需要在字符或字符串前加一个L:
wchar_t a = L'a';
wchar_t *str = L"hello";
还有一些其他的字符串相关的替换如下表:
Typedef | Definition |
---|---|
CHAR | char |
PSTR or LPSTR | char* |
PCSTR or LPCSTR | const char* |
PWSTR or LPWSTR | wchar_t* |
PCWSTR or LPCWSTR | const wchar_t* |
UNICODE和ANSI函数
由于Windows中原生支持UNICODE,因为在Windows中有两个字符串处理的API,一个是ANSI版的,一个是UNICODE版的,
例如设定窗口文本:
SetWindowTextA takes an ANSI string.
SetWindowTextW takes a Unicode string.
但是在内部,ANSI版的API还是会转换成UNICODE版进行输出。
Windows内部也定义了宏UNICODE来自动处理调用哪个函数:
#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif
TCHARs
当应用程序需要同时支持 Windows NT 以及 Windows 95、Windows 98 和 Windows Me 时,根据目标平台,为 ANSI 或 Unicode 字符串编译相同的代码非常有用。为此,Windows SDK 提供将字符串映射到 Unicode 或 ANSI 的宏,具体取决于平台。
Macro | Unicode | ANSI |
---|---|---|
TCHAR | wchar_t | char |
TEXT("x") | L"x" | "x" |
例如下面的代码:
SetWindowText(TEXT("My Application"));
是为了解决下面的情况:
SetWindowTextW(L"My Application"); // Unicode function with wide-character string.
SetWindowTextA("My Application"); // ANSI function.
然而现在TEXT
和TCHAR
都很少用了,因为现在都是用unicode,
在微软C运行库中,也有类似的一系列定义,例如_tcslen
:
#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif
什么是窗口
什么是窗口
显然,Windows的核心内容就是窗口,
你认为的窗口可能是这样的:
窗口类型会被应用窗口(application window)或主窗口(main window)调用,
典型的,他有一个框架,包含标题栏,最大化最小化按钮和其他的标准UI元素,
框架被叫做窗口的非客户端区域,因为框架是被操作系统调用的,
框架内的区域叫做客户端区域,这部分是由你的程序管理的。
另一种窗口类型是这样的:
如果你是Windows编程的新手,你可能会很吃惊,像按钮这样的控件竟然也是窗口。
UI控件和应用窗口的主要区别是,UI控件不能够独立存在,UI控件依赖于应用窗口,当移动应用窗口时,如你所愿,UI控件也会一起移动。
还有,控件和应用窗口之间也可以通信,例如,应用窗口会收到来自按钮的单击事件。
因此,当谈到窗口的时候,不要简单的想到是一个应用窗口,而是要把它想成一个联合:
- 占用屏幕的一部分
- 给定时刻可见或不可见
- 知道怎样描画自己
- 响应用户或操作系统的事件
父窗口和所属窗口
在控件案例中,控件是应用窗口的子窗口,应用窗口是控件的父窗口。
父窗口给子窗口提供坐标系,以及影响子窗口的显示等。
另一种关系例如应用窗口和模态对话框,应用窗口是属主窗口,模态对话框是所属窗口,
所属窗口总是出现在属主窗口前面,当属主窗口最小化时,所属窗口也会最小化。
应用窗口拥有对话框窗口,对话框是两个控件的父窗口,下图显示了这种关系:
窗口句柄
窗口是一个对象,拥有代码和数据,但他不是C++的类,
程序应用窗口通过句柄handle,句柄是一个不透明类型,本质上句柄只是一个操作系统用来识别窗口的整数,
你也可以把窗口和句柄想象成一个表,通过句柄来索引出窗口(但是内部是否是这样操作并不重要),
窗口句柄的数据类型是HWND
,通常发音为"aitch-wind."
窗口句柄通常由窗口创建函数返回: CreateWindow
和 CreateWindowEx
想要操作一个窗口,可以使用HWND值来操作,例如要移动窗口,可以这样:
BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint);
需要注意的是,hwnd不是指针,所以不能*hwnd来使用。
屏幕和窗口坐标
坐标是由设备无关像素测量的,后续会介绍。
有屏幕坐标,窗口坐标和客户端坐标。
WinMain
WinMain是Windows程序的入口点,有两个版本WinMain
和wWinMain
,两者区别是从命令行传入的是ANSI字符还是Unicode字符,一般都用wWinMain,下面是wWinMain函数:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);
这四个参数分别是:
hInstance,叫做句柄实例,当程序加载到内存,操作系统根据句柄实例来识别是哪个exe,有一些程序需要句柄实例作为参数,例如加载图标和位图。
hPrevInstance,没有意义,用于16位窗口,现在总是0.
pCmdLine,包含了命令行参数,这里是Unicode字符串。
nCmdShow,标志位,用于指示应用窗口是否可以最大化,最小化或正常显示。
下面是一个空的WinMain函数:
INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR lpCmdLine, INT nCmdShow)
{
return 0;
}
第一个窗口程序
概览
先实现一个空窗口,
代码如下:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
先概览注释内容,细节后续介绍。
创建窗口
参考链接:
https://docs.microsoft.com/en-us/windows/win32/learnwin32/what-is-a-window-