与所有接受字符串参数的 Windows API 函数一样,CopyFile 函数实际上有两种变体:
CopyFileA 是 ANSI 版本,它采用系统默认字符集中的窄(非 Unicode)字符串。基本上,它接受 type 的参数const char*
,但 Windows 标头LPCSTR
为此使用 typedef。
CopyFileW 是宽版本,它采用Unicode字符串。为了做到这一点,它接受 type 的参数w_char*
,但 Windows 头文件LPCWSTR
为此使用 typedef(注意 typedef 中的附加内容W
)。
然后,根据是否UNICODE
为您的项目定义了预处理器宏(在包含 Windows 标头之前的代码中,或在 Visual Studio 中的项目属性中),Windows 标头将未修饰的 CopyFile 定义为 CopyFileA 或 CopyFileW。自然,如果UNICODE
定义了,CopyFile 将被定义为Unicode 版本的CopyFileW。否则,它将被定义为 CopyFileA。这个想法是对通用 CopyFile 函数的调用在编译时自动解析为正确的变体。
当然,既然您了解了所有这些,您几乎可以忘记它。在现代 Windows 编程中,完全没有理由调用旧 ANSI 版本的函数或处理窄字符串。忘记它char*
甚至可以用作字符串类型——那些字符串对你来说已经死了。从现在开始,您将要使用的唯一字符串是由字符组成的 Unicode 字符串wchar_t
。因此,UNICODE
应该始终为您的代码定义符号,并且您应该只使用W
Windows API 函数的版本。
再次查看 CopyFileW 函数的原型(与您在UNICODE
定义时调用 CopyFile 时得到的相同),我们看到:
BOOL WINAPI CopyFile(LPCWSTR lpExistingFileName,
LPCWSTR lpNewFileName,
BOOL bFailIfExists);
回想一下您在上面学到LPCWSTR
的只是 typedef 的同义词const wchar_t*
,它是由宽字符组成的 C 样式字符串。您已经知道为什么要标记参数const
:因为该函数不会修改这些值。
而且因为您在上面还了解到这些是您应该再使用的唯一类型的字符串,下一步是修改您的 FileTrans 函数以接受宽字符串(const
如果它不打算修改它们,则制作它们):
int _stdcall FileTrans(const wchar_t* InFile, const wchar_t* OutFile);
现在,从 FileTrans 内部,您可以毫无问题地调用 CopyFile,因为您拥有正确类型的字符串。
但是有一点免费的额外建议:永远不要在 C++ 中使用原始 C 风格的字符串。std
始终使用标头在命名空间中定义的 C++ 字符串类<string>
。
这个类有两个常见的变体,std::string
和std::wstring
. 和以前一样,thew
指的是宽字符串,这是您想在 Windows 中使用的唯一类型。整个代码库std::wstring
的新替代品也是如此。CHAR*
将 FileTrans 函数的声明更改为如下所示:
#include <string>
// ...some other stuff...
int __stdcall FileTrans(const std::wstring& InFile, const std::wstring& OutFile);
请注意,我已将您的原始参数更改为对对象CHAR*
的常量引用。std::wstring
常量引用在这里工作得很好,因为您不会在函数内部更改这些值中的任何一个。
如果您不清楚常量的含义、如何使用引用或类类型通常如何在 C++ 中工作,请查阅您最喜欢的 C++ 书籍)——这是所有 C++ 程序员都需要的知识。请记住,C++ 与 C 语言不同,因此不适用相同的习语。在许多情况下,有更好的方法来做事,这当然是这种情况的一个例子。