-2

I want to display these registry key values:

  1. MSSQL12.MSSQLSERVER
  2. MSSQL15.SQLEXPRESS
  3. MSSQL11.TEW_SQLEXPRESS

Registry

Code:

if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        TEXT("SOFTWARE\\Microsoft\\Microsoft SQL Server\\Instance Names\\SQL"),
        0,
        KEY_READ | KEY_WOW64_64KEY,
        &hKey) == ERROR_SUCCESS){
    DWORD i, retCode, cchName, buflen;

    TCHAR    achKey[MAX_KEY_LENGTH];            // buffer for subkey name
    DWORD    cbName;                            // size of name string 
    TCHAR    achClass[MAX_PATH] = TEXT("");     // buffer for class name 
    DWORD    cchClassName = MAX_PATH;           // size of class string 
    DWORD    cSubKeys = 0;                      // number of subkeys 
    DWORD    cbMaxSubKey;                       // longest subkey size 
    DWORD    cchMaxClass;                       // longest class string 
    DWORD    cValues;                           // number of values for key 
    DWORD    cchMaxValue;                       // longest value name 
    DWORD    cbMaxValueData;                    // longest value data 
    DWORD    cbSecurityDescriptor;              // size of security descriptor 
    FILETIME ftLastWriteTime;                   // last write time 

    retCode = RegQueryInfoKey(
        hKey,                    // key handle 
        achClass,                // buffer for class name 
        &cchClassName,           // size of class string 
        NULL,                    // reserved 
        &cSubKeys,               // number of subkeys 
        &cbMaxSubKey,            // longest subkey size 
        &cchMaxClass,            // longest class string 
        &cValues,                // number of values for this key 
        &cchMaxValue,            // longest value name 
        &cbMaxValueData,         // longest value data 
        &cbSecurityDescriptor,   // security descriptor 
        &ftLastWriteTime);       // last write time 

   
    result = RegGetValue(
                hKey, NULL, L"MSSQLSERVER",                    
                RRF_RT_REG_SZ, 0, buf, &bufsz);
    if (result != ERROR_SUCCESS) {
                printf("Failed read value");
                _getch();
                return -1;
            }
    wprintf(L"%s\n", buf);
}

I need to replace L"MSSQLSERVER" with the variable keyName, but I don't understand how to do that. I'm trying to write the name of the key to a variable.

LPWSTR aResult;
LPSTR  keyName;
RegEnumKeyExA(hKey, i, keyName, &cchName, NULL, NULL, NULL, NULL);
MultiByteToWideChar(0, 0, keyName, -1, aResult, 0);
result = RegGetValue(
            hKey, NULL, aResult,                    
            RRF_RT_REG_SZ, 0, buf, &bufsz);

But I think it's wrong. Here keyName is NULL. And keyName is LPSTR, but RegGetValue() needs LPCWSTR.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Alexandr
  • 243
  • 2
  • 3
  • 15
  • 2
    `RegGetValueW` needs an `LPCWSTR` while `RegGetValueA` needs an `LPCSTR`. What `RegGetValue` requires depends on if you've declared `UNICODE` or not so use `LPTSTR`. Why is `keyName` an `LPSTR` instead of an `LPTSTR`? If you are not explicitly using the `W` or `A` functions, you should probably not be explicit when declaring the variables either, but use the `T` types. – Ted Lyngmo Aug 17 '22 at 10:04
  • `aResult` and `keyName` are uninitialized pointers. You cannot hope for any particular outcome, the behavior is undefined. At any rate, see [Working with Strings](https://learn.microsoft.com/en-us/windows/win32/learnwin32/working-with-strings) and [Unicode in the Windows API](https://learn.microsoft.com/en-us/windows/win32/intl/unicode-in-the-windows-api). – IInspectable Aug 17 '22 at 10:09
  • _"declared `UNICODE`"_ should have been _defined `UNICODE`_ in my comment above. – Ted Lyngmo Aug 17 '22 at 10:11
  • The error message from the compiler should be pretty clear, it's a shame you did not include it. – Anders Aug 17 '22 at 11:13
  • 1
    Have a look at [Enumerating Registry Subkeys](https://learn.microsoft.com/en-us/windows/win32/sysinfo/enumerating-registry-subkeys) which enumerates all Subkeys and Values as `TCHAR`. – YangXiaoPo-MSFT Aug 18 '22 at 01:53

2 Answers2

0

It turned out that my task needed names, not values. And only SQL Express. Here is the code:

if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\Instance Names\\SQL",
        0,
        KEY_READ | KEY_WOW64_64KEY,
        &hTestKey) == ERROR_SUCCESS
        )
while (RegEnumValueW(hTestKey, index, (wchar_t*)valueName.c_str(), &lppchValueName, 0, 0, 0, 0) == ERROR_SUCCESS) 
        {
            lppchValueName = MAX_PATH;
            buffSize = MAX_PATH;
            if (RegQueryValueExW(hTestKey, valueName.c_str(), 0, 0, (LPBYTE)value.c_str(), &buffSize) == ERROR_SUCCESS)
            {
                if (value.find(L"Express") != std::wstring::npos || value.find(L"EXPRESS") != std::wstring::npos || value.find(L"express") != std::wstring::npos)
                {
                    // Here is an array entry.
                }
            }
            index++;
            memset((void*)valueName.c_str(), 0, MAX_PATH);
            memset((void*)value.c_str(), 0, MAX_PATH);
        }
Alexandr
  • 243
  • 2
  • 3
  • 15
-1

Looks like have to use unicode, pay attention on L"...." and LPWSTR

    LPWSTR  keyName = L"MSSQL12.MSSQLSERVER";
    result = RegGetValue(
                hKey, NULL, keyName ,                    
                RRF_RT_REG_SZ, 0, buf, &bufsz);

enter image description here

armagedescu
  • 1,758
  • 2
  • 20
  • 31
  • This is passing a wide-character string into a generic-text mapping macro, which just changes the specific compiler error, but doesn't actually solve the issue. You should be calling `RegGetValueW` instead. And `keyName` can be `const`, e.g. `auto const keyName = L"...";`. – IInspectable Aug 17 '22 at 10:47
  • @IInspectable the point is to pay attention on unicode and always be aware of how to use it – armagedescu Aug 17 '22 at 10:50
  • That's understood. However, publishing code that demonstrates how **not** to be aware of the character encoding doesn't make for a convincing argument. – IInspectable Aug 17 '22 at 10:55
  • @IInspectable the key is author is using unicode, and tries to bridge it to char. That's what he must not do. Use ony unicode, and that's it. – armagedescu Aug 17 '22 at 10:58
  • @IInspectable RegGetValue by default defaults to RegGetValueW, unless unicode is explicitly turned off. That's why author plays with MultibyteToWideChar and so on. – armagedescu Aug 17 '22 at 11:02
  • *"RegGetValue by default defaults to RegGetValueW"* - Uh, no, it doesn't. It translates to whatever the user requested. If the `MBCS` preprocessor symbol is defined, it'll expand to `RegGetValueA`, coming full circle to the exact problem the OP was trying to resolve. If you insist on using the generic-text mapping, then you have to go all the way. The character type compatible with `RegGetValue` is a `TCHAR`. That is arcane knowledge only relevant to those who actually support Win9x. Everyone else just uses `WCHAR` and `RegGetValueW`, and you should, too. – IInspectable Aug 17 '22 at 11:20
  • @IInspectable ```....whatever the user requested```, it means if user not requested it defaults to something. And it defaults to RegGetValueW. Yes it does, really. And it defaults because of ```UNICODE``` preprocessor symbol defined by default. See scerenshot. – armagedescu Aug 17 '22 at 11:40
  • *"Configuration Properties"* are choices **you** make. If **you** don't make those decisions, then no-one does it for you. And if you look carefully at the *winreg.h* file snippet you posted, when you decide not to define the `UNICODE` preprocessor symbol, then `RegGetValue` defaults to `RegGetValueA`. I wonder what point you are trying to make here. You could just as well fix the code you posted and move on. – IInspectable Aug 17 '22 at 22:08
  • @IInspectable what I shown is the default setting, when no decision made. That is. – armagedescu Aug 18 '22 at 10:58
  • No, it is not. It is (presumably) some generated MSBuild configuration, created by an arbitrary version of Visual Studio. Other versions of Visual Studio or other IDE's (like Code::Blocks) will make different suggestions. If you still don't grok why the code is incorrect, read this: [It’s okay to be contrary, but you need to be consistently contrary: Going against the ambient character set](https://devblogs.microsoft.com/oldnewthing/20211210-00/?p=106021). – IInspectable Aug 18 '22 at 11:09
  • @IInspectable I only consider VisualStudio. It is the only IDE good for windows. Codeblocks is maybe little bit better than a text editor with syntax highlighting, but be serious, is just not worth mentioning. – armagedescu Aug 18 '22 at 11:25
  • Fine, CMake then. There, no defaults. Why are you continuing to defend incorrect code with statements of dubious merit? – IInspectable Aug 18 '22 at 11:32
  • @IInspectable VisualStudio is the standard and the only choice, everything else is an exotic choice. When comparing exotic choices between them, yeah, CMake is better. – armagedescu Aug 18 '22 at 11:42
  • Visual Studio supports CMake. Are you suggesting that CMake builds were an exotic choice? I mean, more exotic than MSBuild-based projects (which are, let's be honest, squarely focused on .NET). Regardless, none of that explains why you are defending incorrect code. Are you done reading the article yet? – IInspectable Aug 18 '22 at 11:45
  • @IInspectable Yes, VisualStudio is a good choice for CMake programmers, as is a gooid choice for a wide variety of compilers and platforms. A gift from Microsoft to community. But that doesn't make all exotic tools a standard. About the link it is mostly about nothing. Another user trapping himself in exotic misusage. – armagedescu Aug 18 '22 at 12:31
  • @IInspectable there is one more link https://stackoverflow.com/questions/1319461/how-do-i-turn-off-unicode-in-a-vc-project – armagedescu Aug 18 '22 at 12:33
  • There are exactly 3 valid combinations of API names and character types: `1` The Unicode version of the API (`W` suffix) and `WCHAR`, `2` the ANSI version (`A` suffix) and CHAR, and `3` the generic-text mapping (no suffix) and `TCHAR`. Every other combination is wrong (such as in your code), and there is at least one valid combination of the respective preprocessor symbols where your code fails to compile or fails at runtime. – IInspectable Aug 18 '22 at 15:23
  • @IInspectable when failed discussion usually comes to that, showing all your knowledge with little conclusion extracted out of finger, making everything a nonsense. Be aware, author uses standard VisualStudio with standard default environment defaulting to UNICODE. Is easily deductible from each piece of his code. Not for you of course. – armagedescu Aug 21 '22 at 11:59
  • It takes adding literally **a single character** to fix the code, and make this proposed answer universally applicable. Instead, you choose to constrain applicability to build environments that happen to meet your assumptions, and introduce a hidden dependency between the code and some alleged *"default"*. Let me ask you: Why **must** you over-constrain applicability? – IInspectable Aug 21 '22 at 12:37
  • @IInspectable it makes pure nonsense. It is long ago since whole windows api is unicode. The ansi functions exists for backward compatibility only, and there will be never ever a need to do it ansi. The FuncName***A*** variants are basically wrapper functions that convert strings from ansi to wide char representation and then call the corresponding FuncName***W***. And yes, it is solved by a single character ***L***"...", so no need for MultiByteToWideChar as did the author and so on. – armagedescu Aug 21 '22 at 20:14
  • So... if it makes no sense to target the ANSI API, why does your code use a macro whose sole purpose is to target both the Unicode **and** ANSI API? Why does your code not use the actual function name? Why are you obsessed with publishing code that *"works on my machine"* rather than code that works on *any* machine, including yours? – IInspectable Aug 22 '22 at 07:27
  • @IInspectable you are now having nothing else to say than to manipulative things. So, using ***UNICODE*** and not care whether to put a W or not at the end, is the same. The key is do not have to care. Only if you out of blue fill the urge to slow down your application, port it to ansi. About "your machine", that's the code that works on authors machine. And on any standard windows development environment btw – armagedescu Aug 22 '22 at 10:11
  • *"So, hey, OP here. I had to replace `/D "UNICODE"` with `/D "MBCS"` so that compiles. And now your code doesn't compile anymore. What can I do?"* – IInspectable Aug 22 '22 at 10:36
  • @IInspectable If you are doing new development, you should use Unicode for all text strings except perhaps system strings that are not seen by end users. MBCS is a legacy technology and is not recommended for new development: https://learn.microsoft.com/en-us/cpp/text/support-for-multibyte-character-sets-mbcss?view=msvc-170 So, OP is doing new development. Under UNICODE the user will never ever have to replace with MBCS. All WinAPI programs developed by someone are already ported to UNICODE. There is no need to care about mbcs and ansi. – armagedescu Aug 22 '22 at 11:22
  • *"There is no need to care about mbcs and ansi."* - Indeed. So ***why*** does ***your*** code than? Why do you subscribe to a specific character type, but then stubbornly refuse to be equally explicit about the function you call? Anyway, enough time wasted on someone that cannot tell the difference between code that's correct, and code that *sometimes* doesn't fail. – IInspectable Aug 22 '22 at 11:29
  • @IInspectable Feel the difference. I do not insist. I just do not care. It is unicode and that's all. I don't intend to go back to write code for Win 3x. Only stupid thing is to replace the ***L""*** with ***TEXT("")*** and sticking to ansi somehow – armagedescu Aug 22 '22 at 11:36