12

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

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

所以首先我会问:

假设我有一个句柄:

HANDLE h;

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

到目前为止,我知道的唯一方法是尝试将此类句柄注册到 I/O 完成端口(使用CreateIoCompletionPort())。如果成功,则使用 FILE_FLAG_OVERLAPPED 创建句柄。但是只有 I/O 完成端口必须使用,因为句柄不能在不关闭HANDLE h自身的情况下从它注销。

如果有一种简单的方法来确定 的存在FILE_FLAG_OVERLAPPED,那么我的第二个问题就会出现:

有什么办法可以将这样的标志添加到已经存在的句柄中?这将使最初为同步操作打开的句柄为异步打开。有没有办法创建相反的(FILE_FLAG_OVERLAPPED从异步中删除创建同步句柄)?

在阅读了 MSDN 并搜索了很多之后,我没有找到任何直接的方法。至少会有一些技巧可以做到这一点吗?CreateFile()就像使用函数或类似的东西以相同的方式重新创建句柄?甚至部分记录或根本没有记录的东西?

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

亲爱的 Windows 大师:请帮助!

带着敬意

马丁

4

7 回答 7

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 的可能性。

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

于 2010-03-19T22:08:49.907 回答
3

3 年过去了,Windows 8 已经发布。感谢在 Windows 8 中实现控制台时引入的回归,我必须对触发这个问题的问题做一些事情。所以我终于尝试使用 ReOpenFile() 函数调用。

一句话:就我的目的而言,它是无用的。

ReOpenFile() API 用于“获取现有文件句柄并获取具有不同访问权限集的另一个句柄”。至少在原文中是这样说的。

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

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

我得到的是:错误 1168:“未找到元素”。“谢谢微软”。我什至不会尝试将它用于匿名管道,因为文档指出:

“匿名管道不支持异步(重叠)读写操作。这意味着您不能将 ReadFileEx 和 WriteFileEx 函数用于匿名管道。另外,ReadFile 和 WriteFile 的 lpOverlapped 参数在与匿名管道一起使用时会被忽略。”</p>

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

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

现在我很清楚,在 Windows 操作系统中根本不可能以直接的方式取消某些读取操作。从同步句柄读取时,即使 ReadFile() 仍在某个线程中从句柄读取,也只需退出应用程序,因为根本不可能可靠地唤醒这样的读取操作。知道... 在较新的操作系统中,可以取消该操作。但是,无法知道线程是否已经在 ReadFile() 调用中,或者还没有。如果尚未调用 ReadFile(),则没有取消操作,后续读取将挂起。唯一的方法是关闭句柄,但该操作在某些对象和某些操作系统上挂起或失败。唯一合适的解决方案是异步 I/O。但是,正如我一开始提到的,

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

于 2013-05-10T13:17:37.337 回答
2

如果我了解您的需求,我建议您不要关心是否使用重叠标志打开。我相信您可以OVERLAPPED在同步和异步情况下安全地传递结构。您的代码需要能够处理ReadFile()返回falseGetLastError()返回ERROR_IO_PENDING。您还需要添加对 , 等的适当GetOverlappedResult()调用WaitForSingleObject()

MSDN 文章在ReadFile()“使用同步文件句柄的注意事项”和“使用异步文件句柄的注意事项”下的“同步和文件位置”部分中有一些很好的信息。

于 2010-11-02T01:39:52.603 回答
1

测试句柄标志可能应该与测试创建句柄的权限相同。尝试一下。如果 API 失败,请尝试回退。如果失败,则返回错误。

我认为真正能说明问题的是 ReadFile 的文档说“如果使用 FILE_FLAG_OVERLAPPED 打开 hFile,...函数可能会错误地报告读取操作已完成”。

我对错误的解释是,(你需要问自己的问题是):如果可以检查文件句柄的重叠状态,为什么 ReadFile 不进行检查,然后相应地验证 OVERLAPPED 结构,以明确如果使用重叠句柄以非重叠方式调用失败?

于 2010-03-19T11:02:13.597 回答
1

我不知道确定句柄标志和使用 ReOpen api 的副作用的方法,但因为你的目标是

只有一种方法来处理所有句柄将非常有用

如果您需要同步行为(我的意思是对非重叠句柄使用同步 api,并且使用 OVERLAPPED 结构提供异步 api 并随后等待重叠的事件),如果句柄以非重叠模式打开,您也可以始终使用异步 api,如前所述通过@Brett
,我可以确认这有效(至少对于命名管道)es:

void connectSynchronous(HANDLE hPipeThatWeDontKnowItsFlag){
    ...
    BOOL bRet = ::ConnectNamedPipe(hPipeThatWeDontKnowItsFlag, pOverlapped);

    if(bRet == FALSE){
        DWORD dwLastErr = ::GetLastError();

        if(dwLastErr == ERROR_IO_PENDING){
            //The handle was opened for asynchronous IO so we have to wait for the operation
            ...waitFor on the overlapped hEvent;

        }else if(dwLastErr == ERROR_PIPE_CONNECTED){
            //The handle was opened for synchronous IO and the client was already connected before this call: that's OK!
            return;
        }else{
            throw Error(dwLastErr);
        }
    }/*else{
        //The handle was opened for synchronous IO and the client has connected: all OK
    }*/
}
于 2013-01-29T17:36:33.410 回答
0

CreateIoCompletionPort hack 的替代方法是使用 NULL lpOverlapped 执行零字节 ReadFile。如果它因 ERROR_INVALID_PARAMETER 而失败,则假定它是使用 FILE_FLAG_OVERLAPPED 打开的。

于 2017-02-21T00:01:40.637 回答
0

NtSetInformationFile, FileModeInformation, 清除FILE_SYNCHRONOUS_IO_NONALERT标志。

行为因“文件系统”而异。(例如,管道由 npfs 提供)

我在 Windows XP 上尝试了匿名管道,但失败了。

于 2020-08-21T09:29:13.697 回答