什么时候应该使用Win32 / WinAPI类型而不是标准C类型?

时间:2018-01-21 12:30:38

标签: c windows winapi

我迟到了Win32派对,并且有大量功能,例如_tprintfTEXT(),还有像strsafe.h这样的库,它们具有StringCchCopy()等功能基本上,Win32 API在C之上引入了许多额外的函数和类型,这对于没有使用过Win32的C程序员来说可能会让人感到困惑。 在MSDN上查找这些类型和函数的定义时没有问题。但是,我确实在查找何时以及为何应该使用它们时遇到问题。

我有两个问题:

  1. 在使用Win32编程时,使用Microsoft在标准C之上提供的所有这些类型和特殊功能有多重要?废除所有标准C函数和类型并完全使用Microsoft包装器被认为是一种好习惯吗?

  2. 将标准C函数与这些Microsoft类型和函数混合使用是否可以?例如,要使用StringCchLength()代替malloc(),要么使用HeapAlloc()代替printf()等等?

  3. 我有一份Charles Petzold编程的Windows第五版书,但它主要涵盖了GUI的内容,而不是API的其余部分。

3 个答案:

答案 0 :(得分:5)

这里实际上有3个问题,你明确要求的问题,以及你没有问过的问题。让我们先拿出最后一个,因为它往往会造成最大的混乱:

Microsoft提供的CRT提供的那些_t - 扩展是什么?

它们是generic-text mappings,用于编写针对基于ANSI的系统(Win9x)以及基于Unicode的系统(Windows NT)的代码。它们是基于_UNICODE_MBCS预处理器符号扩展到实际函数调用的宏。例如,符号_tprintf会扩展为printfwprintf

同样,Windows API提供API调用的ANSI和Unicode版本。它们也是预处理器宏,它们扩展到实际的API调用,具体取决于预处理器符号UNICODE。例如,CreateFile符号会扩展为CreateFileACreateFileW

通用文本映射在过去二十年中没有用处。今天,只需使用CRT和API调用的Unicode版本(例如wprintfCreateFileW)。您也可以定义_UNICODEUNICODE作为衡量标准,以免意外调用ANSI版本。

  

还有像strsafe.h这样的库,它们具有StringCchCopy()StringCchLength()

等功能

这些是CRT字符串操作调用的安全变体。它们比例如更安全strcpy通过提供目标的缓冲区大小,类似于strncpy。然而,后者会遇到一个尴尬的设计决策,这会导致目标缓冲区在源不适合的情况下不会被终止。 StringCchCopy将始终对目标缓冲区进行零终止,从而为CRT实现提供额外的安全性。 (注意:C11引入了安全变体,例如strncpy_s,如果输入有效,它将始终对目标数组进行零终止。它们还验证输入,在验证失败时调用当前安装的约束处理程序,从而提供甚至比 strsafe.h 实现更强大的安全性。边界检查的实现是conditional feature of C11。)

  

在使用Win32编程时,使用Microsoft在标准C上提供的所有这些类型和特殊功能有多重要?废除所有标准C函数和类型并完全使用Microsoft包装器被认为是一种好习惯吗?

根本不重要。您可以在场景中使用更合适的选项。如果有疑问,通常优选编写便携式(即标准C)代码。您只需要调用Windows API调用,如果您需要他们提供的额外控制(例如HeapAlloc允许更多地控制分配而不是malloc;同样CreateFile提供的选项多于fopen malloc())。

  

将标准C函数与这些Microsoft类型和函数混合使用是否可以?例如,使用HeapAlloc()代替printf(),或使用_tprintf()代替HeapFree等等??

一般情况下,是的,只要您匹配这些来电:HeapAllocfreemalloc您的HeapAlloc。例如,您不得混用freeLocalFree。如果Windows API调用需要使用特殊的内存管理函数,则在文档中明确指出。例如,如果请求FormatMessage分配缓冲区以返回数据,则必须使用malloc释放它。如果您没有请求API分配缓冲区,您可以按照自己喜欢的方式传递一个缓冲区(HeapAllocIMalloc::AllocServerAlias *等。)。

答案 1 :(得分:3)

请注意,不同版本的操作系统使用不同的基本类型定义,并使用不同的路线/填充。记住8086,386和现在的Core i7(16,32,64位)。

当结构需要与早期版本兼容时,它们通常使用预定义的整数宽度和填充以进行传统对齐。

因此,必须在API调用中使用API​​中的类型。

在内存中也使用过,可能是进程内存和共享内存的不同内存模型。例如,剪贴板使用一种共享内存形式。使用Microsoft建议的内存分配机制来进行API调用非常重要。

对于所有非API,我使用标准的C函数和类型。

答案 2 :(得分:3)

可以在Windows上创建程序而不使用任何标准C库函数,但大多数程序都可以,然后您可以使用malloc而不是HeapAllocmalloc将在内部使用HeapAllocVirtualAlloc,但与原始API相比,它可能会针对更好的性能/更少的碎片进行调整。它还使将来更容易移植到POSIX。在您为API分配内存的某些地方,您仍然会被迫使用LocalFree / GlobalFree / HeapFree

处理文本需要特别考虑,您需要决定是否需要Unicode支持。漫步记忆的道路可能会解释为什么事情就像他们一样。

当Windows 95/98成为王道时,您可以将char / CHAR窄字符串类型与C标准函数和Windows API一起使用。除少数功能外,几乎没有Unicode支持。

在Windows NT4 / 2000上,本机字符串类型为WCHAR(UTF-16 LE,但Microsoft只称其为Unicode)。如果您使用的是Microsoft Visual C ++,那么您可以访问C标准库的宽字符串版本,超出C标准实际需要的内容,以简化此平台的编码。使用Microsoft工具链对Windows进行编码时,您可以假设Windows SDK WCHAR类型与C定义的wchar_t类型相同。

因为95和NT4的开发重叠,它们共享相同的API,并且接收/返回字符串的每个函数都有两个版本,一个带有A后缀(“ANSI”),另一个带有W后缀。在Windows 95上,W函数只是返回失败的存根。

当您包含Windows.h时,如果定义了#define CreateProcess CreateProcessW,它将创建类似UNICODE的定义,如果没有,则创建#define CreateProcess CreateProcessA

Visual C ++使用tchar.h标头执行same thing。它使用_UNICODE定义来确定TCHAR类型和_t*函数是使用char还是wchar_t类型。这意味着您可以使用相同的源代码创建两个版本,一个用于Windows 95/98 / ME,另一个用于完全支持Unicode。

这不再那么相关,但你仍然need to make a choice,因为事情将被定义为其中一个。

完成

仍然完全有效
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>

void foo()
{
  TCHAR buf[100];
  SomeWindowsFunction(buf, 100);
  _tprintf(_T("foo: %s\n"), buf);
}

虽然您最近会看到很多人直奔WCHARwprintf

添加了StrSafe函数以便更容易编写无错误的代码,它们仍然具有相同的A / W重复。

您不能将WCHARprintf混合使用,即使您在格式字符串中使用%ls字符串将在内部转换,并且并非所有Unicode字符串都能正确转换。

如果不要求POSIX可移植性,那么我建议您在需要C库函数时使用Microsoft提供的广泛功能扩展。