有没有适当的方法可以检查HKEY/PHKEY是否有效?

Is there a Proper way of Checking if an HKEY/PHKEY is Valid?

提问人:Azriel Elijay 提问时间:2/11/2023 最后编辑:Azriel Elijay 更新时间:2/13/2023 访问量:121

问:

REGISTRY()
{
    /*HKEY*/m_pRoot = nullptr;
    /*HKEY*/m_pReturnKey = nullptr;
    m_pPath = nullptr;
    m_pValueName = nullptr;
}
~REGISTRY()
{
    if ( m_pReturnKey ) 
    {
        RegCloseKey(m_pReturnKey);
        m_pReturnKey = nullptr;
    }
    if ( m_pRoot ) 
    {
        RegCloseKey(m_pRoot);
        m_pRoot = nullptr;
    }
}

我有一个类来处理我的应用程序的所有注册表函数,我让对象的析构函数处理 s 的关闭(如上所示)。我采用了这里提供的答案,但不是使用,而是简单地将 s 设置为在对象构造期间,并检查它是否不在销毁期间并关闭它。我还使用智能指针<>使用它来确保即使抛出异常也会调用析构函数。HKEYHANDLESHKEYnullptrnullptrunique_ptr

由于 After Checking ,when , , , 或返回除此以外的任何东西,它不对传递的执行任何操作,并且它保持为 .RegOpenKeyExARegCreateKeyExARegSetValueExRegQueryValueExAERROR_SUCCESSHKEYnullptr

除了这种方法之外,有没有适当的方法可以检查HKEY/PHKEY是否有效?在 winreg.h 上搜索 MSDN 页面没有为我提供任何信息,如果它在那里,我可能是盲目的,或者它没有使用对像我这样的业余爱好者来说不明显的函数名称。

C++ WinAPI Visual-Studio-2008 注册表

评论

2赞 IInspectable 2/12/2023
这读起来不像是一个有用的抽象。如果你想要一个在自我清理之后清理的对象,让它的 c'tor 获取资源,如果它失败,则抛出。这样,您就不需要处理留出特殊值的问题;该对象将始终保留有效值,或者根本不存在。如果获取资源包含多个步骤,请使用函数来封装该逻辑。让它返回管理资源的任何类型的值。简而言之:使无法构造存储无效数据的对象。
2赞 IInspectable 2/12/2023
“它们要么一无所有,要么有效”——这就是问题所在。为什么不让类实例化变得不可能,除非值有效?一旦建立了这种不变性,就不需要再进行检查了。
1赞 Remy Lebeau 2/12/2023
@AzrielElijay “我只是真的很想知道 API 是否真的没有检查它是否真的活着/有效”——没有 API 来检查这一点。为您提供句柄的 API 要么成功,要么失败,您有责任检查该条件并在失败时忽略句柄。除非另有说明,否则将无效句柄返回给 API 是未定义的行为。
1赞 Remy Lebeau 2/12/2023
@AzrielElijay“有一个实例 RegCreateKeyExA 返回ERROR_SUCCESS但当使用 HKEY 时,它导致了访问冲突”——唯一可能发生的方式是,如果您以使其无效的方式覆盖了自身,或者您与 一起使用的其他参数在某种程度上无效。HKEYHKEY
1赞 Simon Mourier 2/13/2023
不是答案,但如果您使用 Visual Studio,则会提供一个 CRegKey 类(使用 ATL): learn.microsoft.com/en-us/cpp/atl/reference/cregkey-class 这是一个相当不错的注册表 RAII 助手,并且源代码是开放的

答:

1赞 IInspectable 2/13/2023 #1

winreg API 不会留出表示无效的值(与 fileapi 不同,CreateFileW 用于此目的)。相反,API 调用会报告它们是否成功,然后才能保证已将 写入调用方提供的位置。HKEYINVALID_HANDLE_VALUEHKEY

如果您的实现需要知道任何给定的信息是否代表有效数据,则必须单独记录此信息。从 C++17 开始,std::optional 是用于此目的的标准工具。

因此,解决您的问题的可能解决方案可能是:

class REGISTRY
{
    std::optional<HKEY> m_pRoot;
    std::optional<HKEY> m_pReturnKey;
    // ...
}

REGISTRY::~REGISTRY()
{
    if (m_pReturnKey)
    // equivalent to
    // if (m_pReturnKey.has_value())
    {
        RegCloseKey(m_pReturnKey.value());
        m_pReturnKey = {};
    }
    // ...
}

m_pRoot并且是默认初始化的,这意味着它们保存 类型的值。d'tor 会观察这一点,并且不做任何事情。如上所述,这解决了问题。m_pReturnKeystd::nullopt_t

评论

0赞 Azriel Elijay 2/13/2023
感谢您的输入!但是,正如我的标签所指出的那样,我正在应用它的项目是使用 MSVC2008SP1 编译的,所以我被限制在 C++03 和 boost 1.5 上,如果可以的话,我很想将项目提升到更高的标准,但第三方库不允许我。所以是不可能的。但这确实回答了我的问题,简单明了,即 API 实际上没有任何内容可以验证,并且只保证它在返回时的有效性。std::optionalwinregHKEYERROR_SUCCESS
1赞 IInspectable 2/13/2023
@AzrielElijay 好吧,我的错,我完全忽略了 visual-studio-2008 标签,但我想“你必须单独记录这些信息”算作一个答案。现在,如果你对 API 设计指南感兴趣(适用于 C++any),我将提供一些关于如何在不需要 C++17 的情况下更好地构建代码的注释。那将是固执己见的,强烈地这样做。
0赞 Azriel Elijay 2/13/2023
你之前对这个问题的评论越想越有意义,我听从了你的建议,如果不保证它有一个无效的句柄或值,就不可能实例化这个类。到目前为止,我已将它们分为两个类,这将确保至少确保使用该类的任何函数都无法实例化它,如果它传递的参数不能确保正确的.作为自学成才的人,我很高兴你们中的许多人至少能理解我的问题。HKEY