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

时间:2016-02-05 16:28:54

标签: c windows file bluetooth rfcomm

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

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

如果我先打开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(我最初认为应该有效),但是错误总是EINVAL。

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

使用ProcessMonitor捕获事件,令人惊讶的是,只给HKLM \ System \ CurrentControlSet \ Enum \ BTHENUM_LOCALMFG \设备参数\ {端口名称,已验证,加密}和一个IRP_MJ_READ到%WINDIR%\ system32 \ ucrtbased的一堆RegQueryValue .dll而fopen在堆栈上。

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

UPD :正如GetFileType(HANDLE)所揭示的那样,蓝牙COM端口的句柄不是文件处理,也不是GetFileType所知道的。这就是为什么C运行时库拒绝将文件描述符返回到句柄的原因,这可能是fopen拒绝打开端口的原因。我将不得不实现类似fprintf的函数,它接受HANDLE而不是#ifdef并在Windows上使用它。

1 个答案:

答案 0 :(得分:0)

感谢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似乎不知道如何从未知类型FILE*正确生成HANDLES,所以我唯一的办法就是运行vfprintf类函数来写入串口就是为HANDLE自己实现这样一个功能。

鉴于将FILE*与串行端口一起使用是一个坏主意,因为标准库会在读写之间发出seek()次调用,并且串口无法访问,我不得不放弃{完全{1}}在POSIX上使用普通FILE*文件描述符,在Windows上使用int