SDK 入门

目录

一、SDK 概念

二、SDK下载

三、创建桌面应用程序

四、控制台程序 与 Windows程序

处理流程区别

程序入口区别

代码编写区别

五、Windows操作系统

用户模式

内核模式

系统资源

Windows 内核对象

主要作用

如何访问内核对象

内核对象与一般数据结构区别

句柄

总结

Windows 消息机制

窗口消息

消息队列

消息循环

窗口过程函数

总结

虚拟内存

wWinMain 入口函数

Messagew  消息对话框

Hello World

ANSI 与 Unicode

TCHAR char wchat_t

解决编码的影响


一、SDK 概念

SDK —— software development kit 软件开发包(套件)

Windows SDK 提供了大量的 API 接口,用于开发 Windows 平台上的应用程序。这些 API 接口涵盖了各个方面,包括窗口和用户界面、文件和 I/O 操作、网络通信、系统管理等等。

通常情况下,SDK会包含以下内容:

  1. 库文件:SDK提供一组现成的库文件,包含了各种功能模块和工具,例如网络通信、图形处理、数据存储等。开发人员可以直接调用这些库文件,加快开发速度,并且可以避免从头开始编写复杂的功能模块。

  2. API文档:SDK通常会提供详细的API文档,其中包含了所有可用的函数、类和方法的说明。开发人员可以通过查阅API文档了解每个接口的用法、参数说明以及返回值,从而正确地使用SDK提供的功能。

  3. 示例代码:SDK通常提供一些示例代码,展示如何使用SDK进行开发。这些示例代码可以作为开发人员学习和参考的范例,帮助他们快速上手并理解各种功能的实现方式。

  4. 工具软件:一些SDK还会提供一些辅助性的工具软件,用于调试、测试、模拟等目的。这些工具可以简化开发过程中的一些任务,提高开发效率。

为什么之前写的控制台程序不能直接操作鼠标键盘,屏幕?

因为windows系统中,操作系统接管了所有的输入输出设备,应用想要调用输入输出设备,就必须通过WINDOWS操作系统的函数或则代码返回给应用程序

二、SDK下载

 SDK被集成在VS中,以VS2019为例:

安装位置:C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC

查看方式:项目属性 -- VC++目录 -- 库目录

三、创建桌面应用程序

创建Windows桌面应用程序,即可使用SDK开发

项目依赖的库

操作系统提供了许多常用的库文件,以下是一些常见的操作系统库文件:

  1. Windows操作系统:

    • kernel32.dll:提供了诸如文件操作、内存管理、进程管理、线程管理等核心功能的API。
    • user32.dll:提供了窗口、消息、用户输入等用户界面相关的API。
    • gdi32.dll:提供了图形设备接口相关的API,用于绘制图形、文本等操作。
    • advapi32.dll:提供了高级的API,用于注册表操作、安全权限管理等。
    • comctl32.dll:提供了通用的控件库,包括按钮、列表框等常见控件的API。
    • shell32.dll:提供了与Windows Shell相关的API,用于文件操作、快捷方式管理等。
  2. Linux操作系统:

    • glibc:GNU C库,包含了Linux系统的标准C库函数,提供了文件操作、内存管理、进程管理等功能。
    • libpthread:线程库,提供了线程相关的函数。
    • libm:数学库,包含了数学函数,如三角函数、对数函数等。
    • librt:实时库,提供了一些实时相关的函数。
    • libstdc++:C++标准库,提供了C++标准库函数。

这些库文件为开发者提供了丰富的API,可以在程序中调用这些库函数来实现各种功能,从而简化开发过程并提高应用程序的性能和稳定性。

为了学习方便,可以关闭随机基址

在Visual Studio 2019中,创建项目后的外部依赖项里面可能包括以下几种文件:

  1. 标准库头文件:例如<iostream><vector><string>等C++标准库头文件。

  2. Windows SDK头文件:例如<Windows.h>等用于访问Windows API的头文件。

  3. 第三方库的头文件:如果你在项目中引入了第三方库,那么该库的头文件也会出现在外部依赖项中。

  4. 库文件:在外部依赖项中可能还会包含一些库文件,例如.lib.dll文件等,这些文件用于链接项目所需的库函数。

这些外部依赖项文件通常是用于在项目中引入所需的头文件和库文件,以便在编译和链接过程中能够正确地访问和调用相关的函数和数据结构。通过在项目中添加外部依赖项,开发者可以方便地管理项目所需的各种依赖关系,从而更加高效地进行开发和维护。

C++标准库的头文件包含了类和函数的声明,而库文件则包含了这些声明对应的实现。在链接阶段,编译器会将程序中用到的库函数的定义链接到程序中,这就需要用到库文件。

C++标准库的库文件通常以不同的扩展名来区分,比如Windows平台上的库文件扩展名为.lib,Linux平台上的库文件扩展名为.a。在编译和链接阶段,需要将这些库文件与程序一起进行链接,以完成对标准库函数的调用。

四、控制台程序 与 Windows程序

控制台程序与Windows程序两者运行的机制根本就不一样

处理流程区别

控制台程序:主要使用顺序的,过程驱动的程序设计方法。顺序的,过程驱动的程序有一个明显的开始,明显的过程及一个明显的结束,因此程序能直接控制程序事件或过程的顺序。虽然在顺序的过程驱动的程序中也有很多处理异常的方法,但这样的异常处理也仍然是顺序的,过程驱动的结构。

Windows程序:消息驱动,不由事件的顺序来控制,而是由事件的发生来控制,所有的事件都是无序的,所为一个程序员,在你编写程序时,你并不知道用户先按哪个按纽,也不知道程序先触发哪个消息。你的任务就是对正在开发的应用程序要发出或要接收的消息进行排序和管理。事件驱动程序设计是密切围绕消息的产生与处理而展开的,一条消息是关于发生的事件的消息。

程序入口区别

SDK程序:          程序入口:wWinMain         链接选项:SUBSYSTEM:WINDOWS

Console程序:    程序入口:main                  链接选项:SUBSYSTEM:console

代码编写区别

Console程序:C语言编程至少有一个主程序,其名字是main()。

SDK程序:一个是WinMain(),另一个是窗口过程函数WindowProc

  • Windows应用程序的编程就围绕这两个部份进行的。其中WinMain函数为应用程序的入口点,它的名字一定要是WinMain。
  • 在Windows中,应用程序通过要求Windows完成指定操作,而承担这项通信任务的API函数就是Windows的相应窗口函数WindowProc。

在dos里,程序能直接控制事件的发生顺序,结果等。而在Windows里,应用程序不直接调用任何窗口函数,而是等待Windows调用窗口函数,请求完成任务或返回信息。为保证Windows调用这个窗口函数,这个函数必须先向Windows登记,然后在Windows实施相应操作时回调,所以窗口函数又称为回调函数。WindowProc是一个主回调函数,Windows至少有一个回调函数。

五、Windows操作系统

在学习Windows SDK之前,最好先学习一些Windows操作系统的知识

用户模式

用户模式(User Mode)是操作系统中的一种特权级别,相对于高特权级别(例如内核模式)而言,用户模式是相对低特权的运行环境。在用户模式下,主要运行应用程序和用户空间代码,受到操作系统的保护和控制。

在用户模式下,应用程序不能够直接访问系统的硬件资源,例如内存、处理器和设备。相反,应用程序需要通过系统调用(System Call)请求内核模式(Kernel Mode)来执行需要特权的操作,如访问硬件、文件系统、网络等。操作系统的内核会在接收到系统调用请求后,对请求进行验证、控制和执行,然后返回结果给用户模式的应用程序。

用户模式的主要特点有:

  1. 受限访问:应用程序受到严格的访问限制,无法直接操作系统的关键资源,只能通过系统调用间接请求内核模式的操作。
  2. 安全保护:用户模式下的应用程序相互隔离,无法直接干扰彼此,也不能破坏操作系统的稳定性。
  3. 稳定性:操作系统可以保持稳定运行,即使用户模式下的应用程序出现错误或崩溃,也不会对整个系统产生严重影响。

内核模式

内核模式(Kernel Mode)是操作系统中的最高特权级别,也称为特权模式或监控模式。在内核模式下,操作系统核心具有直接访问和控制系统硬件资源、执行特权指令的能力,同时对系统资源具有完全的控制权。

在内核模式下,操作系统核心可以执行以下关键任务:

  1. 管理系统资源:包括内存管理、进程管理、文件系统管理、设备管理等,确保系统资源的合理分配和有效利用。
  2. 响应中断:处理硬件产生的中断请求,确保系统能够及时响应外部事件。
  3. 执行特权指令:能够执行特权级别的CPU指令,包括访问敏感的硬件资源和执行特权操作。

内核模式的主要特点有:

  1. 直接访问硬件资源:操作系统核心可以直接访问系统的硬件资源,包括CPU、内存、设备等,并执行特权指令。
  2. 管理系统资源:可以对系统资源进行全面管理和控制,确保系统的稳定性和安全性。
  3. 具有特权级别:在内核模式下执行的代码拥有最高的特权级别,可以进行敏感操作,如修改内存映射、更改进程权限等。

系统资源

计算机系统中供各种程序和进程使用的各种资源,包括硬件资源和软件资源。常见的系统资源包括:

  1. CPU(Central Processing Unit):是计算机的核心处理器,用于执行计算机程序中的指令。

  2. 内存(RAM):用于存储程序和数据,是计算机运行程序时必不可少的资源。

  3. 硬盘和文件系统:用于长期存储数据和程序,提供了文件的读写功能。

  4. 网络接口和网络资源:用于连接计算机到网络,进行数据通信和访问网络资源。

  5. 输入输出设备:如键盘、鼠标、显示器、打印机等,用于人机交互和数据输入输出。

  6. 内核对象:如互斥体、事件、信号量等,用于实现进程、线程之间的同步和通信。

  7. 文件描述符:在类Unix系统中,用于标识已打开文件的索引。

  8. 设备驱动程序:用于控制和管理硬件设备的软件程序。

  9. 系统调用接口:用于应用程序与操作系统内核进行交互和访问系统资源。

  10. 进程和线程:是操作系统中用于执行程序的执行单元,拥有自己的系统资源,如内存空间、寄存器集、上下文等。

  11. 定时器和事件:用于实现对时间的管理和事件的通知。

Windows 内核对象

Windows系统中的内核对象是由操作系统内核管理的一种系统资源,用于表示和控制各种系统资源和同步机制。内核对象是一种抽象的资源,它可以被多个进程或线程共享,并提供了一种机制来控制对资源的访问和共享。

内核对象是由内核管理和分配的,它们通常通过句柄(Handle)的形式在用户空间和内核空间之间进行引用和访问。应用程序通过系统调用来创建、打开、关闭和操作内核对象。

主要作用

内核对象主要被用于操作系统内核和驱动程序的管理。操作系统内核是计算机系统中的核心部分,负责管理系统资源、调度任务、提供系统服务和保护系统安全。内核对象是操作系统内核提供的一种抽象概念,用于表示和管理系统资源,包括进程、线程、文件、事件、互斥体、信号量、共享内存等。

具体来说,内核对象主要用于以下方面:

  1. 进程和线程管理:操作系统使用内核对象来表示和管理进程和线程,包括进程控制块(Process Control Block, PCB)、线程控制块(Thread Control Block, TCB)等。内核对象用于记录进程和线程的状态、优先级、调度信息等,并提供对进程和线程的调度和管理操作。

  2. 文件和I/O管理:内核对象用于表示和管理文件、设备、I/O请求等资源。操作系统通过内核对象来管理文件句柄、设备对象、文件系统驱动等,提供文件和I/O操作的抽象接口。

  3. 同步和互斥:内核对象用于提供同步和互斥操作的基础,包括事件对象、互斥体、信号量等。这些内核对象用于多线程之间的同步、互斥以及线程间通信。

  4. 内存管理:操作系统使用内核对象来管理系统内存,包括虚拟内存管理、物理内存分配、页面调度等。内核对象包括页表、内存页、内存管理单元等,用于操作系统内存管理的实现。

  5. 安全和保护:内核对象用于实现操作系统的安全和保护机制,包括访问控制列表、安全描述符、安全令牌等。这些内核对象用于验证和控制对系统资源的访问权限。

总的来说,内核对象是操作系统内核提供的一种抽象机制,用于表示和管理系统资源,它是操作系统核心功能和资源管理的基础。内核对象的合理管理和使用对于操作系统的性能、安全性和可靠性具有重要影响。

如何访问内核对象

在Windows系统中,应用程序可以通过系统调用(System Call)来访问内核对象。系统调用是应用程序向操作系统内核请求服务的接口用于执行一些需要特权级别权限的操作,包括创建、打开、关闭和操作内核对象。内核对象是用户模式下代码与内核模式下代码进行交互的基本接口。

当应用程序需要访问内核对象时,通常需要使用相应的API函数,这些函数会将请求转发给操作系统内核,由内核来执行相应的操作。内核对象的句柄可以在应用程序中跨进程传递,并使用诸如WaitForSingleObject、WaitForMultipleObjects等函数来进行同步操作。这种方式可以确保内核对象的安全访问和共享,同时维护系统资源的稳定性和安全性。

内核对象与一般数据结构区别

内核对象与一般数据结构的最大区别是其内部数据结构是隐藏的,必须调用一个对象服务才能从此对象中得到数据。

内核对象的数据结构仅能够从内核模式访问,所以直接在内存中定位这些数据结构对应用程序来说是不可能的,只能通过API来访问它在用户模式下用来表示内存对象的数据成为对象句柄。可以认为是另一种形式下的“指针”。既:访问内核对象 需要 API + 相应的句柄(哪个内核对象)

简单来说就是,如同类私有成员一样,WINDOWS也有保护的成员,这个就是内核。内核提供操作内核数据的接口API。

句柄

句柄(Handle)是操作系统中用于标识和引用各种系统资源的一种抽象概念,它是一种特殊的数据类型在Windows系统中,句柄是一种32位或64位的整数值,用于唯一标识和引用系统资源。

在Windows系统中,不同类型的系统资源都有对应的句柄,比如内核对象句柄、文件句柄、窗口句柄等。通过操作系统提供的API函数,应用程序可以通过句柄来创建、打开、访问和关闭系统资源。例如,通过CreateMutex创建互斥体对象,会返回一个互斥体句柄;通过CreateFile创建文件对象,会返回一个文件句柄。

句柄本身并不直接表示资源的实际数据或内容,而是一个引用和标识系统资源的标识符。句柄的具体含义和作用取决于它所对应的系统资源类型,应用程序需要以正确的方式使用句柄来管理和访问系统资源

句柄的主要作用包括:

  1. 标识系统资源:句柄用于唯一标识和引用系统中的各种资源,如内核对象、文件、图形界面控件等。通过句柄,应用程序可以对系统资源进行操作和访问。

  2. 跨进程访问:句柄可以在进程之间进行传递,并且可以被多个进程共享。这使得多个进程可以访问和操作同一个系统资源。

  3. 资源管理:操作系统可以利用句柄来管理系统资源的分配和释放,确保系统资源的安全和稳定使用。

HINSTANCE 是一个 Win32 API 数据类型,表示当前实例句柄(instance handle)。实例是指操作系统为每个应用程序创建的一个运行时环境,该环境包括可执行文件、资源和数据等元素。对于任何 Windows 应用程序,操作系统都会分配一个唯一的实例句柄来标识该应用程序的当前实例。

HINSTANCE 类型的变量是一个句柄,用于表示当前应用程序实例的唯一标识符,通常用于资源加载和窗口创建等编程任务中。

总结

  • 内核对象是一种系统资源,由内核管理,用于表示和管理系统资源,包括进程、线程、文件、事件、互斥体、信号量、共享内存等。
  • 访问内核系统的方式:API接口(系统调用) + 句柄
  • 内核对象是用户模式下代码与内核模式下代码进行交互的基本接口。
  • 句柄标识引用系统资源

Windows 消息机制

Windows消息机制是一种事件驱动的机制,用于处理用户输入和系统事件。通过消息机制,Windows应用程序可以响应用户操作、处理系统事件,并进行界面更新和业务逻辑处理。

窗口消息

在图形用户界面(GUI)应用程序中,由操作系统发送给窗口的通知和事件。这些消息包含了用户输入、系统事件和应用程序之间的通信等信息,用于触发相应的处理逻辑,实现用户交互和应用程序功能。在Windows操作系统中,窗口消息是基于消息循环机制进行传递和处理的。

常见的窗口消息类型包括:

  • 用户输入消息:包括鼠标点击、键盘输入等用户操作引发的消息,如鼠标点击事件、键盘按键事件等。
  • 系统事件消息:包括定时器事件、系统状态变化等系统级别的消息,如定时器消息、系统重绘消息等。
  • 窗口操作消息:包括窗口创建、销毁、尺寸调整等窗口操作引发的消息,如窗口创建消息、窗口关闭消息等。

当操作系统将消息发送给特定的窗口时,与该窗口关联的窗口过程函数就会被调用,用于处理特定类型的消息。窗口过程函数会根据消息类型执行相应的处理逻辑,如界面更新、数据处理、事件响应等。这样,通过处理窗口消息,应用程序可以实现对用户输入的响应、界面更新和业务逻辑处理等功能。

消息队列

消息队列是Windows消息机制的基础,也是实现图形用户界面(GUI)应用程序的重要机制之一。

消息队列是计算机系统中用于存储和传递消息的一种数据结构。在操作系统中,消息队列通常用于实现进程间通信(Inter-Process Communication, IPC)线程间通信(Thread Communication),以便不同的进程或线程能够相互发送和接收消息,实现信息交换和协作。

在Windows操作系统中,每个窗口都有一个消息队列,用于存储该窗口所接收到的消息。当系统产生消息(如鼠标事件、键盘输入、定时器事件等)时,这些消息首先会被送入相应窗口的消息队列中。然后,窗口的消息循环会从消息队列中取出消息,并将其分发到相应的窗口过程函数进行处理。

消息队列通常具有以下特点:

  1. 先进先出:消息队列通常采用先进先出(FIFO)的方式存储消息,保证消息按照发送的顺序进行处理。
  2. 阻塞与非阻塞:消息队列可以是阻塞的,即当消息队列为空时,试图从中取出消息的操作会被阻塞,直到消息队列中有消息为止;也可以是非阻塞的,允许空队列的操作立即返回空值。
  3. 容量限制:消息队列通常具有一定的容量限制,当消息队列满时无法再加入新的消息,这可以避免消息过多导致系统资源耗尽。

消息循环

消息循环(Message Loop)是指在图形用户界面(GUI)应用程序中,用于不断地从系统消息队列中获取消息,并将消息分发到相应的窗口过程函数进行处理的循环结构。消息循环是实现用户交互和事件处理的重要机制,也是保持应用程序响应用户操作的关键。

在Windows应用程序中,消息循环通常包含一个无限循环,其基本逻辑如下:

  1. 获取消息:消息循环首先通过系统调用(如GetMessage或PeekMessage)从消息队列中获取消息。这些消息可以是来自窗口用户输入、系统事件或其他应用程序发送的消息。
  2. 分发消息:一旦获取到消息,消息循环会将消息分发给相应的窗口过程函数进行处理。每个窗口都有与之关联的窗口过程函数,用于处理特定类型的消息。
  3. 处理消息:窗口过程函数根据消息的类型进行相应的处理,可能包括界面更新、用户交互、业务逻辑处理等。
  4. 继续获取消息:处理完当前消息后,消息循环持续获取下一条消息,并重复以上过程。

消息循环的作用主要包括:

  • 处理用户输入:通过消息循环,应用程序可以响应用户的鼠标点击、键盘输入、窗口操作等,实现用户交互。
  • 处理系统事件:消息循环可以处理系统事件,如定时器事件、系统消息等。
  • 保持应用程序响应:消息循环能够保持应用程序响应用户操作,使应用程序能够及时地处理各种事件和消息。

窗口过程函数

窗口过程函数(Window Procedure)是在Windows应用程序中用于处理窗口消息的回调函数。每个窗口在创建时都会与一个窗口过程函数相关联,当窗口接收到消息时,系统会调用与之关联的窗口过程函数来处理消息。窗口过程函数负责根据消息的类型进行相应的处理,如界面更新、用户交互、业务逻辑等。

总结

  • 消息:有用户消息,系统消息,窗口消息
  • 消息队列:是一种数据结构,每个窗口都有一个消息队列
  • 窗口过程函数:每个窗口都有一个与之对应的窗口过程函数
  • 消息循环:不断地从系统消息队列中获取消息,并将消息分发到相应的窗口过程函数进行处理

虚拟内存

虚拟内存实现原理:

  1. 地址映射:虚拟内存通过地址映射将进程的虚拟地址空间映射到物理内存或磁盘上的页面文件。这种映射关系由操作系统负责管理,当程序访问虚拟内存中的某个地址时,操作系统会根据映射关系将其转换为对应的物理地址或磁盘上的页面文件地址。

  2. 页面调度:当物理内存不足时,操作系统需要决定哪些内存页将被置换到磁盘上的页面文件中,以腾出空间供其他页调入物理内存。页面调度算法根据一定的策略和条件进行选择,常见的算法包括最近最少使用(LRU)、先进先出(FIFO)等。

  3. 页面错误处理:当程序访问的内存页不在物理内存中时,会触发页面错误,此时操作系统会将缺失的内存页从磁盘读入物理内存,并更新地址映射关系。这样,虚拟内存可以使程序在逻辑上拥有比物理内存更大的地址空间。

  4. 内存保护:虚拟内存通过内存保护机制,实现了对内存的保护和权限控制。例如,操作系统可以将某些内存页设置为只读,以防止程序意外修改其内容。

实际上32位的系统上能够运行非常多的进程。原因在于对每个进程进行了内存隔离,每个进程的高2G空间都指向了物理内存的同一块位置(操作系统代码每个进程都一样)。低2G指向了物理内存条不同的地方,这是内存映射且在进程开始的时候并不是运行开始就分配4G的空间,而用多少分配多少,程序运行时的内存也叫虚拟内存,它是映射到物理内存的,当我们需要访问程序的某个地址时,会通过转换 + 偏移,映射到物理内存的特定的位置。

wWinMain 入口函数

函数原型如下:

int APIENTRY wWinMain(
	_In_ HINSTANCE hInstance,          
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR lpCmdLine,
	_In_ int nShowCmd
)

函数参数说明:

  • 参数1:表示当前实例的句柄,是一个唯一标识符,用于区分不同的应用程序实例。
  • 参数2:表示先前实例的句柄,Windows 已经废弃了它,这个参数始终为 NULL。
  • 参数3:表示命令行参数,传递给应用程序的命令行参数列表。
  • 参数4:表示窗口的显示状态,例如是否最小化、是否最大化等。

APIENTRY:是一个宏,是调用约定

#define APIENTRY    WINAPI
#define WINAPI __stdcall

_In_  与  _In_opt_

  • _In_ 是一个标记宏,表示该参数是输入参数。
  • _In_opt_ 是一个用于函数参数的标记宏,表示该参数是可选的(optional);NULL

HINSTANCE

HINSTANCE 是一个 Win32 API 数据类型,表示当前实例句柄(instance handle)。实例是指操作系统为每个应用程序创建的一个运行时环境,该环境包括可执行文件、资源和数据等元素。对于任何 Windows 应用程序,操作系统都会分配一个唯一的实例句柄来标识该应用程序的当前实例。

HINSTANCE 类型的变量是一个句柄,用于表示当前应用程序实例的唯一标识符,通常用于资源加载和窗口创建等编程任务中。

LPWSTR:         typedef WCHAR *LPWSTR;

LPWSTR 是在 Windows 平台上使用的数据类型之一,用于表示一个指向宽字符(Unicode)字符串的指针。LPWSTR 实际上是一个指向 WCHAR 类型的指针,而 WCHAR 是宽字符类型,通常占用两个字节(16 位)。

随机基址已经关闭,看一下参数1

指向操作系统为当前程序创建的实例结构(结构体或者对象),其中包含了程序所需的资源、数据和状态信息。该句柄可以用于获取程序所需的系统资源,例如图标、菜单、对话框等。这个句柄的存储地址是指向当前程序实例的内存地址。

Messagew  消息对话框

Windows API 函数,用于显示一个消息框(Message Box)并等待用户响应。

  • 参数1:指定消息框的父窗口句柄。如果为 NULL,则消息框将没有父窗口。
  • 参数2:指定要显示的消息文本(Message Text)。它是一个以 null 结尾的宽字符字符串(Unicode 字符串)。
  • 参数3:指定消息框的标题栏文本(Caption Text)。它也是一个以 null 结尾的宽字符字符串。
  • 参数4:指定消息框的类型和行为。
int WINAPI MessageBoxW(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCWSTR lpText,
    _In_opt_ LPCWSTR lpCaption,
    _In_ UINT uType)

LPCWSTR:typedef const WCHAR *LPCWSTR;

常量的指针,指针指向的地址不可修改,而存储的值却可以

函数参数官方文档:MessageBox 函数 (winuser.h) - Win32 apps | Microsoft Learn

Hello World

示例程序:

#include <windows.h>

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	int nRet = MessageBox(NULL, L"hello world", L"提示", MB_YESNO);
	if (nRet == IDYES)
	{
		MessageBox(NULL, L"YES", L"提示", MB_YESNO);
	}
	else if (nRet == IDNO)
	{
		MessageBox(NULL, L"NO", L"提示", MB_YESNO);
	}
	
	return 0;
}

结果如下:

ANSI 与 Unicode

ANSI(American National Standards Institute)编码是指使用ASCII码表的字符编码方式,ANSI编码的字符范围是0-127,共计128个字符。其中包括了大写和小写字母、数字、标点符号以及一些控制字符。ANSI编码是单字节编码,每个字符只占用一个字节的存储空间。这意味着ANSI编码只能表示有限的字符集,并且不支持多国语言的字符。

Unicode编码是一种用于表示文字字符的标准编码系统。它旨在统一世界上所有的字符,包括各种语言的字母、符号、标点符号和特殊字符等。Unicode编码使用了固定的位数来表示每个字符,常见的有UTF-8、UTF-16和UTF-32等不同的编码方案。

  • UTF-8使用变长字节表示字符,通常使用1至4个字节来表示不同的字符。UTF-8编码保留了ASCII码表中的字符,只需一个字节表示,这使得UTF-8与ASCII兼容。
  • UTF-16采用固定的16位(2字节)来表示大部分常用的字符,对于非常用字符采用双字节或四字节的方式表示。UTF-16编码可以兼容ASCII编码,但与UTF-8相比,存储空间可能更大。
  • UTF-32则使用固定的32位(4字节)来表示每个字符,不论字符是否为常用字符。UTF-32编码能够提供与其他Unicode编码方案之间的更好的互操作性,但也会占用更多的存储空间。

ASNI对比Unicode:

  1. ASNI仅适用于英语,占空间比Unicode更小
  2. Unicode兼容所有国家语言,但是占空间大

VS2019配置项目编码:两者的区别就是编译器会自动加上宏定义

TCHAR char wchat_t

THAR:就是当你的字符设置为什么就是什么

  • 程序编译为 ANSI, TCHAR 就是相当于 CHAR
  • 当程序编译为 UNICODE, TCHAR 就相当于 WCHAR

如果在程序中既包括ANSI又包括Unicode编码,需要包括头文件tchar.h。TCHAR是定义在该头文件中的宏,它视你是否定义了_UNICODE宏而定义成: 

  • 定义了_UNICODE:    typedef wchar_t TCHAR ; 
  • 没有定义_UNICODE: typedef char TCHAR ;
#ifdef UNICODE 
typedef char TCHAR; 
#else 
typede wchar_t TCHAR; 
#endif

_T( )也是定义在该头文件中的宏,视是否定义了_UNICODE宏而定义成: 

  • 定义了_UNICODE:    #define _T(x) L##x 
  • 没有定义_UNICODE: #define _T(x) x 

注意:如果在程序中使用了TCHAR,那么就不应该使用ANSI的strXXX函数或者Unicode的wcsXXX函数了,而必须使用tchar.h中定义的_tcsXXX函数

char :单字节变量类型,最多表示256个字符,

wchar_t :宽字节变量类型,用于表示Unicode字符,它实际定义在<string.h>里:typedef unsigned short wchar_t。

为了让编译器识别Unicode字符串,必须以在前面加一个“L”

wchar_t a[] = L"Hello!" ;

宽字节类型每个变量占用2个字节,故上述数组a的sizeof(a) = 14

 在字符串前加一个L作用: 
 如  L"我的字符串"    表示将ANSI字符串转换成unicode的字符串,就是每个字符占用两个字节。 

  strlen("asd")   =   3;   
  strlen(L"asd")   =   6; 

_T宏可以把一个引号引起来的字符串,根据你的环境设置,使得编译器会根据编译目标环境选择合适的(Unicode还是ANSI)字符处理方式 

  • 如果你定义了UNICODE,那么_T宏会把字符串前面加一个L。这时 _T("ABCD") 相当于 L"ABCD" ,这是宽字符串。 
  • 如果没有定义,那么_T宏不会在字符串前面加那个L,_T("ABCD") 就等价于 "ABCD" 

TEXT,_TEXT 和_T 一样的 ,如下面三语句:   

  TCHAR   szStr1[]   =   TEXT("str1");   
  char   szStr2[]   =   "str2";   
  WCHAR   szStr3[]   =   L("str3");   

那么第一句话在定义了UNICODE时会解释为第三句话,没有定义时就等于第二句话。但二句话无论是否定义了UNICODE都是生成一个ANSI字符串,而第三句话总是生成UNICODE字符串。 

解决编码的影响

背景:一家公司A,有一款应用,要推广到其他国家,为了兼容语言,需要把原先ASNI编码替换为Unicode。

在SDK中,很多函数都有两种形态,比如:MessageBox,这个本质是一个宏,有两个函数,分别是:MessageBoxA,MessageBoxW分别适合于不同的字符集,实际上在操作系统底层A的使用还是要先转化为W

在上述背景,如何解决字符集使用带来的问题?

方式一:直接全部使用A;或者全部使用W,字符串前需加L

MessageBoxA(NULL,"ANSI","51asm",MB_YESNO)
MessageBoxW(NULL,L"Unicode",L"51asm",MB_YESNO)

方式二:使用TCHAR,引入 #include <tchar.h> 头文件

  • 字符串使用_T()包裹,就可以根据当前使用的字符集进行自动切换
  • Win32 API中实现功能某一功能的函数往往有三个,例如:
    • MessageBox:是一个宏,会根据字符集的使用切换 A,W版本
    • MessageBoxA:多字节版本
    • MessageBoxB:Unicode版本
  • 其他字符串操作函数,往往也是有两个版本,对应多字节和Unicode

这一点会在之后的程序中体现出来

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dao-道法自然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值