Win32 API笔记

简介

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.

然而现在TEXTTCHAR都很少用了,因为现在都是用unicode,

在微软C运行库中,也有类似的一系列定义,例如_tcslen

#ifdef _UNICODE
#define _tcslen     wcslen
#else
#define _tcslen     strlen
#endif

什么是窗口

什么是窗口

显然,Windows的核心内容就是窗口,
你认为的窗口可能是这样的:
Win32 API笔记

窗口类型会被应用窗口(application window)或主窗口(main window)调用,
典型的,他有一个框架,包含标题栏,最大化最小化按钮和其他的标准UI元素,
框架被叫做窗口的非客户端区域,因为框架是被操作系统调用的,
框架内的区域叫做客户端区域,这部分是由你的程序管理的。

另一种窗口类型是这样的:
Win32 API笔记
如果你是Windows编程的新手,你可能会很吃惊,像按钮这样的控件竟然也是窗口。
UI控件和应用窗口的主要区别是,UI控件不能够独立存在,UI控件依赖于应用窗口,当移动应用窗口时,如你所愿,UI控件也会一起移动。
还有,控件和应用窗口之间也可以通信,例如,应用窗口会收到来自按钮的单击事件。

因此,当谈到窗口的时候,不要简单的想到是一个应用窗口,而是要把它想成一个联合:

  • 占用屏幕的一部分
  • 给定时刻可见或不可见
  • 知道怎样描画自己
  • 响应用户或操作系统的事件

父窗口和所属窗口

在控件案例中,控件是应用窗口的子窗口,应用窗口是控件的父窗口。
父窗口给子窗口提供坐标系,以及影响子窗口的显示等。

另一种关系例如应用窗口和模态对话框,应用窗口是属主窗口,模态对话框是所属窗口,
所属窗口总是出现在属主窗口前面,当属主窗口最小化时,所属窗口也会最小化。
Win32 API笔记

应用窗口拥有对话框窗口,对话框是两个控件的父窗口,下图显示了这种关系:
Win32 API笔记

窗口句柄

窗口是一个对象,拥有代码和数据,但他不是C++的类,
程序应用窗口通过句柄handle,句柄是一个不透明类型,本质上句柄只是一个操作系统用来识别窗口的整数,
你也可以把窗口和句柄想象成一个表,通过句柄来索引出窗口(但是内部是否是这样操作并不重要),
窗口句柄的数据类型是HWND,通常发音为"aitch-wind."
窗口句柄通常由窗口创建函数返回: CreateWindow CreateWindowEx

想要操作一个窗口,可以使用HWND值来操作,例如要移动窗口,可以这样:

BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint);

需要注意的是,hwnd不是指针,所以不能*hwnd来使用。

屏幕和窗口坐标

坐标是由设备无关像素测量的,后续会介绍。
有屏幕坐标,窗口坐标和客户端坐标。
Win32 API笔记

WinMain

WinMain是Windows程序的入口点,有两个版本WinMainwWinMain,两者区别是从命令行传入的是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;
}

第一个窗口程序

概览

先实现一个空窗口,
Win32 API笔记

代码如下:

#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-

上一篇:VC6 命令行编译


下一篇:Win32 API TextOut输出时闪烁的解决