Double check my knowledge: Unicode - c++

there is this method: SCardListReaders, http://msdn.microsoft.com/en-us/library/windows/desktop/aa379793(v=vs.85).aspx
On MSDN there is an example.
After this call:
LPTSTR pmszReaders = NULL;
LPTSTR pReader;
lReturn = SCardListReaders(hSC,
NULL,
(LPTSTR)&pmszReaders,
&cch );
Such code is present which extracts strings from a multi string pmszReaders (multistring is terminated with double null characters).
pReader = pmszReaders;
while ( '\0' != *pReader )
{
// Display the value.
printf("Reader: %S\n", pReader );
// Advance to the next value.
pReader = pReader + wcslen((wchar_t *)pReader) + 1;
}
Just my question is. I think above code only works for Unicode right? If I disable Unicode support in my project, I think above code snippet will not correctly extract reader names? Isn't it?
For instance when I checked wcslen returns 22 when Unicode is set and 24 when Unicode is not set.

If you want the function to work in either a Unicode or MBCS build, use Microsoft specific _tcslen instead of wcslen.
If you want a compile time error if someone tries to use the function without Unicode, change LPTSTR to LPWSTR.

Related

How to use SHFileOperation() with CString paths

I am trying to convert CString to LPCWSTR and it works well. But something went wrong in the processing of the code.
I want to copy a directory to another path so I am using SHFILEOPSTRUCT:
HWND console = GetConsoleWindow();
SHFILEOPSTRUCT s = { 0 };
s.hwnd = console;
s.wFunc = FO_COPY;
s.fFlags = FOF_SILENT;
CString _folderName("a6_töüst-Oa5Z.OZS-CI5O5235"),
firstPath("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\"),
lastPart("\\Documents\\*\0"),
firstPathDest("C:\\ORTIM-Daten\\a5Pc 2.0.3\\"),
lastPartDest("Documents\\"),
_folderNameDest("a6_töüst-Oa5Z.OZS-CI5O5235\0");
CString cstrTemp = firstPath + _folderName + lastPart,
cstrTempDest = firstPathDest + lastPartDest + _folderNameDest;
s.pTo = cstrTempDest /*_TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Documents\\a6_töüst-Oa5Z.OZS-CI5O5235\0")*/;
s.pFrom = cstrTemp /*_TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\a6_töüst-Oa5Z.OZS-CI5O5235\\Documents\\*\0")*/;
SHFileOperation(&s);
When I am using CString directly, the copy operation doesn't work, but when I use the _TEXT() macro (as in the comments) to assign the LPCWSTR members in the struct everything works.
EDIT 1
In both variants of source and destination paths the code compiles.
In this variant, the code compiles and does the copy operation:
s.pTo = _TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Documents\\a6_töüst-Oa5Z.OZS-CI5O5235\0");
s.pFrom = _TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\a6_töüst-Oa5Z.OZS-CI5O5235\\Documents\\*\0");
In the other variant, which I actually need, the code compiles too, but the copy operation doesn't take place:
s.pTo = cstrTempDest;
s.pFrom = cstrTemp;
SHFILEOPSTRUCT expects strings ending with two NUL characters, but NUL terminated strings by definition end with one and any additional NUL characters are ignored by CString methods that don't take explicit length argument.
You can force double NUL by adding one manually:
CString cstrTempDest = firstPathDest + lastPartDest + _folderNameDest;
// *** Add NUL manually ***
cstrTempDest.AppendChar( 0 );
s.pTo = cstrTempDest;
// For debuging - verify resulting string with example.
TCHAR* test = _TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Documents\\a6_töüst-Oa5Z.OZS-CI5O5235\0");
// +2 because we want to check two NULs at end.
ASSERT( memcmp( s.pTo, test, (_tcslen(test)+2)*sizeof(TCHAR) ) == 0 );
Alternative solution can use methods with explicit length argument:
CString cstrTempDest = firstPathDest + lastPartDest
+ CString(_folderNameDest, _tcslen(_folderNameDest)+1);
If your project is configured to use unicode character set, call CString constructors with wide strings:
CString _folderName(_T("a6_töüst-Oa5Z.OZS-CI5O5235")),
firstPath(_T("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\"))
...
CString in unicode mode automatically converts narrow strings to wide ones, but it can fail when threre is discrepancy between runtime and development codepages. If you plan to go Unicode and never look back, throw away _TEXT, TEXT and _T macros and just use wide literals:
CString _folderName( L"a6_töüst-Oa5Z.OZS-CI5O5235" ),
firstPath( L"C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\" )
...
You should also check SHFileOperation return value.
The answer of user msp0815 on creating double null ended CString solves your issue.
// strings must be double-null terminated
CString from(cstrTemp + (TCHAR)'\0');
PCZZTSTR szzFrom= from;
s.pFrom= szzFrom;
CString dest(cstrTempDest + (TCHAR)'\0');
PCZZTSTR szzDest= dest;
s.pTo= szzDest;
I generally don't use LPCWSTR that much but here is my idea:
CString TestCSTR = "Hello world";
LPCWSTR TestLPC;
TestLPC = (LPCWSTR)_TEXT(TestCSTR.GetString());
It works as expected in fact the variable TestLPC holds "Hello world" or to be more precise a long pointer to it. It should be possible to remove _TEXT without consequences but I'm not sure, the result is the same btw.

C++: Trying to create a Run key, All i get is Chinese characters in registry.

Please save me! I am new to this, trying to figure this out. I would like to have my program add a run key to run itself on startup . Here is "my" code:
HKEY hKey = 0;
RegOpenKeyEx( HKEY_LOCAL_MACHINE,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
0,
KEY_ALL_ACCESS,
&hKey );
const unsigned char Path[ MAX_PATH ] = "C:\\test.exe";
RegSetValueEx( hKey, L"Testing", 0, 1, Path, strlen("C:\\test.exe") );
RegCloseKey(hKey);
This "works" except they key added reads "㩃瑜獥⹴硥" under data . Took me a while to figure out that the key is going to WoW6432Node too, thought it complied but wasn't working for the first 5 hours, much head to wall action there...
I am sure this has something to do with the way my string is formatted, ANSII vs ASCII vs the other 10 types of strings C++ doesn't seem to be able to convert between... I've tried using (BYTE*)"C:\virus.exe" and anything else i could think of... If i set the length to 1, the first character shows fine. But if its any other length, Chinese starts to show again.
Please help! I am about ready to start choking kittens here!
The problem is this:
const unsigned char Path[ MAX_PATH ] = "C:\\test.exe";
You have defined an ANSI string and then attempted to use the Unicode (UTF-16) version of RegSetValueEx:
RegSetValueEx( hKey, L"Testing", 0, 1, Path, strlen("C:\\test.exe") );
Under the hood, RegSetValueEx is a macro that aliases to RegSetValueExW because you defined the macro UNICODE.
The correct solution is to use a Unicode string literal:
const wchar_t Path[] = L"C:\\test.exe";
RegSetValueEx( hKey, L"Testing", 0, 1, (const BYTE *) Path, sizeof(Path) );
Here I used sizeof because the string is an array of characters whose size is known at compile time. For dynamic strings, use (wcslen(Path) + 1) * sizeof(*Path) instead.
Note: There is no need to specify the length of a constant literal in the declaration because the compiler can automatically deduce that in this specific scenario. It's also bad idea to duplicate the string literal inside your strlen/wcslen because if it goes out of sync your code could be broken and trigger undefined behavior.

Why obtained MachineGuid looks not alike a GUID but like Korean?

I created a simple function:
std::wstring GetRegKey(const std::string& location, const std::string& name){
const int valueLength = 10240;
auto platformFlag = KEY_WOW64_64KEY;
HKEY key;
TCHAR value[valueLength];
DWORD bufLen = valueLength*sizeof(TCHAR);
long ret;
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, location.c_str(), 0, KEY_READ | platformFlag, &key);
if( ret != ERROR_SUCCESS ){
return std::wstring();
}
ret = RegQueryValueExA(key, name.c_str(), NULL, NULL, (LPBYTE) value, &bufLen);
RegCloseKey(key);
if ( (ret != ERROR_SUCCESS) || (bufLen > valueLength*sizeof(TCHAR)) ){
return std::wstring();
}
std::wstring stringValue(value, (size_t)bufLen - 1);
size_t i = stringValue.length();
while( i > 0 && stringValue[i-1] == '\0' ){
--i;
}
return stringValue;
}
And I call it like auto result = GetRegKey("SOFTWARE\\Microsoft\\Cryptography", "MachineGuid");
yet string looks like
㤴ㄷ㤵戰㌭㉣ⴱ㔴㍥㤭慣ⴹ㍥摢㘵〴㉡ㄵ\0009ca9-e3bd5640a251
not like RegEdit
4971590b-3c21-45e3-9ca9-e3bd5640a251
So I wonder what shall be done to get a correct representation of MachineGuid in C++?
RegQueryValueExA is an ANSI wrapper around the Unicode version since Windows NT. When building on a Unicode version of Windows, it not only converts the the lpValueName to a LPCWSTR, but it will also convert the lpData retrieved from the registry to an LPWSTR before returning.
MSDN has the following to say:
If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, and
the ANSI version of this function is used (either by explicitly
calling RegQueryValueExA or by not defining UNICODE before including
the Windows.h file), this function converts the stored Unicode string
to an ANSI string before copying it to the buffer pointed to by
lpData.
Your problem is that you are populating the lpData, which holds TCHARs (WCHAR on Unicode versions of Windows) with an ANSI string.
The garbled string that you see is a result of 2 ANSI chars being used to populate a single wchar_t. That explains the Asian characters. The portion that looks like the end of the GUID is because the print function blew past the terminating null since it was only one byte and began printing what is probably a portion of the buffer that was used by RegQueryValueExA before converting to ANSI.
To solve the problem, either stick entirely to Unicode, or to ANSI (if you are brave enough to continue using ANSI in the year 2014), or be very careful about your conversions. I would change GetRegKey to accept wstrings and use RegQueryValueExW instead, but that is a matter of preference and what sort of code you plan on using this in.
(Also, I would recommend you have someone review this code since there are a number of oddities in the error checking, and a hard coded buffer size.)

C++ string encoding UTF8 / unicode

I am trying to be able to send character "Т" (not a normal capital t, unicode decimal value 1058) from C++ to VB
However, with this method below Message is returned to VB and it appears as "Т", which is the above character encoded in ANSI.
#if defined(_MSC_VER) && _MSC_VER > 1310
# define utf8(str) ConvertToUTF8(L##str)
const char * ConvertToUTF8(const wchar_t * pStr) {
static char szBuf[1024];
WideCharToMultiByte(CP_UTF8, 0, pStr, -1, szBuf, sizeof(szBuf), NULL, NULL);
return szBuf;
}
#else
# define utf8(str) str
#endif
BSTR _stdcall chatTest()
{
BSTR Message;
CString temp("temp test");
temp+=utf8("\u0422");
int len = temp.GetLength();
Message = SysAllocStringByteLen ((LPCTSTR)temp, len+1 );
return Message;
}
If I just do temp+=("\u0422"); without the utf8 function. It sends the data as "?" and its actually a question mark (sometimes unicode characters show up as question marks in VB, but still have the correct unicode decimal value.. this is not the case here... it changes it to a question mark.
In VB if I output the String variable that has data from Message when it is "Т" to a text file it appears as the "Т".
So as far as I can tell its in UTF8 in C++, then somehow gets converted to ANSI in VB (or before its sent?), and then when outputted to a file its changed back to UTF8?
I just need to keep the "Т" intact when sending from C++ to VB. I know VB strings can hold that character because from another source within VB I am able to store it (it appears as a "?", but has the proper unicode decimal value).
Any help is greatly appreciated.
Thanks
A BSTR is not UTF-8, it's UTF-16 which is what you get with the L"" prefix. Take out the UTF-8 conversion and use CStringW. And use LPCWSTR instead of LPCTSTR.

C++ concat LPCTSTR

I am implementing a custom action for a WindowsCE CAB file, and I need to concat a LPCTSTR to get a proper path to an exe.
My custom action receives a LPCTSTR as an argument.
So (pseudocode):
extern "C" codeINSTALL_EXIT MYCUSTOMACTION_API Install_Exit(
HWND hwndParent,
LPCTSTR pszInstallDir,
WORD cFailedDirs,
WORD cFailedFiles,
WORD cFailedRegKeys,
WORD cFailedRegVals,
WORD cFailedShortcuts
)
{
if (FALSE == LaunchApp(pszInstallDir + "\\MyApp.exe"))
::MessageBox(hwndParent, L"Could not launch app!", L"Setup", MB_ICONINFORMATION );
return codeINSTALL_EXIT_DONE;
}
This is using the imaginary "+" operator, that I would use in my standard language, C#.
I have relatively little experience in C++. What is the proper way to append a LPCTSTR for my purposes? The LaunchApp method uses this type as an argument.
Also if I want to display the resulting path (for debugging purposes) in a MessageBox, is there a quick way to convert to a LPCWSTR?
For concatenation use StringCchCat
TCHAR pszDest[260] = _T("");
StringCchCat(pszDest, 260, pszInstallDir);
StringCchCat(pszDest, 260, _T("\\MyApp.exe"));
LaunchApp(pszDest);
You need to allocate a new buffer to assemble the combined string in and then copy both parts into it. You can either pick a fixed, large buffer size
TCHAR fullPath[MAX_PATH + 11]; // 11 = length of "\MyApp.exe" + nul in characters
_sntprintf_s(fullPath, MAX_PATH + 11, _T("%s\\MyApp.exe"), pszInstallDir);
or allocate it dynamically to fit:
size_t installDirLen = tcslen(pszInstallDir);
size_t bufferLen = installDirLen + 11; // again 11 = len of your string
LPWSTR fullPath = new TCHAR[bufferLen];
// if you're paranoid, check allocation succeeded: fullPath != null
tcsncpy_s(fullPath, bufferLen, pszInstallDir);
tcsncat_s(fullPath, bufferLen, _T"\\MyApp.exe");
// use it
delete fullPath;
If you're in Unicode mode then LPCTSTR == LPCWSTR (in MBCS mode == LPCSTR instead). Either way the MessageBox macro should work for you - it'll choose between MessageBoxA or MessageBoxW as appropriate.
As ctacke points out below, this in on Windows CE and I can't assume you're going to have the _s functions. I think in the second case it's OK to use the non _s variants since we know the buffer is big enough, but in the first _sntprintf does not guarantee a trailing null on the output string (as the _s version does) and so we need to initialise the buffer ourselves first:
size_t bufferLen = MAX_PATH + 11;
TCHAR fullPath[bufferLen];
// zero the buffer out first
memset(fullPath, 0, sizeof(TCHAR) * bufferLen);
// only write up to bufferLen - 1, i.e. ensure the last character is left zero
_sntprintf(fullPath, bufferLen - 1, _T("%s\\MyApp.exe"), pszInstallDir);
(It might also be possible to do this by omitting the memset and using _sntprintf's return value to find the end of the combined generated string and nul the next character.)
AFAICR Windows CE is Unicode only and so LPCTSTR == LPCWSTR always.
You can use string to be concatenated and then cast the result to LPCTSTR using ATL helpers like CA2T:
std::string filePath = "\\\\user\\Home\\";
std::string fileName = "file.ex";
std::string fullPath = filePath + fileName;
CA2T t(fullPath.c_str());
LPCTSTR lpctsrFullPath = t;