我迟到了Win32派对,并且有大量功能,例如_tprintf
,TEXT()
,还有像strsafe.h
这样的库,它们具有StringCchCopy()
等功能基本上,Win32 API在C之上引入了许多额外的函数和类型,这对于没有使用过Win32的C程序员来说可能会让人感到困惑。 在MSDN上查找这些类型和函数的定义时没有问题。但是,我确实在查找何时以及为何应该使用它们时遇到问题。
我有两个问题:
在使用Win32编程时,使用Microsoft在标准C之上提供的所有这些类型和特殊功能有多重要?废除所有标准C函数和类型并完全使用Microsoft包装器被认为是一种好习惯吗?
将标准C函数与这些Microsoft类型和函数混合使用是否可以?例如,要使用StringCchLength()
代替malloc()
,要么使用HeapAlloc()
代替printf()
等等?
我有一份Charles Petzold编程的Windows第五版书,但它主要涵盖了GUI的内容,而不是API的其余部分。
答案 0 :(得分:5)
这里实际上有3个问题,你明确要求的问题,以及你没有问过的问题。让我们先拿出最后一个,因为它往往会造成最大的混乱:
Microsoft提供的CRT提供的那些_t
- 扩展是什么?
它们是generic-text mappings,用于编写针对基于ANSI的系统(Win9x)以及基于Unicode的系统(Windows NT)的代码。它们是基于_UNICODE
和_MBCS
预处理器符号扩展到实际函数调用的宏。例如,符号_tprintf
会扩展为printf
或wprintf
。
同样,Windows API提供API调用的ANSI和Unicode版本。它们也是预处理器宏,它们扩展到实际的API调用,具体取决于预处理器符号UNICODE
。例如,CreateFile
符号会扩展为CreateFileA
或CreateFileW
。
通用文本映射在过去二十年中没有用处。今天,只需使用CRT和API调用的Unicode版本(例如wprintf
和CreateFileW
)。您也可以定义_UNICODE
和UNICODE
作为衡量标准,以免意外调用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
等等??
一般情况下,是的,只要您匹配这些来电:HeapAlloc
您free
,malloc
您的HeapAlloc
。例如,您不得混用free
和LocalFree
。如果Windows API调用需要使用特殊的内存管理函数,则在文档中明确指出。例如,如果请求FormatMessage分配缓冲区以返回数据,则必须使用malloc
释放它。如果您没有请求API分配缓冲区,您可以按照自己喜欢的方式传递一个缓冲区(HeapAlloc
,IMalloc::Alloc
,ServerAlias *
等。)。
答案 1 :(得分:3)
请注意,不同版本的操作系统使用不同的基本类型定义,并使用不同的路线/填充。记住8086,386和现在的Core i7(16,32,64位)。
当结构需要与早期版本兼容时,它们通常使用预定义的整数宽度和填充以进行传统对齐。
因此,必须在API调用中使用API中的类型。
在内存中也使用过,可能是进程内存和共享内存的不同内存模型。例如,剪贴板使用一种共享内存形式。使用Microsoft建议的内存分配机制来进行API调用非常重要。
对于所有非API,我使用标准的C函数和类型。
答案 2 :(得分:3)
可以在Windows上创建程序而不使用任何标准C库函数,但大多数程序都可以,然后您可以使用malloc
而不是HeapAlloc
。 malloc
将在内部使用HeapAlloc
或VirtualAlloc
,但与原始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);
}
虽然您最近会看到很多人直奔WCHAR
和wprintf
。
添加了StrSafe函数以便更容易编写无错误的代码,它们仍然具有相同的A / W重复。
您不能将WCHAR
与printf
混合使用,即使您在格式字符串中使用%ls
字符串将在内部转换,并且并非所有Unicode字符串都能正确转换。
如果不要求POSIX可移植性,那么我建议您在需要C库函数时使用Microsoft提供的广泛功能扩展。