C++实时感知使用摄像头的应用

背景

有时候我们需要知道当前哪个应用在使用摄像头。微软提供了对应的硬件注册表来记录摄像头的使用情况。通过监控和获取对应注册表的变化和内容,就能够实时获取当前使用摄像头的应用信息。

注册表路径:HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\webcam
其中使用过摄像头的Microsoft Store应用会列举在此路径下,非Microsoft Store应用会在子目录NonPackaged路径下。

应用注册表下会记录上一次使用摄像头时间,因此当应用正在试用摄像头时,LastUsedTimeStop的值为0

方案

根据微软注册表的规律,可以通过以下方案来实时感知使用摄像头的应用

  1. 通过CreateEvent创建通知事件
  2. 通过RegNotifyChangeKeyValue创建注册表监控
  3. 通过WaitForSingleObject等待注册表发生变化
  4. 通过RegEnumSubKeys获取子应用注册表,再通过RegGetQword查询LastUsedTimeStop的值
  5. LastUsedTimeStop为0的应用,即是正在使用摄像头的应用

实现

  1. 创建通知事件
// 一直进行监控
while (true) {
	HANDLE hNotify = CreateEventW(NULL, FALSE, TRUE, L"RegeditNotifyChanged");
}
  1. 监控注册表变化
std::wstring webcameKeyName = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore\\webcam";
HKEY hRegKey;
if (RegOpenKeyExW(HKEY_CURRENT_USER,
	webcameKeyName.c_str(),
	0,
	KEY_NOTIFY | KEY_QUERY_VALUE,
	&hRegKey
	) != ERROR_SUCCESS) {
	CloseHandle(hNotify);
	RegCloseKey(hRegKey);
}
if (RegNotifyChangeKeyValue(hRegKey,
	TRUE,
	REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET,
	hNotify, TRUE
	) != ERROR_SUCCESS) {
	CloseHandle(hNotify);
	RegCloseKey(hRegKey);
}
  1. 等待注册表发生变化
if (WaitForSingleObject(hNotify, INFINITE) != WAIT_FAILED) {
	// 打印出当前正在使用摄像头的应用名称
	std::wcout << GetUseCameraAppName() << std::endl;
	CloseHandle(hNotify);
	RegCloseKey(hRegKey);
}

4.读取注册表获取当前正在使用摄像头的应用

// 非Microsoft Store会记录全路径,只保留最后的exe名称
std::wstring GetLastStrBySplit(const std::wstring& inputStr, const wchar_t& delimiter)
{
	std::wstringstream sStream(inputStr);
	std::wstring item;
	while (std::getline(sStream, item, delimiter));
	return item;
}
// 获取指定注册表路径中是否有应用使用摄像头
std::wstring GetUseCameraAppNameByKeyName(const std::wstring& keyName)
{
	try {
		auto regKey = win32::RegOpenKey(HKEY_CURRENT_USER, keyName);
		// 枚举所有子应用
		auto subKeys = win32::RegEnumSubKeys(regKey.Get());
		for (auto subKey : subKeys) {
			try {
				// 获取上一次使用时长
				ULONGLONG timeStop = win32::RegGetQword(regKey.Get(), subKey, L"LastUsedTimeStop");
				if (timeStop == 0) {
					// 保留应用名称
					return GetLastStrBySplit(subKey, L'#');
				}
			} catch (win32::RegistryError& e) {
			}
		}
	} catch (win32::RegistryError& e) {
		std::wcout << keyName << e.what() << std::endl;
	}
	return L"";
}

// 先检查Microsoft Store应用,再检查三方应用
std::wstring GetUseCameraAppName()
{
	std::wstring webcameKeyName = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore\\webcam";
	std::wstring systemApp = GetUseCameraAppNameByKeyName(webcameKeyName);
	if (!systemApp.empty()) {
		std::wcout << systemApp << std::endl;
		return systemApp;
	}
	std::wstring thirdAppKeyName = webcameKeyName + L"\\NonPackaged";
	std::wstring thirdApp = GetUseCameraAppNameByKeyName(thirdAppKeyName);
	if (!thirdApp.empty()) {
		std::wcout << thirdApp << std::endl;
	}
	return thirdApp;
}

额外说明

以上示例中读取和遍历注册表用的函数,是来自于Giovanni Dicanio写的C++ - 使用新式 C++ 访问 Windows 注册表文章,附上Registry.hpp的全部代码供参考

/
//
// Modern C++ Wrappers for Win32 Registry Access APIs
// 
// Copyright (C) 2017 by Giovanni Dicanio <giovanni.dicanio AT gmail.com>
// 
// Compiler: Visual Studio 2015
// Compiles cleanly at /W4 in both 32-bit and 64-bit builds.
// 
// -----------------------------------------------------------------------------------
// 
// The MIT License(MIT)
//
// Copyright(c) 2017 Giovanni Dicanio
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
/


#include <Windows.h>        // Win32 Platform SDK
#include <string.h>         // For wcslen()
#include <memory>           // For std::unique_ptr
#include <stdexcept>        // For std::runtime_error
#include <string>           // For std::wstring
#include <utility>          // For std::pair, std::swap
#include <vector>           // For std::vector


namespace win32
{

// Exception class representing an error from Windows Registry API calls
class RegistryError;

// Wrapper around a raw HKEY handle
class RegKey;


//
// Wrappers for getting registry values
// 

DWORD                       RegGetDword         (HKEY hKey, const std::wstring& subKey, const std::wstring& value);
ULONGLONG                   RegGetQword         (HKEY hKey, const std::wstring& subKey, const std::wstring& value);
std::wstring                RegGetString        (HKEY hKey, const std::wstring& subKey, const std::wstring& value);
std::vector<std::wstring>   RegGetMultiString   (HKEY hKey, const std::wstring& subKey, const std::wstring& value);
std::vector<BYTE>           RegGetBinary        (HKEY hKey, const std::wstring& subKey, const std::wstring& value);


//
// Enumerates all the values under the given (open) hKey registry key.
// The information for each value is stored in a pair<wstring, DWORD>,
// representing the value's name and value's type respectively.
//
std::vector<std::pair<std::wstring, DWORD>> RegEnumValues(HKEY hKey);

//
// Enumerate sub-keys of the given (open) hKey registry key.
// 
std::vector<std::wstring> RegEnumSubKeys(HKEY hKey);


//
// Wrappers for creating and opening registry keys
//

RegKey RegCreateKey(
    HKEY hKeyParent,
    const std::wstring& keyName,
    LPTSTR keyClass = REG_NONE,
    DWORD options = REG_OPTION_NON_VOLATILE,
    REGSAM access = KEY_READ | KEY_WRITE,
    SECURITY_ATTRIBUTES* securityAttributes = nullptr,
    DWORD* disposition = nullptr
);

RegKey RegOpenKey(
    HKEY hKeyParent, 
    const std::wstring& keyName, 
    REGSAM access = KEY_READ | KEY_WRITE
);



//-----------------------------------------------------------------------------
//                              Implementations
//-----------------------------------------------------------------------------


//
// Safe, efficient and convenient C++ wrapper around raw HKEY handles.
//
// This class is movable but not copyable.
//
// This class is designed to be very *efficient*, for example non-throwing 
// operations are carefully marked as noexcept, so the C++ compiler can emit 
// optimized code for them.
//
// Moreover, this class just wraps a raw HKEY handle, so it's very efficient
// (there's no shared-ownership overhead like in std::shared_ptr; you can 
// think of this class kind of like a std::unique_ptr for HKEYs).
//
// The class is also swappable (defines a custom non-throwing swap function); 
// comparison and relational operators (==, !=, <, <=, >, >=) are properly 
// overloaded as well.
// 
class RegKey
{
public:

    //
    // Initialize as an empty key handle
    //
    RegKey() noexcept = default;

    //
    // Take ownership of the input raw key handle
    //
    explicit RegKey(HKEY hKey) noexcept
        : m_hKey{ hKey }
    {}

    //
    // Take ownership of the input key handle.
    // The input key handle wrapper is reset to an empty state
    //
    RegKey(RegKey&& other) noexcept
        : m_hKey{ other.m_hKey }
    {
        //
        // Other doesn't own the raw handle anymore
        //
        other.m_hKey = nullptr;
    }

    //
    // Move-assign from the input key handle.
    // Properly check against self-move-assign (which is safe and does nothing).
    //
    RegKey& operator=(RegKey&& other) noexcept
    {
        //
        // Prevent self-move-assign
        //
        if ((this != &other) && (m_hKey != other.m_hKey))
        {
            Close();

            //
            // Move from other
            //
            m_hKey = other.m_hKey;
            other.m_hKey = nullptr;
        }
        return *this;
    }

    //
    // Ban copy
    //
    RegKey(const RegKey&) = delete;
    RegKey& operator=(const RegKey&) = delete;

    //
    // Safely close the wrapped key handle (if any)
    //
    ~RegKey() noexcept
    {
        Close();
    }

    //
    // Give read-only access to the wrapped HKEY handle
    //
    HKEY Get() const noexcept
    {
        return m_hKey;
    }

    //
    // Close current HKEY handle.
    // If key wasn't open, does nothing.
    //
    void Close() noexcept
    {
        if (IsValid())
        {
            ::RegCloseKey(m_hKey);
            m_hKey = nullptr;
        }
    }

    //
    // Is the wrapped HKEY handle valid?
    //
    bool IsValid() const noexcept
    {
        return m_hKey != nullptr;
    }

    //
    // Has the same semantic of IsValid, but allows a short "if (regKey)" syntax
    //
    explicit operator bool() const noexcept
    {
        return IsValid();
    }

    //
    // Transfer ownership of current HKEY to the caller.
    // Note that the caller is responsible for closing the key raw handle!
    //
    HKEY Detach() noexcept
    {
        HKEY hKey = m_hKey;

        //
        // We don't own the HKEY handle anymore
        //
        m_hKey = nullptr;

        //
        // Transfer ownership of the raw handle to the caller
        //
        return hKey;
    }

    //
    // Take ownership of the HKEY handle.
    // Safely close any previously open handle.
    // Input key handle can be nullptr; in this case this wrapper wraps an empty handle.
    //
    void Attach(HKEY hKey) noexcept
    {
        //
        // Prevent self-attach
        // 
        if (m_hKey != hKey)
        {
            Close();

            //
            // Take ownership of hKey
            //
            m_hKey = hKey;
        }
    }

    //
    // Non-throwing swap
    //
    friend void swap(RegKey& a, RegKey& b) noexcept
    {
        //
        // Enable ADL (not necessary in this case, but good practice)
        //
        using std::swap;

        //
        // Swap the raw handle members
        //
        swap(a.m_hKey, b.m_hKey);
    }


    //
    // Overload comparison and relational operators
    // 

    friend bool operator==(const RegKey& a, const RegKey& b) noexcept
    {
        return a.m_hKey == b.m_hKey;
    }

    friend bool operator!=(const RegKey& a, const RegKey& b) noexcept
    {
        return a.m_hKey != b.m_hKey;
    }

    friend bool operator<(const RegKey& a, const RegKey& b) noexcept
    {
        return a.m_hKey < b.m_hKey;
    }

    friend bool operator<=(const RegKey& a, const RegKey& b) noexcept
    {
        return a.m_hKey <= b.m_hKey;
    }

    friend bool operator>(const RegKey& a, const RegKey& b) noexcept
    {
        return a.m_hKey > b.m_hKey;
    }

    friend bool operator>=(const RegKey& a, const RegKey& b) noexcept
    {
        return a.m_hKey >= b.m_hKey;
    }


private:
    //
    // The wrapped registry key handle
    //
    HKEY m_hKey = nullptr;
};


//
// Exception class representing an error of a Windows Registry API call
//
class RegistryError : public std::runtime_error
{
public:
    RegistryError(const char* message, LONG errorCode)
        : std::runtime_error{ message }
        , m_errorCode{ errorCode }
    {}

    LONG ErrorCode() const noexcept
    {
        return m_errorCode;
    }

private:
    LONG m_errorCode;
};


//
// Inline Function Implementations
// 

inline DWORD RegGetDword(HKEY hKey, const std::wstring& subKey, const std::wstring& value)
{
    //
    // Read a 32-bit DWORD value from the registry
    // 
    DWORD data{};
    DWORD dataSize = sizeof(data);
    LONG retCode = ::RegGetValue(
        hKey, 
        subKey.c_str(),
        value.c_str(), 
        RRF_RT_REG_DWORD, 
        nullptr, 
        &data, 
        &dataSize);
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot read DWORD from registry.", retCode };
    }

    return data;
}


inline ULONGLONG RegGetQword(HKEY hKey, const std::wstring& subKey, const std::wstring& value)
{
    //
    // Read a 64-bit QWORD value from the registry
    // 
    ULONGLONG data{};
    DWORD dataSize = sizeof(data);
    LONG retCode = ::RegGetValue(
        hKey,
        subKey.c_str(),
        value.c_str(),
        RRF_RT_REG_QWORD,
        nullptr,
        &data,
        &dataSize);
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot read QWORD from registry.", retCode };
    }

    return data;
}


inline std::wstring RegGetString(HKEY hKey, const std::wstring& subKey, const std::wstring& value)
{    
    //
    // Request the size of the string, in bytes
    //
    DWORD dataSize{};
    LONG retCode = ::RegGetValue(
        hKey,
        subKey.c_str(),
        value.c_str(),
        RRF_RT_REG_SZ,
        nullptr,
        nullptr,
        &dataSize);
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot read string from registry", retCode };
    }

    //
    // Allocate room for the result string.
    // 
    // Note that dataSize is in bytes, but wstring::resize() expects length in wchar_ts,
    // so must convert from bytes to wchar_t count first.
    // 
    std::wstring data;
    data.resize(dataSize / sizeof(wchar_t));

    //
    // Read the string from the registry into local wstring object
    //
    retCode = ::RegGetValue(
        hKey,
        subKey.c_str(),
        value.c_str(),
        RRF_RT_REG_SZ,
        nullptr,
        &data[0],
        &dataSize);
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot read string from registry", retCode };
    }

    //
    // On return, RegGetValue() writes in dataSize the actual size of the string, in bytes.
    // We must resize the wstring object to the proper string size.
    // Note that dataSize is expressed in bytes, and includes the terminating NUL; so we have to 
    // subtract the NUL from the total string length, as wstring objects are already NUL-terminated.
    //
    DWORD stringLengthInWchars = dataSize / sizeof(wchar_t);
    stringLengthInWchars--; // exclude the NUL written by the Win32 API
    data.resize(stringLengthInWchars);

    return data;
}


inline std::vector<std::wstring> 
RegGetMultiString(HKEY hKey, const std::wstring& subKey, const std::wstring& value)
{
    //
    // Request the size of the multi-string, in bytes
    //
    DWORD dataSize{};
    LONG retCode = ::RegGetValue(
        hKey,
        subKey.c_str(),
        value.c_str(),
        RRF_RT_REG_MULTI_SZ,
        nullptr,
        nullptr,
        &dataSize);
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot read multi-string from registry", retCode };
    }

    //
    // Allocate room for the result multi-string.
    // 
    // Note that dataSize is in bytes, but our vector<wchar_t> object has size expressed in wchar_ts.
    // 
    std::vector<wchar_t> data;
    data.resize(dataSize / sizeof(wchar_t));

    //
    // Read the multi-string from the registry into the vector object
    //
    retCode = ::RegGetValue(
        hKey,
        subKey.c_str(),
        value.c_str(),
        RRF_RT_REG_MULTI_SZ,
        nullptr,
        &data[0],
        &dataSize);
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot read multi-string from registry", retCode };
    }

    //
    // Resize vector to the actual size returned by GetRegValue().
    // Note that the vector is a vector of wchar_ts, instead the size returned by GetRegValue()
    // is in bytes, so we have to scale from bytes to wchar_t count.
    // 
    data.resize( dataSize / sizeof(wchar_t) );

    //
    // Parse the double-NUL-terminated string into a vector<wstring>
    //
    std::vector<std::wstring> result;
    const wchar_t* currStringPtr = &data[0];
    while (*currStringPtr != L'\0')
    {
        // Current string is NUL-terminated, so get its length with wcslen()
        const size_t currStringLength = wcslen(currStringPtr);

        // Add current string to result vector
        result.push_back(std::wstring{ currStringPtr, currStringLength });
        
        // Move to the next string
        currStringPtr += currStringLength + 1;
    }

    return result;
}


inline std::vector<BYTE> RegGetBinary(HKEY hKey, const std::wstring& subKey, const std::wstring& value)
{
    //
    // Request the size of the binary data, in bytes
    //
    DWORD dataSize{};
    LONG retCode = ::RegGetValue(
        hKey,
        subKey.c_str(),
        value.c_str(),
        RRF_RT_REG_BINARY,
        nullptr,
        nullptr,
        &dataSize);
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot read binary data from registry", retCode };
    }

    //
    // Allocate room for the result binary data
    // 
    std::vector<BYTE> data;
    data.resize(dataSize);

    //
    // Read the binary data from the registry into the vector object
    //
    retCode = ::RegGetValue(
        hKey,
        subKey.c_str(),
        value.c_str(),
        RRF_RT_REG_BINARY,
        nullptr,
        &data[0],
        &dataSize);
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot read binary data from registry", retCode };
    }

    //
    // Resize vector to the actual size returned by GetRegValue().
    // 
    data.resize(dataSize);


    return data;
}


inline std::vector<std::pair<std::wstring, DWORD>> RegEnumValues(HKEY hKey)
{
    //
    // Get useful pre-enumeration info, like the total number of values
    // and the maximum length of the value names
    //
    DWORD valueCount{};
    DWORD maxValueNameLen{};
    LONG retCode = ::RegQueryInfoKey(
        hKey,
        nullptr,    // no user-defined class
        nullptr,    // no user-defined class size
        nullptr,    // reserved
        nullptr,    // no subkey count
        nullptr,    // no subkey max length
        nullptr,    // no subkey class length
        &valueCount,
        &maxValueNameLen,
        nullptr,    // no max value length
        nullptr,    // no security descriptor
        nullptr     // no last write time
    );
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "RegQueryInfoKey failed while preparing for value enumeration.", retCode };
    }

    //
    // NOTE: According to the MSDN documentation, the size returned for value name max length
    // does *not* include the terminating NUL, so let's add +1 to take it into account
    // when I allocate the buffer for reading value names.
    //
    maxValueNameLen++;

    //
    // Preallocate a buffer for the value names
    //
    auto nameBuffer = std::make_unique<wchar_t[]>(maxValueNameLen);

    //
    // The value names and types will be stored here
    //
    std::vector<std::pair<std::wstring, DWORD>> valueInfo;

    //
    // Enumerate all the values
    //
    for (DWORD index = 0; index < valueCount; index++)
    {
        //
        // Get the name and the type of the current value
        //
        DWORD valueNameLen = maxValueNameLen;
        DWORD valueType{};
        retCode = ::RegEnumValue(
            hKey,
            index,
            nameBuffer.get(),
            &valueNameLen,
            nullptr,    // reserved
            &valueType,
            nullptr,    // no data
            nullptr     // no data size
        );
        if (retCode != ERROR_SUCCESS)
        {
            throw RegistryError{ "Cannot enumerate values: RegEnumValue failed.", retCode };
        }

        //
        // On success, the RegEnumValue API writes the length of the
        // value name in the valueNameLen output parameter 
        // (not including the terminating NUL).
        // So we can build a wstring based on that length.
        // 
        // The value info pair is made by the value's name and the value's type.
        //
        valueInfo.push_back(std::make_pair(
            std::wstring{ nameBuffer.get(), valueNameLen }, 
            valueType
        ));
    }

    return valueInfo;
}


inline std::vector<std::wstring> RegEnumSubKeys(HKEY hKey)
{
    //
    // Get some useful pre-enumeration info, like the total number of subkeys
    // and the maximum length of the subkey names
    //
    DWORD subKeyCount{};
    DWORD maxSubKeyNameLen{};
    LONG retCode = ::RegQueryInfoKey(
        hKey,
        nullptr,    // no user-defined class
        nullptr,    // no user-defined class size
        nullptr,    // reserved
        &subKeyCount,
        &maxSubKeyNameLen,
        nullptr,    // no subkey class length
        nullptr,    // no value count
        nullptr,    // no value name max length
        nullptr,    // no max value length
        nullptr,    // no security descriptor
        nullptr     // no last write time
    );
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "RegQueryInfoKey failed while preparing for subkey enumeration.", retCode };
    }

    //
    // NOTE: According to the MSDN documentation, the size returned for subkey name max length
    // does *not* include the terminating NUL, so let's add +1 to take it into account
    // when I allocate the buffer for reading subkey names.
    //
    maxSubKeyNameLen++;

    //
    // Preallocate a buffer for the subkey names
    //
    auto nameBuffer = std::make_unique<wchar_t[]>(maxSubKeyNameLen);

    //
    // The subkey names will be stored here
    //
    std::vector<std::wstring> subkeyNames;

    //
    // Enumerate all the subkeys
    //
    for (DWORD index = 0; index < subKeyCount; index++)
    {
        //
        // Get the name of the current subkey
        //
        DWORD subKeyNameLen = maxSubKeyNameLen;
        retCode = ::RegEnumKeyEx(
            hKey,
            index,
            nameBuffer.get(),
            &subKeyNameLen,
            nullptr, // reserved
            nullptr, // no class
            nullptr, // no class
            nullptr  // no last write time
        );
        if (retCode != ERROR_SUCCESS)
        {
            throw RegistryError{ "Cannot enumerate subkeys: RegEnumKeyEx failed.", retCode };
        }

        //
        // On success, the ::RegEnumKeyEx API writes the length of the
        // subkey name in the subKeyNameLen output parameter 
        // (not including the terminating NUL).
        // So I can build a wstring based on that length.
        //
        subkeyNames.push_back(std::wstring{ nameBuffer.get(), subKeyNameLen });
    }

    return subkeyNames;
}


inline RegKey RegCreateKey(
    HKEY hKeyParent,
    const std::wstring& keyName,
    LPTSTR keyClass,
    DWORD options,
    REGSAM access,
    SECURITY_ATTRIBUTES* securityAttributes,
    DWORD* disposition
)
{
    //
    // Create the key invoking the native RegCreateKeyEx Win32 API
    // 
    HKEY hKey = nullptr;
    LONG retCode = ::RegCreateKeyEx(
        hKeyParent,
        keyName.c_str(),
        0,          // reserved
        keyClass,
        options,
        access,
        securityAttributes,
        &hKey,
        disposition
    );
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot create registry key: RegCreateKeyEx failed.", retCode };
    }

    //
    // Return the raw key handle safely wrapped in the C++ key resource manager
    // 
    return RegKey{ hKey };
}


inline RegKey RegOpenKey(
    HKEY hKeyParent,
    const std::wstring& keyName,
    REGSAM access
)
{
    //
    // Open the key invoking the native RegOpenKeyEx Win32 API
    // 
    HKEY hKey = nullptr;
    LONG retCode = ::RegOpenKeyEx(
        hKeyParent,
        keyName.c_str(),
        0, // default options
        access,
        &hKey
    );
    if (retCode != ERROR_SUCCESS)
    {
        throw RegistryError{ "Cannot open registry key: RegOpenKeyEx failed.", retCode };
    }

    //
    // Return the raw key handle safely wrapped in the C++ key resource manager
    // 
    return RegKey{ hKey };
}


} // namespace win32
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值