by Michael S. Kaplan, published on 2007/07/08 12:18 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2007/07/08/3764316.aspx
Bindesh's question was:
Hi
I am trying to print out the value from a reg key and have the following code:HKEY hkey;
LONG returnStatus;
DWORD dwRegType = REG_SZ;
DWORD dwRegSize = 255;
char cRegVal[255];
char regKeyPath[] ="";
returnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Netmon3"),
0,
KEY_READ,
&hkey);if(returnStatus == ERROR_SUCCESS) {
returnStatus = RegQueryValueEx(hkey,TEXT("InstallDir"),NULL,&dwRegType,(LPBYTE)&cRegVal,&dwRegSize);
if(returnStatus == ERROR_SUCCESS) {
cout<<cRegVal;
} else {
cout<<"Error "<<returnStatus<<endl;
}
} else {
cout<<"did not get the key"<<endl;
}The issue is that the output from cRegVal(highlighted one) just brings the first character (in this case, just the alphabet ‘c’) from the array and when I see the value stored in the array(putting a breakpoint) I find that after each character there is a null kept that basically marks the character as a complete string and hence I am unable to print the entire value.
Luckily, Doron Holan (a fellow Technical Lead, at least for the moment!) pointed out the problems here quickly:
Are you compiling your application as Unicode? You are mixing character size independent macros (TEXT) with hard coded types (char) instead of size independent types (TCHAR). Furthermore, cout is ANSI, to output in unicode you need to use wcout.
d
Interestingly, I answered the same question that was forwarded to me earlier (thought not seen by Bindesh until after he had asked elsewhere) as follows:
The NULL values are likely due to it being Unicode text, and for that you need to use wcout, not cout.
Though looking at the code you will also want to fix mixed use of TEXT macros and hardcoded data types (if nothing else use WCHAR/wchar_t, but consider getting rid of TEXT() and replacing with L"" type strings, etc.).
Now the way that Doron presented the answer was very interesting, and it is the type of thing I want to get better at myself.
The code clearly had two problems -- one of which was causing the reported problem, and the other of which led to the problem and might lead the same problem happening again (in fact, had the code only been switched from cout to wcout, I think it would have also failed with the new error of a datatype mismatch).
But by starting with the actual solution to the immediate problem rather than starting with underlying problem, I might have unintentionally caused the other issues to not be considered due to the general situation in many cases of developers "coding 'til it works" rather than "coding 'til its right".
Now in this case it wouldn't matter since both problems had to be fixed, at least enough for the wcout call to succeed. But sometimes that won't be the case, and a person could get by until the next bug that came up.
The benefit is in pointing out the architectural issues first (the char variable that led to the cout call) rather than treating it like a side point. It makes it more likely that a person would be nore likely to address all of the issues at the same time....
Now I am probably reading way too much into this short example and attributing way too much to Doron, though he is actually a good person to pay attention to, anyway.
So no harm done. But it did remind me of this particular tendency I have in question answering which ironically enough can be appreciated by the asker even if it would be more helpful to end with the answer rather than begin with it. :-)
This post brought to you by d (U+0064, a.k.a. LATIN SMALL LETTER D)
# Michael Dunn_ on 8 Jul 2007 4:34 PM:
The fact that AppWizards now default to Unicode builds has tripped up many, many newbies. I assume they are working off platform-independent books and/or sample code that make no mention of character sets and use char everywhere. When they get a seemingly crazy error message on a simple line such as MessageBox("Hello world") they don't have the knowledge or experience to fix it, let alone understand the underlying issues.
At least your and Doron's responses were better than the average you'd find on a message board. Often times, replies will say to use casts, which shut the compiler up but can leave a newbie in a worse position (with more code he doesn't understand). And if the casts happen to work, it still hurts the newbie because he gets the impression that throwing in casts is the right way to fix compiler errors.
# Erzengel on 8 Jul 2007 7:24 PM:
std::cout accepts wchar_t*s, and std::wcout accepts char*s, both printing out "acceptable" results. This can trip up newbies to unicode (like me, at a time) when std::wcout << "blah"; works but MessageBoxW("blah",…) doesn't. Wouldn't it be nice to have a compiler switch where "blah" was unicode and C"blah" was ansi, or something like that? (yes yes, non-standard. Wouldn't it be nice though?)
Or, Windows could do as DirectX and other COM apis do and have a precompiler directive to use c++'s features, for example:
#ifdef __cplusplus
inline int MessageBox(char* Message,…)
{
return MessageBoxA(Message,…);
}
inline int MessageBox(wchar_t* Message,…)
{
return MessageBoxW(Message,…);
}
#else
#if _UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif//_UNICODE
#endif//_cplusplus
Not that such a thing will be done... probably would cost too much for the benefit recieved. Plus it makes it even harder to break the habit of forgetting the L's in front of unicode text.
# Michael S. Kaplan on 9 Jul 2007 9:24 AM:
Well, "acceptable" is relative here though, right? You do end up with some truncation in some cases....