从 Windows 上的蓝牙 COM 端口获取文件*

Get a FILE* from a bluetooth COM port on Windows

我使用的 C 库需要一个 FILE* 打开 COM 端口才能使用它,但在将它传递给库之前我必须设置端口(连接速度等)。在 Windows 上,这是使用 SetCommState(HANDLE, DCB*).

完成的

HANDLE 和 FILE* 之间的转换是一个已解决的问题 (How make FILE* from HANDLE in WinApi? and How do I get the file HANDLE from the fopen FILE structure?),但令人惊讶的是,当应用于通过蓝牙获得的 COM 端口时,它以不同的方式失败 (RFCOMM/SPP)。

如果我先打开一个 HANDLE 并设置 COM 端口参数,_open_osfhandle 对我来说总是失败:

HANDLE qc9200_handle = CreateFile(T("\\.\COM9"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
/* this returns a valid value like 0x30 */
SetCommState(qc9200_handle, &qc9200_dcb); /* also succeeds */
int qc9200_fd = _open_osfhandle((intptr_t)qc9200_handle, _O_RDWR|_O_BINARY);
/* returns -1 and sets errno=EINVAL (22) */

我尝试设置各种标志组合,从 _O_APPEND(唯一在 the docs 中提到并且对我的应用程序有意义的标志)到 _O_RDWR|_O_BINARY(我最初认为应该有效),但 errno 始终是 EINVAL。

如果我尝试从 FILE* 或 int file_descriptor 开始以稍后从中生成 HANDLE,_topen(path, _O_RDWR|_O_BINARY)_tfopen(path, "r+b") 都会立即失败并设置 errno=EACCES ( 13) 无论我请求何种访问模式,即使我以管理员权限启动我的应用程序。不过,它们不会立即失败,大约需要一秒钟,我可以在失败前看到我的 USB 加密狗上的灯在闪烁。尝试 open(COMPORT, "+<", "\\.\COM9") 来自 Perl 的端口也失败 "access denied"。

使用 ProcessMonitor 钓鱼事件,令人惊讶的是,只给 HKLM\System\CurrentControlSet\Enum\BTHENUM_LOCALMFG\Device 参数 \{Port Name, Authenticated, Encrypted} 和一个 IRP_MJ_READ 给 %WINDIR%[=39= 一堆 RegQueryValue .dll 而 fopen 在堆栈上。

有问题的 COM 端口是使用普通 Windows 7 蓝牙设置获得的,每个能够使用 COM 端口的终端程序也可以访问它,即使不是 运行 管理员。当我对虚拟 COM 端口 (com0com) 执行相同的操作时,一切都按预期工作;仅当我使用蓝牙 COM 端口时错误仍然存​​在。

UPD:正如 GetFileType(HANDLE) 所揭示的,蓝牙 COM 端口的句柄不是文件句柄,也不是 GetFileType 所知道的任何内容。这就是 C 运行time 库拒绝 return 句柄的文件描述符的原因,这可能就是 fopen 拒绝打开端口的原因。我必须实现一个类似 fprintf 的函数,它接受 HANDLE 和 #ifdef 并在 Windows.

上使用它

感谢 Russian language forum,我能够解决这个问题。

打开一个HANDLE到虚拟COM口后,我在上面调用了GetFileType()

printf("handle=%d\nGetFileType=%lu\n", handle, GetFileType(handle));

这导致

handle=36
GetFileType=0

GetLastError 返回 0。

GetFileType(HANDLE) == 0x0000 表示 FILE_TYPE_UNKNOWN.

似乎 CRT 不知道如何从未知类型 HANDLES 正确生成 FILE*,所以我唯一的方法是 运行 类似 vfprintf写串口的功能就是自己实现HANDLEs这样的功能。

考虑到将 FILE* 与串行端口一起使用是个坏主意,因为标准库会在读取和写入之间发出 seek() 调用,并且串行端口无法访问,我不得不放弃 FILE* 并在 POSIX 上使用纯 int 文件描述符,在 Windows.

上使用 HANDLEs