如何在运行时检查Win32句柄的“类型”

时间:2014-07-22 14:36:21

标签: c++ winapi c++11

我必须在C ++ 11中编写一个引用计数的包装类,用于Win32句柄,如HFONT,HWND,HMODULE等。我想使用一个WinHandle类,它隐式地转换为所有句柄类型(都是void * typedef)。不幸的是,所有句柄都有不同的函数来销毁底层对象,例如DestroyWindow(),CloseHandle(),DeleteObject()等等,因此我需要为每个句柄类型使用不同的类来实现适当的destroy-function。 (这将是47个类+基类,包括用户对象,gdi对象和内核对象)

那么有没有办法在运行时确定句柄是哪个“类型”,或者更确切地说需要调用哪个函数? (在文档中我只发现了isWindow()函数)

我已经考虑过使用RTTI或调用所有删除函数,直到其中一个成功。 RTTI不起作用,因为所有HANDLE类型都是void *的typedef,因此是相同的。后者可能会工作,但所有句柄必须是唯一的才能正常工作(没有GDI句柄可以拥有与用户句柄或内核句柄相同的值)否则它可能会导致错误和内存泄漏

2 个答案:

答案 0 :(得分:2)

如果您要查找的唯一内容是引用计数句柄,为什么不使用shared_ptr

例如:

shared_ptr<void> file( CreateFile(L"la.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL), CloseHandle);   

DWORD written;
WriteFile(file.get(), //get handle
          "ABC\r\n", 
          5,
          &written, 
          NULL);

在代码中没有足够的印记,你不必写40个课程。


您可以通过为每个关闭的类型定义一些函数来避免每次传递相关的关闭函数,例如:

auto make_handle_CloseHandle = [](HANDLE h){ return  (shared_ptr<void>(h,CloseHandle)); };

auto file = make_handle_CloseHandle(CreateFile(L"la.txt", /*same ...*/));

DWORD written;
WriteFile(   file.get(), //get handle
            "ABC\r\n", 
            5,
            &written, 
            NULL);

并将其放在相关命名空间下的某个头文件中,这样您每次都不必输入关闭函数的名称,这些函数的用户将知道调用哪个函数(根据make_handle_*名称)这可能比使用句柄自动识别句柄类型更安全。

答案 1 :(得分:2)

由于CreateFile的失败结果为INVALID_HANDLE_VALUE(-1),因此我不确定这是否是一个好的解决方案。

https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea

实现某种shared_resource或unique_resource类型,而不是使用旨在引用内存地址的方法,可以接受模板参数或封装包装类型特征的单个参数。

我的看起来像这样:

template <typename TRAITS>
struct SharedHandle
{
....
}

,其用法如下:

struct TestTraits
{
    using HandleType = int;
    static constexpr HandleType INVALID = 0;
    static void Close(HandleType& h)
    {
        h = INVALID;
    }
};

using SharedHandleTestType = SharedHandle<TestTraits>;


TEST(ClvLib_SharedHandle_Tests, SharedHandle_default)
{
    auto tt = SharedHandleTestType();

    EXPECT_FALSE(tt);
}

TEST(ClvLib_SharedHandle_Tests, SharedHandle_good)
{
    auto tt = SharedHandleTestType(1);

    EXPECT_TRUE(tt);
}

TEST(ClvLib_SharedHandle_Tests, SharedHandle_use)
{
    auto tt = SharedHandleTestType(1);

    auto result = [](int t)->int {return t + 1; }(tt);
    auto expected = tt.Get() + 1;

    EXPECT_EQ(expected, result);
}

TEST(ClvLib_SharedHandle_Tests, SharedHandle_copy1)
{
    auto tt = SharedHandleTestType(1);
    auto t2 = tt;

    EXPECT_TRUE(tt);
    EXPECT_TRUE(t2);

    tt = 0;

    EXPECT_FALSE(tt);
    EXPECT_TRUE(t2);
}

TEST(ClvLib_SharedHandle_Tests, SharedHandle_copy2)
{
    auto tt = SharedHandleTestType(1);
    auto t2 = tt;

    EXPECT_TRUE(tt);
    EXPECT_TRUE(t2);

    tt = 0;

    EXPECT_FALSE(tt);
    EXPECT_TRUE(t2);

    t2.Release();

    EXPECT_FALSE(tt);
    EXPECT_FALSE(t2);
}

对于Windows文件句柄:

struct WinHandleTraits_IHV
{
    using HandleType = HANDLE;
    static constexpr HandleType INVALID = INVALID_HANDLE_VALUE;
    static void Close(HandleType& h)
    {
        CloseHandle(h);
    }
};

using WinFileHandle = SharedHandle<WinHandleTraits_IHV>;