• 0x01 译文:Windows桌面应用Win32开发简介


    本节课将简单介绍下使用C++开发Windows桌面应用的一些基础知识

     目录:

    准备你的开发环境

    Windows 代码规范

    操作字符串

    什么是一个Window?

    WinMain:程序的入口点

    1. 准备你的开发环境

    安装 Windows SDK

           要用C或者C++开发Windows 程序,你必须安装 Microsoft Windows Software Development Kit (SDK)  或者一个包括Windows SDK的开发环境。

    这个Windows SDK 包含了头文件和编译链接你的程序所需的类库,这个Windows SDK 也包括Visual C++ 编译和链接命令行工具。

    尽管你用这个命令行工具可以编译和链接运行你的程序,但是还是建议你安装一个功能比较齐全的Microsoft Visual Studio.

    Visual C++ Express 是一个免费的可下载的Visual C++ 软件,Visual Studio Community是 Visual Studio Express 的更新替代产品。

    Windows SDK 集成IDE 下载(官方推荐):https://www.visualstudio.com/zh-hans/vs/visual-studio-express/

    Tips:

    这个Windows SDK 不支持硬件驱动的开发,在这个也不作讨论。

    如果想学习这方面的开发,请移步https://docs.microsoft.com/zh-cn/windows-hardware/drivers/gettingstarted/index

     Microsoft Visual C++ 2010 Express(已过时)是微软推出的一款免费软件,它比Microsoft Visual C++ 6.0 Enterprise Edition的兼容性和标准支持更好,比Microsoft Visual Studio 2010 Ultimate的体积更小。

     如果你对使用Visual C++ 2010 Express感兴趣,可以移步查看详细教程:https://msdn.microsoft.com/en-us/library/ff742833.aspx

    Windows代码规范

    如果你是一个开发Windows程序的新手,当你第一次看到一个WIndows程序的时候你可能会感到困惑。

    因为这个代码往往会出现像DWORD_PTR 和 LPRECT 一样充满了奇怪的类型定义和一些像hWnd 和 pwsz 变量(这称为匈牙利命名法)。学习这些代码规范是值得的。

    绝大多数Windows API由函数或 Component Object Model(COM)组件对象模型接口组成。

    很少有Windows API提供的C++类。(一个显著的例外是GDI+,一种二维图形API)

    Windows的头文件包含很多类型,这些很多都定义在头文件WinDef.h 中,下面这些是你经常会遇到的。

    你可以看到,在这些有一定数量的冗余类型。其中一些重叠仅仅是由于Windows API的历史。这里列出的类型有固定的大小,在32位和64个应用程序中的大小相同。例如,DWORD类型总是32位的

    Boolean Type

    BOOL是一个整数类型的值,用于布尔上下文定义。头文件 WinDef.h 也使用bool值定义了两个。

    BOOL is a typedef for an integer value that is used in a Boolean context. The header file WinDef.h also defines two values for use with BOOL

    #define FALSE    0 
    #define TRUE     1

    尽管布尔类型TRUE可以定义为任何一个非0的数字,但是你最好遵守规范总是TRUE设置为1

    正确的方式

    // Right way.
    BOOL result = SomeFunctionThatReturnsBoolean();
    if (result) 
    { 
        ...
    }

    错误的方式

    // Wrong!
    if (result == TRUE) 
    {
        ... 
    }

    Tips:注意BOOL是一个整数型且不可与C++ bool型互换。

    指针类型:

     Windows定义了很多表格 pointer-to-X 类型. 他们通常在名字的前面有前缀  P- or LP- .例如, LPRECT 是一个指向矩形的指针类型 , 这个矩形是一个描述了一个矩形的结构体.

    下面这样是等价的

    RECT*  rect;  // Pointer to a RECT structure.
    LPRECT rect;  // The same
    PRECT  rect;  // Also the same.

    历史上,P代表“指针”,LP代表“长指针”。长指针(也叫远指针)从16位Windows的延期,当他们需要解决内存范围以外的当前段。保留了LP前缀,使它更容易将32位Windows的16位代码移植到32位窗口中。

    但是今天没有区别——指针就是指针。

     指针精度类型

    以下数据类型始终是指针的大小,即32位应用程序中的32位宽,64位应用程序中的位宽为64位。大小是在编译时确定的。

    当32位应用程序在64位Windows上运行时,这些数据类型仍为4字节宽。(64位应用程序不能在32位Windows上运行,因此不会出现相反的情况。)

    • DWORD_PTR
    • INT_PTR
    • LONG_PTR
    • ULONG_PTR
    • UINT_PTR

     These types are used in situations where an integer might be cast to a pointer. They are also used to define variables for pointer arithmetic and to define loop counters that iterate over the full range of bytes in memory buffers. More generally, they appear in places where an existing 32-bit value was expanded to 64 bits on 64-bit Windows.

    在将整数转换为指针的情况下使用这些类型。它们还用于定义指针算术的变量,并定义循环计数器,迭代内存缓冲区中的所有字节。更一般地,它们出现在64位窗口上现有32位值被扩展到64位的地方。

    Hungarian Notation 匈牙利命名法

    匈牙利符号是将前缀添加到变量名中的实践,以便提供有关变量的附加信息。(符号的发明者查尔斯·西蒙尼是匈牙利人,因此得名)。

     操作字符串

    Windows本身支持UI元素,Unicode字符串的文件名,等等。Unicode是首选字符编码,因为它支持所有字符集和语言。Windows是Unicode字符使用UTF-16编码,其中每个字符被编码为一个16位的值。UTF-16字符称为宽字符,以区别于8位ANSI字符。Visual C++编译器支持宽字符的内置数据类型

    WinNT.h  头文件中也定义了以下类型

    typedef wchar_t WCHAR;

    你会看到在MSDN的例子代码的两个版本。若要声明宽字符文字或宽字符串文字,请将 L 置于文字之前。

    wchar_t a = L'a';
    wchar_t *str = L"hello";

    这里有一些其他的字符串相关的定义,你会看到:

    Unicode和ANSI函数

     当微软向Windows引入Unicode支持时,它通过提供两组并行API,一个用于ANSI字符串,另一组用于Unicode字符串,从而缓解了过渡。

    例如,设置窗口标题栏的文本有两个函数:

    SetWindowTextA //takes an ANSI string.
    SetWindowTextW //takes a Unicode string.

     实际上真正使用的时候会加上判断这样定义

    #ifdef UNICODE
    #define SetWindowText  SetWindowTextW
    #else
    #define SetWindowText  SetWindowTextA
    #endif 

     在MSDN中,函数的名字记录SetWindowText函数下,即使那是真的宏的名字,不是实际的函数名。

    新的应用程序应该总是调用Unicode版本。许多世界语言都需要Unicode。如果使用ANSI字符串,将不可能本地化应用程序

    ANSI版本的效率也较低,因为操作系统必须在运行时将ANSI字符串转换为Unicode。

    根据你的喜好,你可以调用Unicode功能明确,如setwindowtextw,或者使用宏。MSDN上的示例代码通常会调用宏,但两种形式是完全等价的。Windows中大多数更新的API只有Unicode版本,没有相应的ANSI版本

     当应用程序需要同时支持Windows NT以及Windows 95、Windows 98和Windows Me 时,根据目标平台编译ANSI或unicode字符串相同的代码是非常有用的。

    为此,Windows SDK提供了将字符串映射到Unicode或ANSI的宏,这取决于平台

    下面是个例子:

    SetWindowText(TEXT("My Application"));

    解析如下:

    SetWindowTextW(L"My Application"); // Unicode function with wide-character string.
    
    SetWindowTextA("My Application");  // ANSI function.

    TEXT 和 TCHAR  宏今天已经很少用了,因为所有的应用程序都应该使用Unicode ,然而你可能在MSDN看到一些这样的代码:

    然而,你可能会在旧的代码和一些MSDN代码例子看到他们。
    微软C运行时库的标题定义了一组类似的宏

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

     注意:有些标题使用预处理器符号Unicode,别人用_unicode使用下划线前缀。总是定义这两个符号。当你创建一个新项目,Visual C++将它们通过默认的方式设置。

     什么是Window?

     显然,Windows是Windows的中心。它们是如此重要,以至于他们在操作系统之后命名了操作系统。

    但是什么是窗口?当你想到一个窗口,你可能会想到这样的东西:

    这种类型的窗口称为应用程序窗口或主窗口。它通常有一个标题栏、最小化和最大化按钮以及其他标准UI元素的框架。

    该框架称为窗口的非客户端区域,之所以称为“窗口”,是因为操作系统负责管理窗口的那部分。框架内的区域是客户区。这是您的程序管理的窗口的一部分。

    这里是另一种类型的窗口:

    如果您是Windows编程新手,您可能会惊讶于UI控件,如按钮和编辑框本身就是Windows。UI控件和应用程序窗口之间的主要区别在于控件本身并不存在。

    相反,控件相对于应用程序窗口定位。当拖动应用程序窗口时,控件将随它移动,如您所期望的那样。另外,控件和应用程序窗口可以相互通信。

    (例如,应用程序窗口从按钮接收单击通知。)

    因此, 当你考虑window时, 不要把它简单地认为是个应用窗口,取而代之的是一种编程结构:

    占据屏幕的某一部分。
    在某一时刻可能不可见。
    知道如何画自己。
    响应来自用户或操作系统的事件。

    Parent Windows and Owner Windows 父窗口和所有者窗口

    在UI控件的情况下,控件窗口被称为应用程序窗口的子窗口。应用程序窗口是控件窗口的父窗口。父窗口提供用于定位子窗口的坐标系统。有父窗口会影响窗口外观的各个方面;例如,子窗口被裁剪,这样子窗口的任何部分都不能出现在其父窗口的边框外面。
    另一种关系是应用程序窗口和模式对话框窗口之间的关系。当应用程序显示一个模态对话框时,应用程序窗口是所有者窗口,而对话框是一个拥有窗口。拥有的窗口总是出现在它的所有者窗口前面。当所有者被最小化时,它被隐藏,并在所有者被销毁的同时被销毁。
    下图显示了一个应用程序,其中显示一个带有两个按钮的对话框:

    这个应用窗口有自己的对话框窗口,这个对话框窗口是父窗口中的两个按钮

    下面是这两者之间的关系:

    Window Handles

    窗口对象既有代码和数据,但他们都不是C++类。相反,程序使用一个称为Handle(句柄)的值引用一个窗口

    一个Handle 是一个不透明类型。本质上,它只是操作系统用来标识对象的一个数字。您可以将窗口视为创建所有窗口的大表。它使用此表通过Hanlder查找窗口。(无论是它是如何工作的内部是不重要的。)

    窗口的数据类型Hanlder是一个HWND,这通常是明显的“aitch-wind“。窗口句柄是由函数返回:CreateWindowCreateWindowEx创建窗口。

    在一个窗口进行操作,你通常会调用一些函数有一个窗口的句柄值作为参数。例如,复位窗口在屏幕上,调用MoveWindow函数:

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

     第一个参数是要移动的窗口的句柄。其他参数指定窗口的新位置和窗口是否应该重绘.

    记住句柄不是指针。如果窗口是一个变量,包含一个hanlder,试图引用句柄HWND  *hwnd 这样写是错误的。

    屏幕和窗口坐标

    坐标是以设备无关的像素来度量的。在讨论图形时,我们将更多地讨论与设备无关的像素的与设备无关的部分。
    根据您的任务,您可以测量相对于屏幕的坐标,相对于窗口(包括帧),或相对于窗口的客户端区域。

    例如,您将使用屏幕坐标在屏幕上定位窗口,但您将使用客户机坐标在窗口中绘图。在每种情况下,原点(0, 0)总是区域的左上角。

    WinMain: 程序入口点

    每一个Windows程序,都有一个入口函数,叫做WinMainwWinMain

    int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);

    这四个参数的意义:

    hInstance :          是一种叫做“实例的句柄”或“处理模块”的操作系统使用这个值来确定可执行文件(EXE)时,在内存中加载。实例句柄是某些Windows功能-例如,加载图标或位图。

    hprevinstance:    没有意义。它在16位窗口中使用,但现在总是0。

    pcmdline:            包含命令行参数作为一个Unicode字符串。

    ncmdshow:          是一个标志,是应用程序的主窗口将被最小化,最大化,或显示正常。

    函数返回一个int类型的值。操作系统不使用该返回值,但你可以使用返回值将状态代码发送给您编写的其他程序。

    WINAPI 的调用约定。调用约定定义函数如何从调用者接收参数。例如,它定义了参数出现在堆栈上的顺序。只要确保你wWinMain函数如下声明。

    WinMain 函数和 wWinMain是相同的,唯一不同的是命令行参数作为一个ANSI字符串,Unicode 版本是首选。

    你可以使用ANSI WinMain 函数,尽管你编译的程序是以Unicode编码。要得到一个Unicode字符编码的命令行参数,可以调用 GetCommandLine 函数。

    这个函数将返回一个单一的字符串,如果你想作为一个参数式阵列的参数,通过这个字符串CommandLineToArgvW

    CRT内部主要做一些额外的工作。例如,静态初始化器是之前调用wWinMain。虽然可以告诉链接器使用不同的入口点函数,但如果链接到CRT,则使用默认值。

    否则,将跳过CRT初始化代码,结果不可预测。(例如,全局对象初始化不正确)。

    这是一个空的函数

    INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        PSTR lpCmdLine, INT nCmdShow)
    {
        return 0;
    }

    现在你已经拥有了入口点并理解了一些基本术语和编码约定,下篇博文我们就可以创建一个完整的窗口程序了。

  • 相关阅读:
    ubuntu 12.04(Precise Pangolin)启用休眠(Hibernate)功能的方案
    svn小技巧——重定向svn diff
    引用对象深度复制
    引用对象深度复制的简单实现方法
    vue Excel导出 [post请求+提示语]
    随手笔记-二进制的正负计算
    枚举类的扩展使用
    关于担心java import xxx.*对资源占用的一次小实践
    idea 自动添加注释 (方法+类 带参数/返回值)
    maven打包到本地仓库
  • 原文地址:https://www.cnblogs.com/xingyunblog/p/7142445.html
Copyright © 2020-2023  润新知