LogonUserW logon fails for a non-ascii username - c++

I have C++ code that tries to authenticate a local user to windows:
BOOL result = ::LogonUserW(localAdminUserName_.c_str(), L".", localAdminPassword_.c_str(),
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT);
This works fine for ASCII character-set based usernames.
But doesn't work for a user named, say userああ
If I print the variable localAdminUserName_ in the log, It's printing the username just fine.
Is .c_str() messing it up somehow?
Should I encode the username/password in someway before making this API call?
The following console application I made to test this scenario, is working fine!
_declspec(dllimport)
BOOL
__stdcall
LogonUserW(
__in LPCWSTR lpszUsername,
__in_opt LPCWSTR lpszDomain,
__in LPCWSTR lpszPassword,
__in DWORD dwLogonType,
__in DWORD dwLogonProvider,
__deref_out PHANDLE phToken
);
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hToken = NULL;
BOOL returnValue = LogonUserW(
L"userああ",
L".",
L"pa$$w0rd",
LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT,
&hToken);
if (returnValue == false) {
std::cout<<"Error!";
} else {
std::cout<<"Success!";
}
return 0;
}

Problem got solved after the original string is converted to multi byte string and then used the MultiByteToWideChar method in windows.h to convert it to wide char:
//convert the credentials to a multi-byte string first
std::string MbLocalAdminUserName = MbString(localAdminUserName_.c_str());
std::string MbLocalAdminPassowrd = MbString(localAdminPassowrd_.c_str());
//convert this multi-byte format to wide char that windows is expecting
//username
int len_MbLocalAdminUserName = MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminUserName.c_str() , -1, NULL, 0);
wchar_t* WcLocalAdminUserName = new wchar_t[len_MbLocalAdminUserName + 1];
MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminUserName.c_str(), -1, WcLocalAdminUserName, len_MbLocalAdminUserName + 1);
//password
int len_MbLocalAdminPassowrd = MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminPassowrd.c_str() , -1, NULL, 0);
wchar_t* WcLocalAdminPassowrd = new wchar_t[len_MbLocalAdminPassowrd + 1];
MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminPassowrd.c_str(), -1, WcLocalAdminPassowrd, len_MbLocalAdminPassowrd + 1);
BOOL result = ::LogonUser(WcLocalAdminUserName, L".", WcLocalAdminPassowrd,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken);
delete[] WcLocalAdminUserName;
delete[] WcLocalAdminPassowrd;
Where MbString is :
MbString::MbString(const wchar_t* src)
: buf_(0) {
const size_t count = 1 + sizeof(wchar_t) * wcslen(src);
buf_ = new char[count];
// The 3rd parameter specifies the size of multi-bytes string buffer.
wcstombs(buf_, src, count);
}

Related

RegCreateKeyEx always returns 5 ERROR_ACCESS_DENIED even when admin

I'm really stuck...I have a Windows program that I'm trying to simply write a value to the Registry.
The problem is RegCreateKeyEx() returns 5 (ERROR_ACCESS_DENIED).
My logon username belongs to the local Administrators group.
The lpdwDisposition parameter in RegCreateKeyEx() gets set to 2 (REG_OPENED_EXISTING_KEY), but RegCreateKeyEx() still returns ERROR_ACCESS_DENIED.
It's a 32-bit program, so I have the SAM set to KEY_WOW64_32KEY | KEY_WRITE
Here is minimal working example code:
#include <Windows.h>
#include <time.h>
#include <strsafe.h>
void GetCurrentDateAndTimeWithMs(WCHAR *sValueData, DWORD dwSizeValueData)
{
CONST INT iSize = 64;
WCHAR sDate[iSize] = {0};
WCHAR sTime[iSize] = {0};
SYSTEMTIME lt = {0};
GetLocalTime(&lt);
GetDateFormat(LOCALE_USER_DEFAULT, NULL, &lt, NULL, sDate, iSize);
GetTimeFormat(LOCALE_USER_DEFAULT, NULL, &lt, NULL, sTime, iSize);
StringCchPrintf(sValueData, dwSizeValueData, L"%s %s.%u",sDate, sTime, lt.wMilliseconds);
}
DWORD SaveToRegistry(CONST WCHAR *sPath, CONST WCHAR *sValueName, CONST WCHAR *sValueData)
{
LSTATUS dwRV = 0;
HKEY hKey = NULL;
DWORD rv = 0;
DWORD dwType_Local = REG_SZ;
dwRV = RegCreateKeyExW(HKEY_LOCAL_MACHINE, sPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WOW64_32KEY | KEY_WRITE, NULL, &hKey, &rv);
if (dwRV == ERROR_SUCCESS)
{
DWORD dwLenData = (DWORD) wcslen(sValueData) +1;
dwRV = RegSetValueExW(hKey, sValueName, 0, dwType_Local, (LPBYTE) sValueData, dwLenData * sizeof(WCHAR));
}
RegCloseKey(hKey);
return dwRV;
}
int main()
{
WCHAR sDT[64] = {0};
GetCurrentDateAndTimeWithMs(sDT, 64);
SaveToRegistry(L"Software\\Company\\Product\\Settings", L"CurrentDateTime", sDT);
}

Windows CAPI CryptDecrypt error key not working

I am trying to decrypt data from openssl generated private key. I have converted the public key from PEM to DER format, but when i use CryptDecrypt it throws error key does not exist. I have previously used the same method to encrypt data and decrypt using openssl, i am also aware regarding the Endianess difference in openssl and wincrypto. Here is the code if someone can point out where i am going wrong.
#include <Windows.h>
#include <wincrypt.h>
#include <stdio.h>
#pragma comment(lib, "Crypt32.lib")
char default_pub_key[] =
"-----BEGIN PUBLIC KEY-----"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVTOXB/Ti8SFvP42Z1XFB6GQ+R"
"jnqs42XiTFRXWpsSTlSPMRHi8aXpf1KYzzKHMC+4hU3rrgdbOu8bl7FekDoy38No"
"PX8ACoEmRhdn8mXs+ftmIRCuEE44mtgWUme65A1nTyT8nRmAVF6roo/rry+Xkbe9"
"iC6vRBRbVzprmCv7jwIDAQAB"
"-----END PUBLIC KEY-----";
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
DWORD dwKeySize = 0;
void SwapBytes(char* pv, size_t n) {
char* p = pv;
size_t lo, hi;
for (lo = 0, hi = n - 1; hi > lo; lo++, hi--)
{
char tmp = p[lo];
p[lo] = p[hi];
p[hi] = tmp;
}
}
int init_crypto()
{
LPBYTE pbBuffer;
DWORD dwKeyBlob, dw_pub_key_len = 0;
unsigned int offset = 22; // 22 = 1024, 24 = 2048 and so on
DWORD dwParamSize = sizeof(DWORD);
CERT_PUBLIC_KEY_INFO* publicKeyInfo;
CryptStringToBinaryA(default_pub_key, 0, CRYPT_STRING_ANY, NULL, &dw_pub_key_len, NULL, NULL);
pbBuffer = (LPBYTE)GlobalAlloc(GPTR, dw_pub_key_len);
CryptStringToBinaryA(default_pub_key, 0, CRYPT_STRING_ANY, pbBuffer, &dw_pub_key_len, NULL, NULL);
dwKeyBlob = 0;
CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbBuffer, dw_pub_key_len, 0, NULL, NULL, &dwKeyBlob);
publicKeyInfo = (CERT_PUBLIC_KEY_INFO*)GlobalAlloc(GPTR, dwKeyBlob);
CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbBuffer, dw_pub_key_len, 0, NULL, publicKeyInfo, &dwKeyBlob);
CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING, publicKeyInfo, &hKey);
CryptGetKeyParam(hKey, KP_KEYLEN, (BYTE*)&dwKeySize, &dwParamSize, 0);
char da[16];
memset(da, 0, sizeof(da));
wsprintfA(da, "%d", dwKeySize);
MessageBoxA(NULL, da, NULL, MB_OK);
dwKeySize /= 8;
return 0;
}
int main(int argc, char** argv)
{
// start rsa crypto key
init_crypto();
// Read encrypted data
DWORD junk;
HANDLE hEncFile = CreateFile(L"test_enc.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD EncFileSize = GetFileSize(hEncFile, 0);
char* EncFileBuf = (char*)GlobalAlloc(GPTR, EncFileSize + 1);
ReadFile(hEncFile, EncFileBuf, EncFileSize, &junk, NULL);
CloseHandle(hEncFile);
// convert for win32
SwapBytes((char*)EncFileBuf, EncFileSize);
CryptDecrypt(hKey, 0, TRUE, 0,(PBYTE)EncFileBuf, &EncFileSize);
hEncFile = CreateFile(L"test_dec.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hEncFile, EncFileBuf, EncFileSize, &junk, NULL);
CloseHandle(hEncFile);
GlobalFree(EncFileBuf);
// Do proper cleanup
return 0;
}
CryptDecrypt
[in] hKey
A handle to the key to use for the decryption. An application obtains this handle by using either the CryptGenKey or CryptImportKey function.
You pass a wrong hKey to CryptDecrypt. Use CryptImportKey after CryptImportPublicKeyInfo for getting an expected hKey and pass it to CryptDecrypt.

Writing & reading to windows registry with windows API

I am trying to write&read from the windows register:
Writting:
std::string path = "c:\\"
LPCTSTR str_data = TEXT(path.c_str());
auto size = static_cast<DWORD>(strlen(str_data));
LONG setRes = RegSetValueEx(*key, TEXT("DumpFolder"), 0, REG_EXPAND_SZ, (LPBYTE)str_data, size);
Reading:
char str_data[1028];
DWORD keyType;
DWORD size;
auto sk = TEXT("SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps");
auto status = RegGetValue(HKEY_LOCAL_MACHINE, sk, TEXT("DumpFolder"), RF_RT_REG_EXPAND_SZ, &keyType, str_data, &size);
Writing appears to work fine, at least it looks fine in regedit.exe.
Reading fails with ERROR_INVALID_PARAMETER = 87. If I change RF_RT_REG_EXPAND_SZ to RRF_RT_ANY, it works in debug mode, but still fails in release with error code ERROR_MORE_DATA = 234. I tried:
std::string path = "c:\\";
path = path + "\0" (it should be null terminated anyway
but it doesn't help
UPDATE
First of all, thanks for answers, I understand the thing a little better now. Unfortunately, I am still unable to read the string successfully.
Here is the test example combined from the answer below:
HKEY registry_key;
LPCTSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting";
// open registry key
auto openRes = RegOpenKey(HKEY_CURRENT_USER, sk, &registry_key);
// set default dump options
HKEY default_key;
auto createRes = RegCreateKey(registry_key, "LocalDumps", &default_key);
if (createRes != ERROR_SUCCESS) {
auto b = createRes;
}
std::string path = "c:\\";
LONG setRes = RegSetValueExA(default_key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
std::string str_data;
DWORD size = 0;
const char *sak = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
auto status = RegGetValueA(HKEY_CURRENT_USER, sak, "DumpFolder", RRF_RT_REG_EXPAND_SZ, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1)) {
str_data.resize(size - 1);
status = RegGetValueA(HKEY_CURRENT_USER, sk, "DumpFolder", RRF_RT_REG_EXPAND_SZ, NULL, &str_data[0], &size);
}
Writing again works fine (checked in regedit, and the return error code). On the other hand, reading the size of string register sets the size to 0 and returns error code 87 = ERROR_INVALID_PARAMETER.
Apparently, I am still missing something. (the project is set to multy-byte character set)
SOLUTION
After fixing things proposed by the answers below, the following code worked for me:
#include <Windows.h>
#include <string>
#include <iostream>
#define reg_type HKEY_LOCAL_MACHINE
void main() {
const std::string reg_path = "Software\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";
const std::string dump_folder = "DumpFolder";
const std::string path = "c:\\";
// WRITING
HKEY default_key;
auto status = RegCreateKeyExA(reg_type, reg_path.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, NULL, &default_key, NULL);
if (status != ERROR_SUCCESS) {
std::cout << "Creating key failed.";
return;
}
status = RegSetValueExA(default_key, dump_folder.c_str(), 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
if (status != ERROR_SUCCESS) {
std::cout << "Setting key value failed.";
return;
}
// READING
std::string str_data;
DWORD size = 0;
status = RegGetValueA(default_key, "", dump_folder.c_str(), RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1)){
str_data.resize(size - 1);
status = RegGetValueA(default_key, "", dump_folder.c_str(), RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ, NULL, &str_data[0], &size);
std::cout << "Successfully read key value: " << str_data;
} else {
std::cout << "Unable to retrive value. Error: " << status;
}
RegCloseKey(default_key);
}
I found, that RegGetValueA should be called with a
RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ
flag, which appears strange, but is described in header where it is defined, so I guess it is correct. If using only
RRF_RT_REG_EXPAND_SZ
error 87 occurs ERROR_INVALID_PARAMETER.
On the writing side:
std::string uses char elements, but TCHAR maps to either char or wchar_t depending on whether your code is compiled with UNICODE defined or not.
The TEXT() macro only works with compile-time literals, you can't use it with runtime data. TEXT(path.c_str()) is an invalid type-cast, and won't even compile if UNICODE is enabled.
You are clearly working with char data, so you should be using the char-based API functions instead of the TCHAR-based functions.
You are also not following one of the most important rules of RegSetValueEx():
For string-based types, such as REG_SZ, the string must be null-terminated. With the REG_MULTI_SZ data type, the string must be terminated with two null characters... The size of the information pointed to by the lpData parameter, in bytes. If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must include the size of the terminating null character or characters.
std::string::c_str() returns a pointer to null-terminated data, but you are not including the null terminator when reporting the size of the data you are writing to the Registry. RegGetValue() knows how to deal with that mistake, but RegGetValueEx() does not. You might not be the only person to ever read the value, so make sure you include the null terminator properly.
Try this instead:
std::string path = "c:\\";
LONG setRes = RegSetValueExA(*key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size()+1);
On the reading side:
You are getting errors because you are not telling RegGetValue() how large your str_data buffer is. You have to set your size variable to the size of str_data, in bytes, before you pass it in.
Try this instead:
char str_data[1028];
DWORD size = sizeof(str_data);
DWORD dwFlags = RRF_RT_REG_EXPAND_SZ;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps", "DumpFolder", RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND, NULL, str_data, &size);
Alternatively:
std:string str_data;
DWORD size = 0;
const char *sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size-1);
status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
UPDATE: your new code fails because you have introduced new bugs.
You are using legacy Registry functions that are meant for 16bit apps. You need to use RegOpenKeyEx/RegCreateKeyEx instead of RegOpenKey/RegCreateKey, and then you can specify only the specific access rights that you actually need (create subkeys, set values, read values, etc). Even better, RegCreateKeyEx() creates missing keys for you, so you don't need to manually open a parent key separately just to create a new subkey.
Also, you changed HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER, but not consistently. Some of your steps use one root, other steps use the other root. You are not able to read back the value you are writing because you are not reading from the same key you wrote to.
Try this instead:
LPCSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";
HKEY default_key;
auto status = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sk, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &default_key, NULL);
if (status == ERROR_SUCCESS)
{
std::string path = "c:\\";
status = RegSetValueExA(default_key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
RegCloseKey(default_key);
}
LPCSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
std::string str_data;
DWORD size = 0;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size - 1);
status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
On the other hand, when you have to make multiple API calls to read a value (ie, to query the size, then query the data), you should explicitly open the parent key first:
const char *sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
std:string str_data;
HKEY default_key;
auto status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, sk, 0, KEY_QUERY_VALUE, &dumps_key);
if (status == ERROR_SUCCESS)
{
DWORD size = 0;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
status = RegGetValueA(default_key, "", "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size-1);
status = RegGetValueA(default_key, "", "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
RegCloseKey(default_key);
}

Create StartMenu Entry

i try to create link to file in StartMenu folder, my code:
bool createStartMenuEntry(string targetPath, string name){
std::wstring stemp = s2ws(targetPath);
LPCWSTR target = stemp.c_str();
WCHAR startMenuPath[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, startMenuPath);
if (SUCCEEDED(result)) {
std::wstring linkPath = std::wstring(startMenuPath) + s2ws(name);
LPCWSTR link = linkPath.c_str();
//TEST MESSAGE!!!
MessageBox(NULL, LPCSTR(target), LPCSTR(link), MB_ICONWARNING);
CoInitialize(NULL);
IShellLinkW* shellLink = NULL;
result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink);
if (SUCCEEDED(result)) {
shellLink->SetPath(target);
//shellLink->SetDescription(L"Shortcut Description");
shellLink->SetIconLocation(target, 0);
IPersistFile* persistFile;
result = shellLink->QueryInterface(IID_IPersistFile, (void**)&persistFile);
if (SUCCEEDED(result)) {
result = persistFile->Save(link, TRUE);
persistFile->Release();
}
else {
return false;
}
shellLink->Release();
}
else {
return false;
}
}
else {
return false;
}
return true;
}
String to widestring conversion:
std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
When I call my func like createStartMenuEntry("E:\\file.exe" , "File"), in test message I have only first letters of path and shortcut isn't created, I think, problem in unicode conversion.
There are multiple problems here:
MessageBox(NULL, LPCSTR(target), LPCSTR(link), MB_ICONWARNING); is all kinds of wrong. You should not be casting strings like this. If you are compiling without UNICODE defined, you must use MessageBoxW() to display a LPCWSTR string. You get a single character because "c:\\" as a Unicode string is 'c',0,':',0,'\\',0,0,0 in memory, and that is the same as a "c" string when treated as a narrow ANSI string.
You ignore the result of persistFile->Save()! You also ignore the results of SetPath() and SetIconLocation().
A normal user cannot write to CSIDL_COMMON_PROGRAMS, only administrators have write access to that folder, because it is shared by all users. If you are not planning to require UAC elevation, you must write to CSIDL_PROGRAMS instead.
You should not use std::string to store paths, only std::wstring and WCHAR*/LP[C]WSTR, because paths that contain certain Unicode characters cannot be represented in a narrow ANSI string.

utf-8 to/from utf-16 problem

I based these two conversion functions and an answer on StackOverflow, but converting back-and-forth doesn't work:
std::wstring MultiByteToWideString(const char* szSrc)
{
unsigned int iSizeOfStr = MultiByteToWideChar(CP_ACP, 0, szSrc, -1, NULL, 0);
wchar_t* wszTgt = new wchar_t[iSizeOfStr];
if(!wszTgt) assert(0);
MultiByteToWideChar(CP_ACP, 0, szSrc, -1, wszTgt, iSizeOfStr);
std::wstring wstr(wszTgt);
delete(wszTgt);
return(wstr);
}
std::string WideStringToMultiByte(const wchar_t* wszSrc)
{
int iSizeOfStr = WideCharToMultiByte(CP_ACP, 0, wszSrc, -1, NULL, 0, NULL, NULL);
char* szTgt = new char[iSizeOfStr];
if(!szTgt) return(NULL);
WideCharToMultiByte(CP_ACP, 0, wszSrc, -1, szTgt, iSizeOfStr, NULL, NULL);
std::string str(szTgt);
delete(szTgt);
return(str);
}
[...]
// はてなブ in utf-16
wchar_t wTestUTF16[] = L"\u306f\u3066\u306a\u30d6\u306f\u306f";
// shows the text correctly
::MessageBoxW(NULL, wTestUTF16, L"Message", MB_OK);
// convert to UTF8, and back to UTF-16
std::string strUTF8 = WideStringToMultiByte(wTestUTF16);
std::wstring wstrUTF16 = MultiByteToWideString(strUTF8.c_str());
// this doesn't show the proper text. Should be same as first message box
::MessageBoxW(NULL, wstrUTF16.c_str(), L"Message", MB_OK);
Check the docs for WideCharToMultiByte(). CP_ACP converts using the current system code page. That's a very lossy one. You want CP_UTF8.