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.
Printable View
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.
So exactly what do you want to know? You could do something like this:
That's of course, only one variant of solution.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;
}
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
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
Couple of questions on the above snippet. What does
"for(;;) " mean, Also whats should i pass in for maxstrings? Thanks.
I meant for(;;)
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
for(; ; ) is an infinite loop - same as while(1).
You can disable smilies on the post, but I know what you meant.
--
Mats
what does the above "for" statement mean, also what is maxstrings? Thanks buddy!
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
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
);
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
Thanks mat, I will try it out.
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;
}
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.
That's the definition yeah, but RegSetValueEx will (or at least, sometime in its' past did) happily write non-null terminated string data. Leading to:
A more thorough treatment of the problem can be found in the comment section hereQuote:
Originally Posted by http://msdn.microsoft.com/en-us/library/ms724911.aspx - RegQueryValueEx
I agree, it's something a solid application should check. However, if we assume that the user of the data is an application that is well-behaved, then I expect that the value stored would be correctly formed.
Checking for proper termination is not that hard:
Code:int checktermination(char *buffer, size_t bufsize, int nzeros)
{
size_t i;
int j;
for(i = bufsize - 1; i; i--)
{
if (!buffer[i])
{
for(j = nzeros-1; j && i >= j; j--)
{
if (buffer[i-j])
break;
}
if (!j)
{
return 1; // Success.
}
}
}
return 0;
}
Incorrectly using RegSetValueEx will definitley cause issues. Strictly adhering to MS's guidelines on its use will definitely keep a programmer out of trouble.
I'd have to question the validity of the info on that posted link. Here's a link to a reputable resource. This resource states in section 8.2.3.2:
None of the many registry resources I've checked support the claims on the website.Quote:
8.2.3.2. Getting and setting values
Let's start with something familiar: SHQueryValueEx is a dead ringer for RegQueryValueEx. It takes the same parameters and has the same restrictions and conditions, so it's pretty much a drop-in replacement.
Not sure that strlen() will work as expected in this case.
I think strlen() will return the number of chars until the FIRST terminator.
ie strlen("xxx/0yyy/0/0") == 3
The returned value lpcbData should be used as the size of the string, including terminators.
After the return from RegQueryValueEx has been checked for errors a terminator should be added at the lpcbData element of the string.
This is not tested code, trust it as much as any code written early Sunday morning......
Code:#define NUM_REG_STR 2//for ease I am assuming we have two values in the data
//open reg key (hKey)
//fill in subkey string (szSubKey)
iRet=RegQueryValueEx (hKey, szSubKey, 0, &dwType, szRegData, &dwSize);
if (iRet==ERROR_SUCCESS && dwSize && dwType==REG_MULTI_SZ)//got the right data
{
char *pTerm=NULL,*pStart=NULL;//pointers for string calcs
char szReturn[NUM_REG_STR][MAX_PATH]={0};
int iRemaining=dwSize;//buffer remaining
int iFound=0;//number of strings extracted
//add terminator to end of string
szRegData[dwSize]='\0';
while(iFound<NUM_REG_STR && iRemaining>0)
{
pStart=szRegData;//set the start of the string
//find the first terminator
pTerm=strchr(pStart,'\0');//find the first term
if(pTerm)//found one
{
//print in the part of the string we need
_snprintf(szReturn[iFound],pTerm-pStart,"%s",pStart);
//increment our counters
iFound++;
iRemaining-=pTerm-pStart;
//we only want to move past the terminators while we have more strings in the data
if(iFound<NUM_REG_STR)
{
//move past the terminator
while(*pTerm=='/0' && iRemaining>0)
{
pTerm++;
iRemaining--;
}
//uodate the start pointer
pStart=pTerm;
}
}
//EDIT
else break;//no more terminators, when we added one to the end???
}
}
Mea Culpa. Geoff's analysis is correct given the context (which is XP SP1 now that I've looked at it again), it was I who presumed the behaiour extended back to its creation. A quick ride on my time machine back to Win98 and NT4 and forwards to the present day reveals SHQueryValueEx guarantees correct NULL termination on any OS with any version of IE6 installed and by default from stock XP installs (shlwapi.dll version 6.0.2600.0). It's currently serving as nothing more than a wrapper of SHRegGetValue which is explicit about its string handling behaviour.
Edit: Ever have that feeling when you work something out and are confident of the result only for something to shatter that confidence moments later?
Empirical evidence suggests the preceeding is true for REG_SZ only. IE6 on 2k server SP4 won't properly terminate malformed REG_MULTI_SZ (XP SP3 does) which means that I'm an idiot and kicked up the fuss for nothing. Oh well, at least there was a potential buffer overflow so it hasn't been a complete waste.
Ok, I'm confused again. the website has posted this information concerning the early implementations of ShQueryValueEx
Note that it refers to Windows 98 SE and SHLWAPI build version 5.00. But Matt Pietrek in his article states that SHLWAPI did not come into existence until NT 5 which was alpha Windows 2000. All, the references I've checked and I've checked a lot of references, indicate that SHLWAPI requires Win 2000 SP1 or higher.Quote:
In the case where pvData is not NULL but pcbData is NULL, versions before 5.00 fault because the function dereferences a NULL pointer when learning the buffer’s size. The first builds of version 5.00, for Internet Explorer 5.0 and Windows 98 SE, do not fix this correctly. When the function calls RegQueryValueEx, the local variable it uses for passing the buffer’s size and learning the amount of available data is uninitialised. A more or less random amount of memory at the address given by pvData may be corrupted.
Also, using the kernel debugger indicates that both the RegQueryValueEx and ShQueryValueEx map directly to the kernel NtQueryValue call. Which leads to another question. Why in the world would MS want to call another userland function to eventually get to a kernel function? I just dunno.
I believe Shlwapi didn't become part of the base / vanilla OS install until Win 2000, but it was available with IE.
Dll and Shell Version numbers
Quote:
Originally Posted by Note 1(above link)
Since Wikipedia says that IE 4 was released in late 1997, that would put it's first release around the same time as the earliest 2k/5.0 betas. So it's plausible that shlwapi was in general use before Win2K went on general release.Quote:
Originally Posted by Note 2
Convenience? It's always expanded environment variables, so maybe someone got sick of making two calls and wrapped it up.Quote:
Why in the world would MS want to call another userland function to eventually get to a kernel function? I just dunno.
Anyhoo, I don't know the guy so I couldn't honestly care less how straight his facts are now that I've been in and dug around myself. We've probably dragged this thread on long enough with pointless chatter in respect to the OP and actual thread topic, so I'm happy to continue via PM if you're so inclined.
I find that the best way to deal with multi-strings (a null-terminated sequence of null-terminated strings) is to:
- replace all '\0' characters up to the length of the multistring with '\n' characters upon reading the string in
- perform any operations (such as strcat) that you so desire
- replace all '\n' characters in the string with '\0' characters upon writing the string back out
It's just sooo much easier to deal with, and if you think about it, that's what RegEdit does too!
Here's some proof of concept code that anyone can play with to determine how RegQueryValueEx reacts to any type of data written to the key. The code first lists all the strings from the key and then it appends a new string to the key. So, you can modify the RegSetValueEx call to write any type of crazy data to the key whether or not that data is null terminated. Take the really incompetent programmer approach and get really creative (crazy) with what you write to the key.
You'll have to create the registry key prior to running the code.
Code:// The following registry key must exist prior to running app
// HKEY_LOCAL_MACHINE\Software\TestKey\TestSubKey
// TestSubKey is a REG_MULTI_SZ key
#pragma comment ( lib, "advapi32.lib" )
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <time.h>
int main(void)
{
HKEY hKey = NULL;
DWORD dwReturn = ERROR_SUCCESS;
char szNewString[MAX_PATH] = {0};
srand(time(NULL));
_snprintf(szNewString,sizeof(szNewString), _T("NewString%d"),rand() % 100);
dwReturn = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("Software\\TestKey"),
0,
KEY_ALL_ACCESS,
&hKey
);
if( dwReturn == ERROR_SUCCESS )
{
DWORD dwSize;
dwReturn = RegQueryValueEx(
hKey,
TEXT("TestSubKey"),
0,
0,
0,
&dwSize
);
if( dwReturn == ERROR_SUCCESS )
{
DWORD dwType;
DWORD dwNewSize = dwSize + _tcslen(szNewString) ;
LPBYTE lpBuffer = LPBYTE(GlobalAlloc(GPTR, dwNewSize + 1));
if( lpBuffer == NULL )
{
_tprintf("GlobalAlloc failed (%d)\n", GetLastError());
RegCloseKey(hKey);
return -1;
}
dwReturn = RegQueryValueEx(
hKey,
TEXT("TestSubKey"),
0,
&dwType,
lpBuffer,
&dwSize
);
if( dwReturn == ERROR_SUCCESS )
{
LPTSTR p = LPTSTR(lpBuffer);
for(; *p; p += _tcslen(p) + 1)
_tprintf("%s\n",p);
if(!*p)
{
_tcscpy(p,szNewString );
dwReturn = RegSetValueEx(
hKey,
TEXT("TestSubKey"),
0,
dwType,
lpBuffer,
dwNewSize
);
if( dwReturn == ERROR_SUCCESS )
_tprintf("Registry update successful\n");
else _tprintf("Registry update FAILED\n");
}
else _tprintf("pointer is not at end of buffer\n");
}
else _tprintf("Second RegQueryValueEx failed\n");
GlobalFree(HGLOBAL(lpBuffer));
}
else _tprintf("First RegQueryValueEx failed\n");
RegCloseKey(hKey);
}
else _tprintf("RegOpenKeyEx failed\n");
return 0;
}
Here's something quite flexible,
I didn't test it after making it though...
Code:// var outam is optional - returns failure only when out of memory
BOOL SplitMultiString(char *instr, char ***outstr, DWORD *outam)
{
DWORD a, i = 0, am = 1;
for(a = 0; instr[a] || instr[a + 1]; a++)
if(!instr[a])
am++;
a = 0;
char **out = new char*[am + 1];
if(!out)
return FALSE;
while(instr[a])
{
DWORD len = lstrlen(&instr[a]);
out[i] = new char[len + 1];
if(!out[i])
{
for(a = 0; a < i; a++)
delete [] out[a];
delete [] out;
return FALSE;
}
lstrcpy(out[i++], &instr[a]);
a += len + 1;
}
out[am] = NULL;
*outstr = out;
if(outam)
*outam = am;
return TRUE;
}
void FreeMultiString(char **outstr)
{
DWORD a;
for(a = 0; outstr[a]; a++)
delete [] outstr[a];
delete [] outstr;
return;
}
Hi all
Thanks for all your replies, RegSetValueEx requires that we have the multi string in the right format before writing it out, i.e. AAA\0BBB\0CCC\0\0 and I am having issues doing this.
This is what i am trying to do, basically I want to find if a data value exists in what i get from the registry (have done this already), if I find it I want to delete it, create the right format and then write back into the registry using the right format.
Below is what I have right now and I am stuck. I have read the REG_MULTI_SZ data from the registry and it is stored is params, I am basically checking if "XXX" is present in params[] and if I find it I am not writing it to tmp2 which is a pointer to an char array. As you can see strcat does not seem to help. Can someone help me with this Thanks!
Code:char* createMultiString(char *params[], int j, char *tmp2)
{
char *tmp=NULL;
int i=0;
int cpycount=0;
int count=0;
char value[MAX_PATH]={NULL};
sprintf (value,"XXX");
for (i=0; i<j;i++)
{
if (tmp2 == NULL)
{
printf ("In tmp2equal to NULL\n");
if (strcmp(params[i],value) != 0)
{
tmp2 = (char *)malloc(MAX_PATH);
strcpy(tmp2,params[i]);
}
else
{
printf ("XXX data value found\n");
}
}
else
{
printf ("In tmp2notequaltoNULL\n");
if (strcmp(params[i],value) != 0)
{
cpycount = strlen(params[i]);
int len2=0
char *ptr[] =params;
char *ptr1;
for(;;)
{
len2 =strlen[tmp2];
if (len2 == 0)
{
strncpy(tmp2,params[i],cpycount);
}
else
{
tmp2[count] = ptr1;
count++;
ptr1 += len2 + 1;
//ptr += cpycount + 1;
}
}
//strcat(tmp2,"\n");
//strcat(tmp2,params[i]);
}
else
{
printf ("XXX data value found\n");
}
}
}
strcat(tmp2,"\n");
printf("---Final Value of Temp is %s after appending all Parameters\n",tmp2);
return tmp2;
}
You may want to consider possibly using unsigned char * as the type of input as opposed to char * to your function as a starting point. Keep in mind that the data returned by RegQueuryValueEx call has embedded NULL terminators. Also, as novacain previously pointed out, strlen etc. won't work with data returned by the call. This is also due to the embedded NULL terminators.
Actually I was able to use the code snippet in comment #2, to retrieve data from the registry without the null terminators and have written it to params.
>>You may want to consider possibly using unsigned char * as the type of input as opposed to >>char * to your function as a starting point
Can you let me know where you are pointing this too? Thanks.
Sometimes nobody listens to me. Seriously guys, this way of doing it only takes a handful of lines of code when you use bits of the standard library like replace. We use this technique successfully in production code and the only problem we've had is that someone once forgot to include a newline at the end of the string they were appending.
Nevermind using a long and untested piece of code for this. If I wasn't just about to head off to work I could give you a short and obviously correct piece of code to do it easily.
iMalc,
I actually tried the approach that you suggested. But when I used RegSetValueEx to write to the registry, it writes just the first data value, not sure why. Thanks.
Here is one possible option. I've tested it withe my code from above.
It's called from the code as follows:Code:VOID SplitMultiString(unsigned char * instr)
{
int x = 0;
while(1)
{
printf("%c", instr[x]);
if(instr[++x] == '\0' && instr[x + 1] == '\0')
break;
else if (instr[x] == '\0')
{
++x;
printf("\n");
}
}
}
Code:SplitMultiString(lpBuffer);
Hii all,
Below are the latest modifications to my code, I believe the data has be written to tmp2
using ptr1, but not sure whether I am returning things right, when I print the value of tmp2 its prints only the first data value, which is obvious because of the \0 after the first data value.
I pass tmp2 into RegSetValueEx which is not correct. Ho can i pass the whole sting tmp2 which will include the null characters in between and teh terminating null characters.
Code:char* createMultiString(char *params[], int j, char *tmp2)
{
char *ptr1;
int i=0;
int count=0;
int len=0;
char value[MAX_PATH]={NULL};
sprintf (value,"XXX");
tmp2 = (char *)malloc(MAX_PATH);
memset(tmp2, '\0', sizeof(tmp2));
ptr1=tmp2;
for (i=0; i<j;i++)
{
printf ("In loop\n");
if (strcmp(params[i],value) != 0)
{
printf ("Data value not XXX\n");
strcpy(ptr1,params[i]);
len =strlen(ptr1);
printf ("Data value is %s and len is %d\n",ptr1,len);
ptr1 += len + 2;
printf ("The current pointer value is\n",ptr1);
}
else
{
printf ("Data value is XXX\n");
}
}
printf("---Final Value of Temp is %s after appending all Parameters\n",tmp2);
return tmp2;
}
Hi all
Just a follow up to my earlier post, this is how tmp2 is being passed to RegSetValueEx
Code:sprintf(value11, "ValueName");
RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)&srcKeyName,0,KEY_SET_VALUE, &Gt5Key);
if (setValue_multi_sz(Gt5Key, value11, tmp2) == true)
{
printf("---Success in setting new ValueName\n");
}
else
{
printf("---Failure in setting value of ValueName under %s , errorcode is: %d\n",srcKeyName, GetLastError());
}
bool setValue_multi_sz(HKEY hKey, const char *valueName, char* value) {
//indent++;
bool rc = true;
DWORD result = RegSetValueEx(hKey, valueName, 0,REG_MULTI_SZ, reinterpret_cast <CONST BYTE *>(value), strlen(value)+1);
if (NO_ERROR != result) {
//MessageBox(hWnd, valueName, "setValue error", MB_OK);
printf(msgbuff, "ERROR: RegSetValueEx(%s:%s) failed with error code %d.\n", valueName, value, result);
// LogMessage(hModule, (LPCTSTR)msgbuff);
rc = false;
}
else {
printf("\t to set %s=%s\n", valueName, value);
// LogMessage(hModule, (LPCTSTR)msgbuff);
}
// indent--;
return rc;
}
I'm just not sure I'd want to use strcpy, strlen etc. in your project. IMHO, it's just too easy to get into trouble using those calls with char arrays that have embedded nulls.Quote:
I pass tmp2 into RegSetValueEx which is not correct. Ho can i pass the whole sting tmp2 which will include the null characters in between and teh terminating null characters.
Here is one possible solution:
Code:#pragma comment ( lib, "advapi32.lib" )
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
CHAR* createMultiString(CHAR *params[], INT iCount, INT *iReturn)
{
char *pData = NULL;
int iIndex1,iIndex2;
for (iIndex1 = 0, iIndex2 = 0; iIndex1 < iCount; iIndex1++)
iIndex2 += strlen(params[iIndex1])+ 1;
pData = (char *)malloc(iIndex2);
memset(pData, 0, sizeof(pData));
for (iIndex1 = 0, iIndex2 = 0; iIndex1 < iCount; iIndex1++)
{
strcpy(pData+iIndex2, params[iIndex1]);
iIndex2 += strlen(params[iIndex1])+1;
}
pData[iIndex2] = 0;
*iReturn = iIndex2;
return pData;
}
INT main(VOID)
{
HKEY hKey = NULL;
char szValue[MAX_PATH] = {0};
char **pPtr = NULL;
int iReturn, iIndex;
pPtr = (char **)malloc(3 * sizeof *pPtr);
if (pPtr != NULL)
{
for (iIndex = 0; iIndex < 3; iIndex++)
pPtr[iIndex] = (char *) malloc(sizeof(char) * 10);
}
else return 0;
strcpy(pPtr[0], "String1");
strcpy(pPtr[1], "String2");
strcpy(pPtr[2], "String3");
memcpy(szValue, createMultiString(pPtr, 3, &iReturn), MAX_PATH);
RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("Software\\TestKey"),0,KEY_ALL_ACCESS,&hKey);
RegSetValueEx(hKey, "TestSubKey", 0,REG_MULTI_SZ, reinterpret_cast <CONST BYTE *>(szValue), iReturn + 1);
RegCloseKey(hKey);
return 0;
}
You probably took the strlen after converting \n back to \0 which wont work.
Here's an abstract from some old code production code where I have renamed everything to anonymise it, and this code is no longer used anyway. It appends a string to a multiistring registry value. This code is not particularly C++ish, and I take no responsibility for any problems it might cause. The code we eventually used did not have the potential buffer overrun this has.Code:TCHAR appendMe = _T("Append Me\n");
CRegKey regKey;
if (regKey.Create(HKEY_LOCAL_MACHINE, MY_HKEY_LOCATION) == ERROR_SUCCESS)
{
TCHAR multiString[512];
ULONG nChars(countof(multiString));
regKey.QueryMultiStringValue(_T("MyMultiString"), multiString, &nChars);
for (ULONG i=0; i<nChars-1; ++i)
if (multiString[i] == _T('\0'))
multiString[i] = _T('\n');
if (_tcsstr(multiString, appendMe) == NULL)
{
_tcscat(&multiString[nChars-1], appendMe);
nChars += (ULONG)_tcslen(appendMe);
for (ULONG i=0; i<nChars-1; ++i)
if (multiString[i] == _T('\n'))
multiString[i] = _T('\0');
regKey.SetMultiStringValue(_T("MyMultiString"), multiString);
}
regKey.Close();
}
Hi all, another follow up question
I have a string array with the following data
AAA BBB CCC DDD
and I want to copy all the above data expect BB to another string array. I have the below. inputarray is my source string array and I want to write it to outputarr.
The problem I have is as seen below where data does not get copied consecutively to outputarr. Can some help me on this? Thanks.
Code:nt remBBB(char *inputarray[], char *outputarr[], int count)
{
printf ("In remBBB\n");
int x=0;
int count1=0;
char value[MAX_PATH]={NULL};
sprintf (value,"BBB");
for (x=0; x < count; x++)
{
if (strcmp(inputarray[x],value) != 0)
{
memcpy(outputarr[x], inputarray[x], sizeof(outputarr[x]));
count1++;
}
}
return count1++;
}
Hi all, reposting because of some typos
I have a string array with the following data
AAA BBB CCC DDD
and I want to copy all the above data except BBB to another string array. I have the below. inputarray is my source string array and I want to write it to outputarr.
The problem I have is as seen below where data does not get copied consecutively to outputarr. Can some help me on this? Thanks.
Code:int remBBB(char *inputarray[], char *outputarr[], int count)
{
printf ("In remBBB\n");
int x=0;
int count1=0;
char value[MAX_PATH]={NULL};
sprintf (value,"BBB");
for (x=0; x < count; x++)
{
if (strcmp(inputarray[x],value) != 0)
{
memcpy(outputarr[x], inputarray[x], sizeof(outputarr[x]));
count1++;
}
}
return count1++;
}
One possible solution:
It's called as follows:Code:BOOL ModifyRegistryValue(LPTSTR lpInput, LPCTSTR lpSearchValue, INT iSize)
{
INT iCount = 0, iIndex = 0;
while(_tcslen(lpInput))
{
if(_tcscmp(lpInput, lpSearchValue) == 0)
{
iCount += _tcslen(lpInput) + 1;
iIndex = _tcslen(lpInput) + 1;
memcpy(lpInput,lpInput + iIndex, iSize - iCount);
lpInput[iIndex + iCount + 1] = 0;
return TRUE;
}
else
{
iCount += _tcslen(lpInput) + 1;
lpInput += _tcslen(lpInput) + 1;
}
}
return FALSE;
}
Code:RegQueryValueEx(
hKey,
TEXT("TestSubKey"),
0,
&dwType,
lpBuffer,
&dwSize
);
LPTSTR p = LPTSTR(lpBuffer);
ModifyRegistryValue(p, "BBB", dwSize);
Did you mean?
iSize is a byte count while iCount is a character count, their subtraction would yield an incorrect number of bytes to copy for Unicode strings.Code:memcpy(lpInput, lpInput + iIndex, (iSize - (iCount * sizeof(*lpInput))));
lpInput[_tcslen(lpInput) + 1] = 0;
return TRUE;
Then why use the all the _t versions? Besides judging from all the dubious casts in post 33 it looks like (s)he's planning to or already compiling as unicode.
Oh and the code in post 22 has an out of bounds access if a string consisting of "\0\0" is passed in. I know you're trying to help Bob, but giving out buggy code ultimately helps no-one.
@zmfcat
What errors or warnings do you get if you take out the red bit below? It smells a bit dodgy, and shouldn't be required.
Quote:
RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)&srcKeyName,0,KEY_SET_VALUE, &Gt5Key);
Hi all,
Thanks for your help, I was able to get things working. Posting the code snippet, which might help someone.
count is the number of data values found in the registry string.
Code:int remBBB(char *inputarray[], char *outputarr[], int count,MSIHANDLE hModule)
{
sprintf (msgbuff,"---In remBBB\n");
LogMessage(hModule, (LPCTSTR)msgbuff);
sprintf (msgbuff,"----Number of data values received by this function is %d\n",count);
LogMessage(hModule, (LPCTSTR)msgbuff);
int x=0;
int count1=0;
char value[MAX_PATH]={NULL};
sprintf (value,"BBB");
for (x=0; x < count; x++)
{
sprintf (msgbuff, "----The input data value is %s\n",inputarray[x]);
LogMessage(hModule, (LPCTSTR)msgbuff);
if (strcmp(inputarray[x],value) != 0)
{
outputarr[count1]= inputarray[x];
count1++;
}
}
return count1;
}
Hi Bob
I used the createMultiString snippet posted in comment #34, I have no issues on 32-bit compilation, but it throws me some warnings on 64-bit compilation, I see the below. Is there a conversion problem that we need to take care off? Thanks.
C:\Program Files\Microsoft Visual Studio\MyProjects\123\xxx\xxx.cpp(292) : warning C4267: '+=' : conversion from 'size_t' to 'int', possible loss of data
Just a follow up to comment #44, teh above warning is thrown on the line
iIndex2 += strlen(params[iIndex1])+ 1;
So make iIndex2 an size_t type, rather than int.
--
Mats
I STRONGLY advise you NOT to use sprintf().
sprintf() will crash your app any time the string equals or exceeds the buffer size (if not in the sprintf() the mem corruption will crash it later).
Use _snprintf() with the size set to one less than the buffer size.
It's many months after the thread has ended, but please let me support the accuracy and reliability of my work which BobS0327 rubbishes.
Regarding your "reputable source", yes, SHQueryValuEx "takes the same parameters" as RegQueryValueEx. It is plainly intended as a "drop-in replacement". However, as noted in my article, it is a replacement with an advantage. It varies the behaviour as described both in my article and (much less comprehensively) in Microsoft's SHLWAPI.H. Certainly, the expansion of environment variables in REG_EXPAND_SZ to be returned as REG_SZ data is easily verified with a test program.
That SHQueryValueEx also null-terminates REG_SZ data is more contentious - not for whether it does, but to what extent and with what deliberation. It's possible that where Microsoft (and your other "registry resources") talk of null-termination, they mean only the matter of how the string data gets stored in the caller's buffer. My analysis goes further, to ask how they deal with poor storage in the registry hive. If your own code did the storage, you might reasonably trust that all your strings are stored properly, but whether you should rely on other programmers "adhering to MS's guidelines" is a whole other question. SHQueryValueEx certainly behaves differently (indeed, better) in these bad cases than does RegQueryValueEx, though I can't say how much of this is due to explicit attention by Microsoft's programmers.
All the differences of behaviour that I describe in that article are carefully demonstrated. It's all open to reproduction. Now, if you know of a failed attempt to reproduce my demonstrations, then I would surely want you to question my accuracy and reliability, but if all you're doing is repeating from sources that obviously haven't even thought to attempt anything like the same depth, then take a little more care, please. Some of us trade on our reputations for checking facts.
On the matter of SHLWAPI versions, you again stress that you have "checked a lot of references" but of what quality and to what end? Although the first appearance of SHLWAPI in a Windows NT release is indeed (the original) Windows 2000, there is a SHLWAPI.DLL in Windows 95 OSR2 and in Internet Explorer 3.01, whatever any number of your references say to the contrary.
Though it is true that SHQueryValueEx is implemented primarily as pre-and-post-processing of calls to RegQueryValueEx which is in turn implemented primarily as pre-and-post-processing of a call to NtQueryValueKey, the idea that RegQueryValueEx and/or SHQueryValueEx "map directly" to any kernel function is entirely fanciful, whatever you think you've seen in a debugger. If nothing else, the kernel's registry functions don't know about the pre-defined keys such as HKEY_LOCAL_MACHINE. So, there has to be some non-trivial code (in ADVAPI32) to translate RegQueryValueEx to NtQueryValueKey - more than enough to defeat the casual inspection of all but experts.
Geoff Chappell