Search code examples
c++visual-studiocharacter-encodingregistry

Read registry and output to console with C++


As a C++ beginner I need your help!

I'm currently working on a program that should be able to read a registry key and its value and write it into a vector array for further processing.

But now I have the problem that umlauts are displayed cryptically in the output on the console. I suspect this is due to the fact that a wrong character encoding is used. After some research I found the function "SetConsoleOutputCP(65001)", which is supposed to set the output of the console to UTF8.

Unfortunately, the output now breaks on umlauts if the Unicode compatible functions were used to read the registry. In the second case the console simply hides the umlauts when using the ANSI compatible functions. What am I doing wrong here and how do I get umlauts correctly read from the registry and output to the console?

Also I would like to write the umlauts correctly into a CSV file later. Do I have to pay attention to anything special here as well?

You can find my code below with some comments. Thanks to all helpers!

#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
    
/**
 * @brief Function to read an registry key to a vector of strings. Registry function will be called as unicode compatible version e.g. RegQueryValueExW (W at the end). This function is overloaded and exists with a different parameter list as well.
 * 
 * @param rootKey Standard hkey for the root key value
 * @param subKey Specific path for a subkey value
 * @param value The value name whose value is to be read
 * @return std::vector<std::wstring> 
 */
std::vector<std::wstring> regMultiSzToVector(HKEY rootKey, LPCWSTR subKey, LPCWSTR value) {
    HKEY hkey;
    DWORD type, size;
    std::vector<std::wstring> target;

    // Open registry key and write it to hkey variable
    if (RegOpenKeyEx(rootKey, subKey, 0, KEY_READ | KEY_WOW64_64KEY, &hkey) != ERROR_SUCCESS)
        exit(1);
    
    // Get type and necessary memory size
    if (RegQueryValueExW(hkey, value, NULL, &type, NULL, &size) != ERROR_SUCCESS)
        exit(1);

    if (type == REG_MULTI_SZ) {
        std::vector<wchar_t> temp(size / sizeof(wchar_t));

        if (RegQueryValueExW(hkey, value, NULL, NULL, reinterpret_cast<LPBYTE>(&temp[0]), &size) != ERROR_SUCCESS)
            exit(1);

        // Writing value from registry key to string array
        size_t index = 0;
        size_t len = wcslen(&temp[0]);
        while (len > 0) {
            target.push_back(&temp[index]);
            index += len + 1;
            len = wcslen(&temp[index]);
        }
    }
    // Closing registry key (see API)
    RegCloseKey(hkey);
    return target;
}

/**
 * @brief Function to read an registry key to a vector of strings. Registry function will be called as ansi compatible version e.g. RegQueryValueExA (A at the end). This function is overloaded and exists with a different parameter list as well.
 * 
 * @param rootKey Standard hkey for the root key value
 * @param subKey Specific path for a subkey value
 * @param value The value name whose value is to be read
 * @return std::vector<std::string> 
 */
std::vector<std::string> regMultiSzToVector(HKEY rootKey, LPCSTR subKey, LPCSTR value) {
    HKEY hkey;
    DWORD type, size;
    std::vector<std::string> target;

    // Open registry key and write it to hkey variable
    if (RegOpenKeyExA(rootKey, subKey, 0, KEY_READ | KEY_WOW64_64KEY, &hkey) != ERROR_SUCCESS)
        exit(1);

    // Get type and necessary memory size
    if (RegQueryValueExA(hkey, value, NULL, &type, NULL, &size) != ERROR_SUCCESS)
        exit(1);

    if (type == REG_MULTI_SZ) {
        std::vector<char> temp(size);

        if (RegQueryValueExA(hkey, value, NULL, NULL, reinterpret_cast<LPBYTE>(&temp[0]), &size) != ERROR_SUCCESS)
            exit(1);
        
        // Writing value from registry key to string array
        size_t index = 0;
        size_t len = strlen(&temp[0]);
        while (len > 0) {
            target.push_back(&temp[index]);
            index += len + 1;
            len = strlen(&temp[index]);
        }
    }
    // Closing registry key (see API)
    RegCloseKey(hkey);
    return target;
}

int main(int argc, char const* argv[]) {
    
    // Saving function return values to vectors
    std::vector<std::wstring> result1 = regMultiSzToVector(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\007", L"Counter");
    std::vector<std::string> result2 = regMultiSzToVector(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\007", "Counter");
    
    // Printing String values from first and second vector
    printf("Vector 1: \n");
    for (auto &e : result1) {
        std::wcout << e << std::endl;
    }

    printf("\n\n\n Vector 2: \n");
    for (auto &e : result2) {
        std::cout << e << std::endl;
    }
    
    return 0;
}

Solution

  • If you want to use CP65001 (UTF-8) then you also need to convert your utf-16 encoded wstring to a utf-8 encoded string.

    Here is a simple way of doing this: https://stackoverflow.com/a/12903901/347508

    I've made only small modifications to your code:

    • change RegOpenKeyEx -> RegOpenKeyExW
    • remove the non-unicode portion of your code
    • print the result as raw wstring, and as utf-8 encoded string to demo the difference

    #include <iostream>
    #include <string>
    #include <vector>
    #include <windows.h>
    #include <codecvt>
    
    #pragma comment(lib, "user32")
    #pragma comment(lib, "Advapi32")
    
    // utf-8 conversion from https://stackoverflow.com/a/12903901/347508
    // convert UTF-8 string to wstring
    std::wstring utf8_to_wstring (const std::string& str)
    {
        std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
        return myconv.from_bytes(str);
    }
    
    // convert wstring to UTF-8 string
    std::string wstring_to_utf8 (const std::wstring& str)
    {
        std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
        return myconv.to_bytes(str);
    }
    
    /**
     * @brief Function to read an registry key to a vector of strings. Registry function will be called as unicode compatible version e.g. RegQueryValueExW (W at the end). This function is overloaded and exists with a different parameter list as well.
     * 
     * @param rootKey Standard hkey for the root key value
     * @param subKey Specific path for a subkey value
     * @param value The value name whose value is to be read
     * @return std::vector<std::wstring> 
     */
    std::vector<std::wstring> regMultiSzToVector(HKEY rootKey, LPCWSTR subKey, LPCWSTR value) {
        HKEY hkey;
        DWORD type, size;
        std::vector<std::wstring> target;
    
        // Open registry key and write it to hkey variable
        if (RegOpenKeyExW(rootKey, subKey, 0, KEY_READ | KEY_WOW64_64KEY, &hkey) != ERROR_SUCCESS)
            exit(1);
        
        // Get type and necessary memory size
        if (RegQueryValueExW(hkey, value, NULL, &type, NULL, &size) != ERROR_SUCCESS)
            exit(1);
    
        if (type == REG_MULTI_SZ) {
            std::vector<wchar_t> temp(size / sizeof(wchar_t));
    
            if (RegQueryValueExW(hkey, value, NULL, NULL, reinterpret_cast<LPBYTE>(&temp[0]), &size) != ERROR_SUCCESS)
                exit(1);
    
            // Writing value from registry key to string array
            size_t index = 0;
            size_t len = wcslen(&temp[0]);
            while (len > 0) {
                target.push_back(&temp[index]);
                index += len + 1;
                len = wcslen(&temp[index]);
            }
        }
        // Closing registry key (see API)
        RegCloseKey(hkey);
        return target;
    }
    
    
    int main(int argc, char const* argv[]) {
        
        // Saving function return values to vectors
        std::vector<std::wstring> res = regMultiSzToVector(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", L"hansi");
        
        // Printing String values from first and second vector
        printf("Result: \n");
        for (auto &e : res) {
            std::wcout << "* utf-16=" << e << std::endl;
            std::cout  << "  utf-8 =" << wstring_to_utf8(e) << std::endl;
        }
    
        
        return 0;
    }
    

    My registry key for testing

    Output:

    C:\Users\Hansi>chcp 850
    Active code page: 850
    
    C:\Users\Hansi>cl /EHsc reg_test.cpp
    Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29910 for x64
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    reg_test.cpp
    Microsoft (R) Incremental Linker Version 14.28.29910.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /out:reg_test.exe
    reg_test.obj
    
    C:\Users\Hansi>reg_test.exe
    Result:
    * utf-16=Íl
      utf-8 =Öl
    * utf-16=H÷ren
      utf-8 =H├Âren
    * utf-16=T³re
      utf-8 =T├╝re
    * utf-16=Da▀
      utf-8 =Daß
    
    C:\Users\Hansi>chcp 65001
    Active code page: 65001
    
    C:\Users\Hansi>reg_test.exe
    Result:
    * utf-16=l
      utf-8 =Öl
    * utf-16=Hren
      utf-8 =Hören
    * utf-16=T  utf-8 =Türe
      utf-8 =Daß