CreateFile()和CreateFileA()有什么区别?

9

我正在按照这里的教程学习串口通信。

打开和关闭串口的主要代码如下:

HANDLE hComm;

hComm = CreateFileA((LPCSTR)"COM8",                //port name
    GENERIC_READ | GENERIC_WRITE, //Read/Write
    0,                            // No Sharing
    NULL,                         // No Security
    OPEN_EXISTING,// Open existing port only
    0,               // Non Overlapped I/O              // FILE_FLAG_NO_BUFFERING,            // copied from the MFC version
    NULL);        // Null for Comm Devices

if (hComm == INVALID_HANDLE_VALUE){
    DWORD err = GetLastError();
    printf("Error in opening serial port\n");
    printf("err = 0x%x\n", err);
}
else
    printf("opening serial port successful\n");

CloseHandle(hComm);//Closing the Serial Port

如果我使用CreateFile(),代码可以成功编译,但串口无法打开(会显示“Error in opening serial port”错误信息)。在对代码进行了一番尝试之后,我发现CreateFileA()可以成功打开串口(而且我能找到这个函数是因为当我谷歌搜索CreateFile()函数时,我得到的第一个结果是CreateFileA()的MSDN页面)。
我搜索了一下,但并没有找到两者之间的区别。我发现这篇文章说应该始终使用CreateFile(),让编译器自行处理,但在我的情况下无效,只有特别使用CreateFileA()才有效。
那么CreateFile()CreateFileA()之间有什么区别?在我的程序中应该使用哪一个以进行基本的串口通信?
Windows 10
Visual Studio 2013 express

6
CreateFile是一个宏,根据ASCII编码时被定义为CreateFileA,在Unicode编码时被定义为CreateFileW。 - Alex F
@Alex,我在我链接的那篇旧论坛帖子中发现,我们只应该使用CreateFile(),编译器会将其解析为正确的函数。为什么在我的情况下它不起作用呢? - user13267
@Alex GetLastError() 显示 2。 - user13267
3
使用"_T(“COM8”)",这应该在两种配置中都有效。阅读此文:https://www.codeproject.com/Articles/446/Unicode-MBCS-and-Generic-text-mappings?msg=1420179 - Alex F
@AlexF,你的评论非常好地回答了这个问题。能否请你考虑将它们合并成一个答案。这值得保留,因为整个CreateFile / CreateFileA / CreateFileW / L"String" / "String" / _T("String")问题可能会让初学者程序员感到有些困惑。 - dgnuff
显示剩余3条评论
1个回答

20
这里需要解释一下Windows API:Microsoft很早就引入了Unicode。当时,他们决定用16位表示一个Unicode字符。我猜当时这是一个有效的决定。因此,在Microsoft平台上,wchar_t宽度为16位,(Unicode)文本存储在wchar_t中——这样做的好处是每个字符宽度相等。缺点是处理char的现有API不再兼容。
现在,随着32位Unicode代码点的出现,这看起来很傻——Microsoft平台上的Unicode文本使用UTF-16编码,因此你既无法与简单的基于char的字符串兼容,也无法与多字符序列兼容。在许多其他平台上,wchar_t宽度为32位,Unicode要么存储在这些wchar_t中,要么以“正常”的char形式编码为UTF-8。
但回到Microsoft API:为了解决这个问题,他们将所有涉及字符串的API调用重命名为带有A后缀的,并引入了一组相同的调用,带有W后缀,接受wchar_t作为Unicode变体。根据编译时开关UNICODE,预处理器将原始名称映射到带有相应后缀的真实名称。
正如你可能知道的那样,在C中,字符串字面值的类型是char *。要创建类型为wchar_t *的字符串字面值,需要在其前面加上一个L。为了在定义了UNICODE时自动执行此操作,提供了另一个宏:_T()。因此,在代码中,您需要将任何字符串字面值包装在_T()中,它会在定义了UNICODE时准确地添加前缀L
因此,这一行:
hComm = CreateFileA((LPCSTR)"COM8"

应该改为:“应该阅读:”
hComm = CreateFile(_T("COM8")

关于 LPCSTRLPWCSTR 一词:它们现在等同于 char *wchar_t *。因此,强制转换是不必要的。这些命名的原因是为了向后兼容具有分段内存和“far指针”的古老系统。只要您不需要将代码编译为 Win16,就可以忘记它们。

最后是个人意见:Windows 已经很长时间支持 Unicode(如果我没记错的话,早在 Win95 上)。现在它已经成为标准。你可能永远不想将目标设置为不支持 Unicode 的 Windows 系统。更有可能的是,你想编写一些可移植的代码,这时候问题就出现了——Unicode 的处理方式不同(在 Windows 上使用 wchar_t,在大多数其他系统上使用 UTF-8 的 char,UTF-8 也是互联网上常用的编码)。
为了解决这个问题,我喜欢始终定义 UNICODE,使用 wchar_t 字符串字面值来表示 windows API 中所需的任何常量字符串(比如你的 CreateFile() 调用,只需在前面加上 L),并将所有字符串内部存储为 UTF-8 的 char,只有在需要将它们传递给 Windows API 时才将它们转换。有两个函数可以进行转换:MultiByteToWideChar()WideCharToMultiByte()。这样,你就可以轻松编写适配器,用于其他使用 UTF-8 的操作系统的 API。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接