【问题标题】:How to check if a HANDLE is valid or not?如何检查 HANDLE 是否有效?
【发布时间】:2011-08-01 23:54:55
【问题描述】:

在 C++ 中,我打开了一个具有HANDLE 的串行端口。由于端口可能会被外部应用程序关闭,在读取数据之前如何验证HANDLE 是否仍然有效?

我认为可以通过检查 HANDLE 与合适的 API 函数来完成,但是哪个? 谢谢。

【问题讨论】:

  • 你有什么样的HANDLE?哪个函数创建了它?
  • 是哪个编译器和操作系统?
  • 根据删除答案下的cmets,是Win32。
  • 对不起,它是 Windows 32 并且 CreateFile() 是 HANDLE 创建者。 (请看答案)
  • 令一些 API 作者感到羞耻的是,名为 Get....Handle 的函数不一定会向系统文件或对象返回 HANDLE。例如,GdiGetSpoolFileHandle 出人意料地返回了伪装成 int HANDLE 的内部 ID。

标签: c++ c winapi port handle


【解决方案1】:

您可以使用DuplicateHandle 来测试句柄有效性。

第一种方法:您可以尝试复制要检查有效性的句柄。基本上无效句柄是不能复制的。

第二种方法:DuplicateHandle 函数确实从一开始就在 Win32 句柄描述符表中搜索一条空记录以重用它,并为它分配一个重复的句柄。您可以仅在大于您的句柄地址的值上测试重复的句柄地址值,如果它更大,则句柄不会被视为无效,因此不会被重用。但是这种方法是非常具体和有限的,它只在您要测试的句柄值地址之上没有更多空的或无效的句柄记录时才有效。

但是,上面所说的所有这些只有在您跟踪所有句柄的创建和复制时才有效。

Windows 7 的示例:

方法#1

// check stdin on validity

HANDLE stdin_handle_dup = INVALID_HANDLE_VALUE;
const bool is_stdin_handle_dup = !!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &stdin_handle_dup, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (is_stdin_handle_dup && stdin_handle_dup != INVALID_HANDLE_VALUE) {
    CloseHandle(stdin_handle_dup);
    stdin_handle_dup = INVALID_HANDLE_VALUE;
}

方法#2

// Assume `0x03` address has a valid stdin handle, then the `0x07` address can be tested on validity (in Windows 7 basically stdin=0x03, stdout=0x07, stderr=0x0b).
// So you can duplicate `0x03` to test `0x07`.

bool is_stdout_handle_default_address_valid = false;
HANDLE stdin_handle_dup = INVALID_HANDLE_VALUE;
const bool is_stdin_handle_dup = !!DuplicateHandle(GetCurrentProcess(), (HANDLE)0x03, GetCurrentProcess(), &stdin_handle_dup, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (is_stdin_handle_dup && stdin_handle_dup != INVALID_HANDLE_VALUE) {
    if (stdin_handle_dup > (HANDLE)0x07) {
        is_stdout_handle_default_address_valid = true; // duplicated into address higher than 0x07, so 0x07 contains a valid handle
    }
    CloseHandle(stdin_handle_dup);
    stdin_handle_dup = INVALID_HANDLE_VALUE;
}

【讨论】:

  • DuplicateHandle 是一个相当笨拙的函数。你为什么不发布一些示例代码?
  • @Elmue DuplicateHandle 没有其他代码就没有多大意义。这取决于你想做什么。
【解决方案2】:

我知道这有点晚了,但我有一个类似的问题要问你,如何检查管道(我使用 CreateFile 创建的管道)是否仍然打开(可能另一端关闭连接)并且可以读取,如果不是,请再次打开它。我按照@Felix Dombek 的建议做了,我使用 WriteFile 检查连接。如果它返回 1 表示管道已打开,否则我再次使用 CreateFile 打开它。这意味着您的管道是双工的。这是 CreateFile:
hPipe2 = CreateFile(lpszPipename2, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
这是我检查连接的方式:

while(1)
{   
    bool MessageSent = WriteFile(hPipe2, "Test", 0, &cbWritten, NULL);
    if (!(MessageSent))
    {
        LogsOut("Read pipe has been disconnected");
        //Call method to start the pipe again
        break;
    }
    Sleep(200); // I need this because it is a thread
}

这对我来说很好用:)

【讨论】:

    【解决方案3】:

    如果给你一个HANDLE 并且只是想查明它是否确实是一个打开的文件句柄,那么可以使用Windows API 函数GetFileInformationByHandle

    根据您的句柄授予您文件的权限,您还可以尝试使用SetFilePointer 移动文件指针,使用ReadFile 从其中读取一些数据,或使用WriteFile 执行空写入操作nNumberOfBytesToWrite 设置为 0。

    【讨论】:

      【解决方案4】:

      一些 WinAPI 函数返回无意义的 ERROR_INVALID_PARAMETER,即使传递了有效的句柄,所以一个真实的用例来检查句柄的有效性。

      GetHandleInformation 函数完成这项工作: http://msdn.microsoft.com/en-us/library/ms724329%28v=vs.85%29.aspx

      【讨论】:

        【解决方案5】:

        因为端口可能会被外部应用程序关闭

        这是不可能的,外部应用程序无法获得正确的句柄值以传递给 CloseHandle()。一旦你打开了端口,任何其他试图获取端口句柄的进程都会被拒绝访问。

        也就是说,有一些垃圾软件通过对存储进程句柄的未记录内核结构的秘密知识来破解此限制。你对他们无能为力,不要犯了同样的错误来参加这场战斗。你会输的。如果客户对此提出投诉,请向他们提供我的医生建议:“如果疼就不要这样做”。

        【讨论】:

        • 不是这样。具有适当权限的应用程序可以使用 DuplicateHandle() 关闭另一个进程中的句柄。记录在案;请参阅 MSDN 页面。
        • @janm - 这假设第二个进程可以获得句柄值。当拥有过程不合作时,这是非常不重要的。要求未记录的内核表黑客或进程的其他秘密知识从内存中读取它。
        • @hans - 如果句柄最初是从另一个应用程序传递的,并且该应用程序记住它传递的句柄值,那么它是微不足道的。
        • 被最后一句覆盖了。
        • 取决于谁拥有另一个进程。我的答案的最后几句话也涵盖了! (区别:“记账”与“不记账”)。但是,是的,我同意这是一个可能永远不应该使用的功能。
        【解决方案6】:

        为了检查句柄,首先我们需要知道我们的句柄是什么,(对于文件/端口/窗口,...),然后找到一个合适的函数来检查它(感谢@janm的帮助) .请注意,该功能的职责可能是专门针对此目的地的,也可能不是。在我通过 CreateFile() 打开串行端口的情况下,我可以通过填充我们的 COM 信息结构的 GetCommState() API 函数检查 COM 状态。如果端口不再打开或无法访问,则函数返回 0,并且如果您立即调用 GetLastError(),您将获得 ERROR_INVALID_HANDLE 值。感谢大家的帮助。

        【讨论】:

        • 小心;调用 GetCommState 之类的东西是不够的,因为竞争条件可能会打开其他东西并且操作系统会重用句柄值。
        【解决方案7】:

        检查句柄是否“有效”是错误的。你需要有更好的方法来处理这个问题。

        问题是,一旦句柄被关闭,相同的句柄值可以由不同的东西的新打开生成,并且您的测试可能会说句柄是有效的,但您并没有在您认为的文件上操作是。

        例如,考虑这个序列:

        1. 手柄打开,实际值为0x1234
        2. 使用句柄并传递值
        3. 手柄已关闭。
        4. 程序的其他部分打开一个文件,得到句柄值 0x1234
        5. 原始句柄值是“检查有效性”,并通过。
        6. 使用了句柄,操作了错误的文件。

        因此,如果这是您的流程,您需要跟踪哪些句柄有效,哪些无效。如果您从其他进程获得句柄,它将使用 DuplicateHandle() 将其放入您的进程中。在这种情况下,您应该管理句柄的生命周期,而源进程不应该为您这样做。如果您的句柄正在从另一个进程关闭,我假设您是这样做的人,并且您需要处理簿记。

        【讨论】:

        • 我不这么认为。如果它是同一程序的不同线程,是的,有可能。但首先你有控制权,因为它是你的程序。现代操作系统实现只会 +1 来处理值,这使得在短时间内不可能发生冲突。 (如果你的程序写得很仔细,在同一个程序中,有可能检测到这个问题。)如果你在谈论另一个进程......我相信一个没有句柄所有权的进程(具有相同的句柄值)会将其视为无效句柄,否则属于安全漏洞。
        • @RobinHsu 类 Unix 操作系统分配最小编号的可用句柄。这使得close(0); dup(h); 接近和select() 工作。在类 Unix 系统上,是的,您不能关闭另一个进程中的句柄。 Windows 不做任何 +1 保证。它确实提供了 +4 保证,因此低位可用于应用目的。我们不知道代码中值的生命周期——对于一个长期存在的进程来说,这可能是一个很长的时间段。正确性不应该取决于“短时间”。在 DuplicateHandle() 上 - 具有适当的权限,另一个进程可能会导致意外。
        • 谢谢。我想我明白你的意思。然而,通过仔细编程,检查句柄应该仍然是可能的。 (好吧,程序需要确保在可能创建句柄的地方放置一个检查点。不过可能非常乏味,我同意你的 cmets 的这个乏味的做法。)。
        • @RobinHsu 真正的问题是,只有当您保证进程在关闭正在检查的句柄后不会打开任何句柄时,“检查​​句柄”才能正确。一旦你做出了强有力的保证,你就不需要“检查句柄”功能了。
        • 不完全正确。当手柄为管道时,可以被对方关闭。 (并被对方无效)。调用PeekNamedPipe()会得到错误返回值,调用getLastError()时错误是无效句柄。
        【解决方案8】:

        可能你在windows下使用ReadFile读取数据。检查它的唯一方法是尝试阅读。如果HANDLE 无效,它会返回一个错误代码(使用GetLastEror() 查看它是哪一个),可能是ERROR_HANDLE_INVALID

        【讨论】:

        • 也可以查看GetCommState的返回值,看看HANDLE是否还有效。
        猜你喜欢
        • 1970-01-01
        • 2010-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-08
        • 2021-11-30
        相关资源
        最近更新 更多