【问题标题】:How to ensure and force ownership of a handle to prevent unwanted CloseHandle calls?如何确保并强制拥有句柄以防止不必要的 CloseHandle 调用?
【发布时间】:2018-10-09 16:33:19
【问题描述】:

TL;DR

假设我实现了一个类,该类实现了一个返回 HANDLE 的 API。 假设我的班级是 HANDLE 的所有者并负责创建、更新和关闭它。

如何防止我的 API 调用者关闭我的句柄并破坏我的设计?

背景

CreateToolhelp32Snapshot 是一个昂贵的电话,必须明智地使用。 我的解决方案中有很多项目,每个项目都会不小心调用CreateToolhelp32Snapshot

我想实现一个包装类,它提供对CreateToolhelp32Snapshot 的“智能”访问。

我的班级将是CreateToolhelp32Snapshot 返回的HADNLE 的所有者,并将在需要时更新它(重新调用CreateToolhelp32Snapshot)(尚未完全定义何时需要)。

为简单起见,我们假设我的应用程序是单个进程和线程,并且我的类只有一个实例。

【问题讨论】:

  • 如果HANDLE 归您的类实现所有,请不要将其交给客户。如果您需要为客户提供与您的班级交流的方式,请给他们一个您制作的句柄类型,您的班级可以映射到实际的HANDLE,而无需离开您的班级。
  • 如果您以正常方式(例如 pimpl)隐藏 HANDLE 的实现,您应该不会遇到很多问题。 HANDLE 不会创建或删除任何内容,您只需 2 个 API 调用即可创建和销毁。但我想我没有得到任何东西。
  • @IInspectable 我的包装类的客户端需要HANDLE 才能使用许多其他 WINAPI。我不想封装使用该句柄的整个 API 系列。
  • 您当然可以在将句柄传递给客户端之前调用DuplicateHandle,并强制客户端关闭此句柄。但是如果他们不关闭它会怎样?如果客户使用无效参数调用您的代码会怎样?如果客户端代码不正确和崩溃过程怎么办?只需将原始句柄传递给客户端,它就不能关闭它。如果客户端代码不正确 - 这不是您的问题。它总是可能不正确并破坏所有
  • 也可以作为“解决方案”调用SetHandleInformationHANDLE_FLAG_PROTECT_FROM_CLOSE 以保护您的句柄不被关闭。最后自己删除这个标志。猜客户也不要尝试这样做))

标签: c++ winapi c++14 handle


【解决方案1】:

假设我实现了一个实现返回 HANDLE 的 API 的类

您退回把手破坏了您的设计。您的包装器的用户不应该关心句柄,而是关心他们想要的数据,例如您的案例中的模块列表。公开一个可以将此类数据返回给您的用户的 API。

class snapshot
{
    public:
    snapshot()
    {
        m_handle = CreateToolhelp32Snapshot(...);
    }
    ~snapshot()
    {
        CloseHandle(m_handle);
    }
    getModules()
    {
        // use m_handle to return a list of modules.
    }
    private:
    HANDLE m_handle;
};

【讨论】:

  • 这是一种方法,但我不想封装使用该句柄的整个 API 系列。这似乎是一个重大的矫枉过正,真的让我相信我的设计有缺陷。
  • @idanshmu 您指的是哪个 API 系列?在我看来,既然您是手柄的所有者,那么您应该控制它的使用方式。如果您想以任何方式公开句柄,那么您必须相信 API 的用户不会干预句柄。
  • 公开一个类的成员并不意味着公开所有权或完全信任调用者。就我而言,我想公开只有“阅读”访问权限的成员。与您返回 const 成员相同。微软本可以轻松地将 const 概念带到他们的HANDLE
  • @ida:你不能保护HANDLE,它只是下面的一个指针。如果客户想在上面调用CloseHandle,没有什么可以阻止的。如果您不希望您的客户关闭手柄,请不要将其分发出去。制作HANDLE const 对您没有任何帮助;毕竟它只是一个值,Microsoft 无法提供您所要求的任何东西。
【解决方案2】:

你可以做的是使用 DuplicateHandle 和 require 调用者调用 CloseHandle。除此之外,您必须信任调用者。

【讨论】:

    【解决方案3】:

    你不能。只要你不愿意完全封装HANDLE的所有用途,可以直接访问HANDLE的外部代码都可以关闭它。这和能做delete smart_ptr.get();没什么区别。

    C++ 可以保护您免受意外误用。它不能保护你免受背信弃义。如果使用智能手柄类的用户没有收到智能手柄类是HANDLE 所有者的备忘录,那么您无能为力。

    【讨论】:

      【解决方案4】:

      如果我要实现该类,我只需编写方法Enumerate 并调用CreateToolhelp32Snapshot、另一组函数,然后调用CloseHandleHANDLE 不会是类本身的一部分,而是 Enumerate 方法的局部。 枚举将填充所需的结构(用户定义)并创建该结构的vector(或 (unordered_)map,如果需要某些 PID -> ProcessInformation 访问。)。 该类将促进获取该枚举数据的方法。对于一流的 C++ 类,我实际上会实现 beginend 方法,以便类/对象可以与基于范围的 for 循环一起使用。

      CreateToolhelp32Snapshot的实际处理中,为什么真的需要延迟?当真正的调用者在该句柄上调用一些安全 Win32 API 时,正在运行的进程的状态将会改变。如果您的班级对象停留很长时间(呃),那么您所拥有的只是针对该句柄的陈旧/不一致的信息。

      【讨论】:

        猜你喜欢
        • 2011-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-09
        • 2013-12-13
        • 1970-01-01
        • 1970-01-01
        • 2023-01-15
        相关资源
        最近更新 更多