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.)
Related
I have a service which writes in a named pipe, it writes a DWORD, Its is then read by another process(both running in unicode)
When I try to receive the DWORD and convert it to a displayable string (TCHAR,char,wchar_t etc) and print it using printf in command prompt, I get uneven results with newline
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
TCHAR szBuffer[SIZEOF_BUFFER];
DWORD dwRead;
for (;;)
{
if (!ReadFile(hRemoteOutPipe, szBuffer, SIZEOF_BUFFER, &dwRead, NULL) ||
dwRead == 0)
{
DWORD dwErr = GetLastError();
if (dwErr == ERROR_NO_DATA)
break;
}
szBuffer[dwRead / sizeof(TCHAR)] = _T('\0');
// Send it to our stdout
printf("%s",szBuffer);
fflush(stdout);
}
CloseHandle(hRemoteOutPipe);
hRemoteOutPipe = INVALID_HANDLE_VALUE;
::ExitThread(0);
the printf works fine for multibyte, but doesnt work fine for unicode, Kindly help me out
First off: TCHAR was an idea that made sense in 1995, not so in 2022. You get these weird errors. Secondly, you're tagging as C++, but you're not using std::cout. That is really the root cause of the problem: printf doesn't understand TCHAR, you'd need to use _tprintf.
TCHAR is a preprocessor macro that maps to either wchar_t or char depending on whether UNICODE is defined or not, respectively.
The %s placeholder expects a char* string in printf(), whereas it expects a wchar_t* string in wprintf().
Since you are using TCHAR strings, the output will not be what you expect when TCHAR is wchar_t, as that would be a type-mismatch in printf(). You would need to use _tprintf() in <tchar.h> instead, which is a preprocessor macro that maps to either wprintf() or printf() depending on UNICODE.
I'm developing an application and would like to know if there's a way to get it's executable path automatically and run alongside Windows startup by adding it to the registry.
This is my function so far:
void Open(){
HKEY hKey;
WCHAR path[MAX_PATH]; //to store the directory
DWORD size = GetModuleFileNameW(NULL, path, MAX_PATH);
const char* StartName = "MyApplication";
LONG lnRes = RegOpenKeyEx( HKEY_CURRENT_USER,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
0 , KEY_WRITE,
&hKey);
if( ERROR_SUCCESS == lnRes )
{
lnRes = RegSetValueEx( hKey,
StartName,
0,
REG_SZ,
(LPBYTE)path,
size );
}
RegCloseKey(hKey);
}
I'm using GetModuleFileName to get the path, but it returns me the path with a single backslash and in the registry it only recognizes the "D" drive. For example: D:\Usuario\Desktop\log\mariobros.exe
https://prnt.sc/vondsi (Here's a print from my registry)
I suspect that the problem is that for the code to be recognized as a single backslash it needs to have a double backslash. This is how I think it should've need to be: D:\\Usuario\\Desktop\\log\\mariobros.exe
Does anyone know what could I do here?
Thanks in advance.
You are clearly compiling with UNICODE undefined in your project, which means RegOpenKeyEx() and RegSetValueEx() are actually calling the ANSI functions RegOpenKeyExA() and RegSetValueExA(), respectively (as evident by you being able to pass char* strings to them without compiler errors).
But, you are retrieving the file path as a Unicode UTF-16 string and passing it as-is to RegSetValueExA(), so you end up with embedded nul characters written to the Registry when RegSetValueExA() misinterprets your UTF-16 string as an ANSI string and re-encodes each of its bytes individually to Unicode characters. Unicode characters in the ASCII range have nul bytes in them.
Since you are using a Unicode function to retrieve the file path, and because the Registry internally stores strings in Unicode form only, you should use the Registry's Unicode functions to match that same encoding.
Also, note that the return value of GetModuleFileName(A|W) does not include the null terminator in the output string's length, but RegSetValueEx(A|W) expects the cbSize parameter to include enough bytes for a null terminator for REG_(EXPAND_|MULTI_)SZ value types.
Try this:
void Open()
{
WCHAR path[MAX_PATH]; //to store the directory
DWORD size = GetModuleFileNameW(NULL, path, MAX_PATH);
if ((size > 0) && (size < MAX_PATH))
{
HKEY hKey;
LONG lnRes = RegOpenKeyExW(HKEY_CURRENT_USER,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
0, KEY_SET_VALUE,
&hKey);
if( ERROR_SUCCESS == lnRes )
{
lnRes = RegSetValueExW(hKey,
L"MyApplication",
0,
REG_SZ,
(LPBYTE)path,
(size + 1) * sizeof(WCHAR) );
RegCloseKey(hKey);
}
}
}
This looks like you are passing a wide string, while promising that it is a narrow string (evident by your C-style cast).
The second byte in a wide string is 0, and this terminates your narrow string.
Suggestion: use wide strings only while dealing with Win API.
I googled it for 2 hours now, and i can't find an answer for my problem: i need to get a registry REG_SZ value and pass it to a char*.
char host_val[1024];
DWORD hostVal_size = 1024;
char* hostName;
DWORD dwType = REG_SZ;
RegOpenKeyEx(//no problem here);
if( RegQueryValueEx( hKey, TEXT("HostName"), 0, &dwType, (LPBYTE)&host_val, &hostVal_size ) == ERROR_SUCCESS )
{
//hostName = host_val;
}
How should i do this conversion hostName = host_val?
The resulting host_val is a possibly non-null-terminated string (see "Remarks"), so you should copy it to a newly allocated string with memcpy, and ensure it's null-terminated:
hostName = new char[hostVal_size + 1];
// host_val may or may not be null-terminated
memcpy(hostName, host_val, hostVal_size);
hostName[hostVal_size] = '\0';
You will need to delete[] the hostName later.
use the ANSI version of the function
RegQueryValueExA
that way you don't need to convert.
If you're compiling with Unicode you're copying a Unicode string (that is possibly NOT terminated) into a narrow char buffer. the first character in the unicode string will be 0x3100 (accounting for the endianness on your machine, which is likely little-endian, and the fact that you said the IP address is 192....)
That value stuffed into the char[] array will report back as a single-char-null-terminated string. You have two options.
Use RegQueryValueExA, everything else stays the same, or
Change your char[] array to a wchar_t[] array, do what you're currently doing, then convert to narrow using WideCharToMultiByte(docs are in the SDK).
For obvious reasons, I'd take the former of those two options.
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;
Is it good/safe/possible to use the tiny utfcpp library for converting everything I get back from the wide Windows API (FindFirstFileW and such) to a valid UTF8 representation using utf16to8?
I would like to use UTF8 internally, but am having trouble getting the correct output (via wcout after another conversion or plain cout). Normal ASCII characters work of course, but ñä gets messed up.
Or is there an easier alternative?
Thanks!
UPDATE: Thanks to Hans (below), I now have an easy UTF8<->UTF16 conversion through the Windows API. Two way conversion works, but the UTF8 from UTF16 string has some extra characters that might cause me some trouble later on...). I'll share it here out of pure friendliness :) ):
// UTF16 -> UTF8 conversion
std::string toUTF8( const std::wstring &input )
{
// get length
int length = WideCharToMultiByte( CP_UTF8, NULL,
input.c_str(), input.size(),
NULL, 0,
NULL, NULL );
if( !(length > 0) )
return std::string();
else
{
std::string result;
result.resize( length );
if( WideCharToMultiByte( CP_UTF8, NULL,
input.c_str(), input.size(),
&result[0], result.size(),
NULL, NULL ) > 0 )
return result;
else
throw std::runtime_error( "Failure to execute toUTF8: conversion failed." );
}
}
// UTF8 -> UTF16 conversion
std::wstring toUTF16( const std::string &input )
{
// get length
int length = MultiByteToWideChar( CP_UTF8, NULL,
input.c_str(), input.size(),
NULL, 0 );
if( !(length > 0) )
return std::wstring();
else
{
std::wstring result;
result.resize( length );
if( MultiByteToWideChar(CP_UTF8, NULL,
input.c_str(), input.size(),
&result[0], result.size()) > 0 )
return result;
else
throw std::runtime_error( "Failure to execute toUTF16: conversion failed." );
}
}
The Win32 API already has a function to do this, WideCharToMultiByte() with CodePage = CP_UTF8. Saves you from having to rely on another library.
You cannot normally use the result with wcout. Its output goes to the console, it uses an 8-bit OEM encoding for legacy reasons. You can change the code page with SetConsoleCP(), 65001 is the code page for UTF-8 (CP_UTF8).
Your next stumbling block would be the font that's used for the console. You'll have to change it but finding a font that's fixed-pitch and has a full set of glyphs to cover Unicode is going to be difficult. You'll see you have a font problem when you get square rectangles in the output. Question marks are encoding problems.
Why do you want to use UTF8 internally? Are you working with so much text that using UTF16 would create unreasonable memory demands? Even if that was the case, you're probably better off using wide chars anyway, and dealing with memory issues in some other way (using a disk cache, better algorithms or data structures).
Your code will be much cleaner and easier to deal with using wide chars native to the Win32 API internally, and only doing UTF8 conversions when reading or writing out data that requires it (eg. XML files or REST APIs).
Your problem may also occur at the point where you print your output to the console, see: Output unicode strings in Windows console app
Finally I haven't used the utfcpp library, but UTF8 conversions are fairly trivial to perform using Win32's WideCharToMultiByte and MultiByteToWideChar with CP_UTF8 as the code page. Personally I would do a one time conversion and work with the text in UTF16 until it was time to output or transfer it in UTF8 if needed.