是否可以更改为同步I/O打开的HANDLE,以便在其生命周期内为异步I/O打开?

Mar*_*šík 9 winapi asynchronous synchronous createfile overlapped-io

我在Windows中的大部分日常编程工作现在都是各种I/O操作(管道,控制台,文件,套接字......).我很清楚从不同类型的句柄读取和写入的不同方法(同步,异步等待事件完成,等待文件HANDLE,I/O完成端口和可警告的I/O).我们使用其中许多.

对于我们的一些应用程序,只有一种方法来处理所有句柄是非常有用的.我的意思是,程序可能不知道它收到了什么样的句柄,我们想要使用,比方说,I/O完成端口.

首先我会问:

我们假设我有一个句柄:

HANDLE h;
Run Code Online (Sandbox Code Playgroud)

我从某个地方收到了I/O进程.有没有简单可靠的方法来找出它创建的标志?有问题的主要标志是FILE_FLAG_OVERLAPPED.

到目前为止,我所知道的唯一方法是尝试将这样的句柄注册到I/O完成端口(使用CreateIoCompletionPort()).如果成功,则使用FILE_FLAG_OVERLAPPED创建句柄.但是之后只能使用I/O完成端口,因为如果不关闭它HANDLE h本身就不能从中取消注册句柄.

提供一种简单的方法来确定存在FILE_FLAG_OVERLAPPED,我的第二个问题就出现了:

有没有办法如何将这样的标志添加到现有的句柄?这将使最初为同步操作打开的句柄打开以进行异步操作.有没有办法如何创建相反的(删除FILE_FLAG_OVERLAPPED从异步创建同步句柄)?

通过MSDN阅读和google搜索后,我没有找到任何直接的方法.至少会有一些技巧可以做同样的事吗?就像使用CreateFile()函数或类似的东西以相同的方式重新创建句柄一样?某些东西甚至部分记录或根本没有记录?

我需要这个的主要地方是确定进程应该从第三方应用程序发送给它的句柄读取/写入的方式(或改变方式).我们无法控制第三方产品如何创建其句柄.

亲爱的Windows大师:请帮助!

带着敬意

马丁

Mar*_*šík 5

我看到我是MSDN的坏读者:/我完全错过了ReOpenFile() 早在2003年6月在Windows Server 2003中引入的功能(根据这篇文章).为了至少保护自己一点:我希望CreateFile()描述能够与描述交叉引用ReOpenFile().ReOpenFile()页面到CreateFile()页面有一个引用,但不是相反.

这个功能似乎正好能够实现我所需要的:FILE_FLAG_OVELRAPPED通过创建具有所需属性的新句柄来添加或删除已存在的句柄!但是-DI还没有测试过它.不幸的是,它仅在Windows 2003 Server,Windows Vista及更高版本上可用.有关以前操作系统版本的问题已在此处得到解答.Windows 2003 Server之前的操作系统中的公共API中不存在该功能.它由底层实现使用,但在这些系统上的开发人员无法使用(不支持).

这实际上意味着至少在未来几年内我没有希望,直到我们放弃对旧版Windows平台的支持.这也意味着I/O的情况在Windows Vista之前的操作系统上非常糟糕.完全缺失的其他痛苦部分是可以取消那些旧系统上的同步和异步I/O.

此外,我仍然会错过答案的一部分:是否可以通过任何方式测试旗帜的存在?我没有找到这样做的功能.这意味着如果我们想要保证文件对象中存在某些标志,则必须重新打开该文件.

  • 读者注意:`ReOpenFile()` 非常适用于文件,因为您可以重新打开文件流并向新句柄添加异步 I/O 支持,但这不适用于匿名管道(总是返回 `ERROR_PIPE_BUSY`)。 (2认同)

Mar*_*šík 3

3年过去了,Windows 8已经发布。感谢 Windows 8 中控制台实现中引入的回归,我必须对引发此问题的问题采取一些措施。所以我最终尝试使用 ReOpenFile() 函数调用。

\n\n

一句话:对于我的目的来说,它毫无用处。

\n\n

ReOpenFile() API 用于\xe2\x80\x9c 获取现有文件句柄并获取具有不同访问权限集的另一个句柄\xe2\x80\x9d。至少原文章中是这么说的。

\n\n

我尝试在控制台输入句柄上使用 ReOpenFile():

\n\n
  stdin_in = GetStdHandle(STD_INPUT_HANDLE);\n  stdin_in_operlapped = ReOpenFile(stdin_in, GENERIC_READ | GENERIC_WRITE,\n                                   FILE_SHARE_READ, FILE_FLAG_OVERLAPPED);\n  if (stdin_in_operlapped ==  INVALID_HANDLE_VALUE)\n    {\n      my_debug("failed to ReOpen stdin handle with OVERLAPPED flag: %d", GetLastError());\n      exit(1);\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

我得到的是:错误 1168: \xe2\x80\x9cElement Not Found\xe2\x80\x9d。\xe2\x80\x9c谢谢微软\xe2\x80\x9d。我什至不会尝试将它用于匿名管道,因为文档指出:

\n\n

\xe2\x80\x9c 匿名管道不支持异步(重叠)读写操作。这意味着您不能将 ReadFileEx 和 WriteFileEx 函数与匿名管道一起使用。此外,当这些函数与匿名管道一起使用时,ReadFile 和 WriteFile 的 lpOverlapped 参数将被忽略。\xe2\x80\x9d

\n\n

谢谢大家的建议。当异步读取句柄时,还必须准备好操作可能同步完成这一事实。我知道。我问这个问题的主要原因是:

\n\n

当对某些对象(至少是匿名管道和 Windows 8 中的控制台输入)发出同步读取时,从同一句柄上的另一个线程调用 CloseHandle() 将失败或挂起,直到 ReadFile() 完成;这意味着在许多情况下它将无限期地挂起。这就是为什么我想用异步替换同步句柄的原因。

\n\n

现在我很清楚,在 Windows 操作系统中根本不可能以直接的方式取消某些读取操作。当从同步句柄读取时,即使 ReadFile() 仍在某个线程中从句柄读取,也只需退出应用程序,因为根本不可能可靠地唤醒这样的读取操作。据了解...在较新的操作系统中可以取消该操作。但是,无法知道线程是否已处于 ReadFile() 调用中或尚未处于调用中。如果尚未调用 ReadFile(),则没有可取消的操作,后续读取将挂起。唯一的方法是关闭句柄,但该操作会挂起,或者在某些对象和某些操作系统上失败。唯一正确的解决方案是异步 I/O。但是,正如我在开头提到的,我们的应用程序是由第三方应用程序启动的,我们不能强迫它们始终创建为 stdio 设置了重叠标志的命名管道。

\n\n

我放弃并打算实施令人讨厌的丑陋黑客......我们将不得不继续阅读,而不会从使用 OVERLAPPED 标志创建的句柄中没有重叠结构,并泄漏句柄和线程......

\n