Thread: Reading REG_MULTI_SZ strings

  1. #1
    Registered User
    Join Date
    Dec 2008
    Posts
    19

    Reading REG_MULTI_SZ strings

    Hi
    I need some help on how to read data values from a REG_MULTI_SZ windows registry string. I understand that each data value is null terminated and there is a another terminator in the end. Thanks.

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    So exactly what do you want to know? You could do something like this:
    Code:
    int split_multi_strings(const char *regvalue, char *outputarr[], int maxstrings)
    {
         const char *ptr = regvalue;
         int count, len;
         for(;;)
         {
             len = strlen(ptr);
             if (len == 0 || count == maxstrings-1)
                 break;
             outputarr[count] = ptr;
             count++;
             ptr += len;
          }
          // len != 0 means we didn't get to the end of regvalue, so 
          // we need more space for strings.  
          if (len != 0)
             return -count;
          return count; 
    }
    That's of course, only one variant of solution.

    Note: I haven't compiled and/or tested the above code, so there may be both syntactical and logical errors in it. It's your task to validate it.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Dec 2008
    Posts
    19
    Thanks for the quick reply
    I have something like this, my registry value is AAA and its data values are XXX BBB
    so it the registry it is like
    AAA REG_MULTI_SZ XXX BBB
    I believe when I run a query using RegQueryValueEx, in memory the above is stored as
    xxx\0BBB\0\0.

    My basically want to remove BBB from this REG_MULTI_SZ string and write XXX back into teh registry

  4. #4
    Registered User
    Join Date
    Dec 2008
    Posts
    19
    Couple of questions on the above snippet. What does
    "for(; " mean, Also whats should i pass in for maxstrings? Thanks.

  5. #5
    Registered User
    Join Date
    Dec 2008
    Posts
    19
    I meant for(;

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Yes, that's how it is described on the MS web-site at least, so either you, I and the MS website are right, or that's not how it's stored! My bet is that we're right!

    So, to write it back again, you need to do the opposite of the splitting function I just posted, build a string from the components, and finish it with an extra zero. Actually, I think the code I posted needs a "ptr += len+1" instead of "ptr += len".

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #7
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    for(; ; ) is an infinite loop - same as while(1).

    You can disable smilies on the post, but I know what you meant.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #8
    Registered User
    Join Date
    Dec 2008
    Posts
    19
    what does the above "for" statement mean, also what is maxstrings? Thanks buddy!

  9. #9
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I've already explained the for-statement (probably just crossing you posting while I was typing). Maxstrings is the number of elements in your outputarray. Note that we're just copying the location of the string inside the original buffer, so you may need to use a different buffer to overwrite the original data. [Although if you just want to remove the LAST entry, you can just put a zero in the first character of that string].

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  10. #10
    Registered User
    Join Date
    Dec 2008
    Posts
    19
    Yes it was a cross posting. Sorry, But still do not understand the maxstrings part. In the below
    lpData is the data value and lpcbData is the size of the buffer. We do not know the number of actual string or slots until we parse it correct, so how can I pass maxstrings to the function. Can you pls. explain a bit more? I am a newbie, pls. bear with me.

    LONG WINAPI RegQueryValueEx(
    __in HKEY hKey,
    __in_opt LPCTSTR lpValueName,
    __reserved LPDWORD lpReserved,
    __out_opt LPDWORD lpType,
    __out_opt LPBYTE lpData,
    __inout_opt LPDWORD lpcbData
    );

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    No, it's not the actual number of strings - it is the number of elements in your array to recieve the split-out values.
    So if we have "string1\0string2\0string3\0final strings\0\0" in the registry, then we could do this:

    Code:
    #define MAXSTRINGS 20
    int main()
    {
        char buffer[1000];
        DWORD size = sizeof(buffer);
        char *strings[MAXSTRINGS];
        LONG res; 
        int count; 
        int i;
    ... 
    // Code that opens the registry, etc. 
    ...     
        res = RegQueryValueEx(hKey, L"ValueName",0, &type, buffer, &size);
        if (res > 0)
        {
           count = split_multi_strings(buffer, strings, MAXSTRINGS); 
           if (count < 0) 
           {
              printf("Too many strings in registry, need more space: %d is not enough\n"
                "displaying what we got so far\n", MAXSTRINGS);
              count = -count; 
           }
           for(i = 0; i < count; i++)
           {
               printf("%03d: %s\n", i, strings[i]);
            }
        } else 
             // deal with errors. 
        ...
    }
    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Registered User
    Join Date
    Dec 2008
    Posts
    19
    Thanks mat, I will try it out.

  13. #13
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Code:
    #pragma comment ( lib, "advapi32.lib" ) 
    #include <windows.h>
    #include <stdio.h>
    #include <tchar.h>
    
    int main(void)
    {
        HKEY hKey = NULL;
        DWORD dwReturn = ERROR_SUCCESS;
        dwReturn = RegOpenKeyEx( 
            HKEY_LOCAL_MACHINE,
            TEXT("System\\CurrentControlSet\\Services\\DHCP"), 
            0,                 
            KEY_ALL_ACCESS,    
            &hKey              
            );
        if( dwReturn == ERROR_SUCCESS ) {
            DWORD dwSize;
            dwReturn = RegQueryValueEx(
                hKey,           
                TEXT("DependOnService"),
                0,             
                0,              
                0,              
                &dwSize         
                );
    
            if( dwReturn == ERROR_SUCCESS ) {
                DWORD dwType;
                LPBYTE lpBuffer = LPBYTE(GlobalAlloc(GPTR, dwSize));
                if( lpBuffer == NULL )
                {
                    printf("GlobalAlloc failed (%d)\n", GetLastError());
                    RegCloseKey(hKey);
                    return  -1;
                }
                dwReturn = RegQueryValueEx(
                    hKey,           
                    TEXT("DependOnService"),
                    0,              
                    &dwType,        
                    lpBuffer,      
                    &dwSize        
                    );
                if( dwReturn == ERROR_SUCCESS ) {
                    register LPTSTR p = LPTSTR(lpBuffer);
                    for(; *p; p += _tcslen(p) + 1) 
                        printf("%s\n",p);
                }
                GlobalFree(HGLOBAL(lpBuffer));
            }
            RegCloseKey(hKey);
        }
        return 0;
    }

  14. #14
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Quote Originally Posted by BobS0327 View Post
    Code:
                dwReturn = RegQueryValueEx(
                    hKey,           
                    TEXT("DependOnService"),
                    0,              
                    &dwType,        
                    lpBuffer,      
                    &dwSize        
                    );
                if( dwReturn == ERROR_SUCCESS ) {
                    register LPTSTR p = LPTSTR(lpBuffer);
                    for(; *p; p += _tcslen(p) + 1) 
                        printf("&#37;s\n",p);
                }
    This isn't reliable, there's no guarantee the returned data is null terminated so you're potentially off into the weeds and dead faster than you can say Steve Irwin. If you don't want to check for proper termination yourself use RegGetValue if you can target Vista, or SHQueryValueEx elsewhere. Another alternative if you're only querying one value is to use SHRegGetUSValue or SHGetValueand forget about all the opening and closing malarky.
    Last edited by adeyblue; 12-04-2008 at 09:47 PM.

  15. #15
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Quote Originally Posted by adeyblue View Post
    This isn't reliable, there's no guarantee the returned data is null terminated so you're potentially off into the weeds and dead faster than you can say Steve Irwin. If you don't want to check for proper termination yourself use RegGetValue if you can target Vista, or SHQueryValueEx elsewhere. Another alternative if you're only querying one value is to use SHRegGetUSValue or SHGetValueand forget about all the opening and closing malarky.

    I'm not sure I understand what you mean by there's no guarantee that a REG_MULTI_SZ will be null terminated. This MSDN link indicates that a REG_MULTI_SZ is null terminated.

    EDIT On second thought, I can see how that can possibly happen. For example, an incompetent programmer badly forming a REG_MULTI_SZ string and then writing it out to the key.

    EDIT1 So, if you want to code defensively against incompetency in the above example, just increase dwSize by one.
    Last edited by BobS0327; 12-04-2008 at 10:41 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. reading data from strings
    By Winston Hong in forum C Programming
    Replies: 11
    Last Post: 05-28-2008, 08:56 PM
  2. Replies: 2
    Last Post: 01-28-2008, 03:07 AM
  3. reading from a file + list of strings
    By stewie1986 in forum C Programming
    Replies: 2
    Last Post: 12-06-2007, 11:59 PM
  4. Reading strings input by the user...
    By Cmuppet in forum C Programming
    Replies: 13
    Last Post: 07-21-2004, 06:37 AM
  5. question about reading in strings from a file :>
    By bball887 in forum C Programming
    Replies: 8
    Last Post: 04-13-2004, 06:24 PM