【问题标题】:Get a FILE* from a bluetooth COM port on Windows从 Windows 上的蓝牙 COM 端口获取 FILE*
【发布时间】:2016-05-15 17:08:39
【问题描述】:

我使用的 C 库需要打开到 COM 端口的 FILE* 才能使用它,但我必须在将其传递给库之前设置端口(连接速度等)。在 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(我最初认为应该有效),但 errno 总是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\Device parameters\{Port Name, Authenticated, Encrypted} 和一个 IRP_MJ_READ 提供一堆 RegQueryValue 到 %WINDIR%\system32\ucrtbased .dll 而 fopen 在堆栈上。

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

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

【问题讨论】:

  • 尝试弄乱_open_osfhandle 的标志。也许删除_O_RDWR 和/或添加_O_APPEND 会有所作为。 legacy_ntp 中有一些代码在串行端口上使用_open_osfhandle,尽管它使用_O_TEXT 而不是_O_BINARY。一旦你有了一个有效的文件描述符,你就可以使用_fdopen打开一个FILE*(但你已经知道了)。
  • @Ian,无论我尝试使用_open_osfhandle 的什么标志,它总是-1 和EINVAL。就像_open_osfhandle 不喜欢我的手柄一样。但是,当CreateFile 成功时,为什么 COM 端口上的fopen 会失败 - 这是一个真正的谜。为什么open/fopen 在我清楚地可以使用任何终端仿真器访问端口时总是返回 EACCES?

标签: c windows file bluetooth rfcomm


【解决方案1】:

感谢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 上为 987654336@ 文件描述符,Windows 上为 HANDLEs。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-07
    • 1970-01-01
    • 2016-02-21
    • 2017-11-02
    • 1970-01-01
    • 2014-12-22
    相关资源
    最近更新 更多