Converting CStringW to CString resulting question mark - c++

My vc++ project's property "character set" is set to "Use Multi-Byte Character", I am converting CStringW (Chinese language string) into CString and it's converting the Chinese characters into question marks.
CStringA utf8;
int cc=0;
// get length (cc) of the new multibyte string excluding the \0 terminator first
if ((cc = WideCharToMultiByte(CP_UTF8, 0, stringw, -1, NULL, 0, 0, 0) - 1) > 0)
{
// convert
char *buf = utf8.GetBuffer(cc);
if (buf) WideCharToMultiByte(CP_UTF8, 0, stringw, -1, buf, cc, 0, 0);
utf8.ReleaseBuffer();
Please help, I'm new in c++.

The conversion is working correctly, you can send the UTF-8 result to another program which requires UTF-8, or test it in a web browser, or test it in Notepad with UTF-8 encoding.
The only problem you may face is that Windows API cannot directly display UTF-8 string, so it may look as if it failed. For example, ::MessageBoxA(0, utf8, 0, 0) will show the wrong result.
To see if the conversion worked, you can convert back to UTF-16. For example:
#include <atlstr.h>
...
CStringW utf16 = L"汉字 / 漢字";
CStringA utf8 = CW2A(utf16, CP_UTF8);
CStringW copy = CA2W(utf8, CP_UTF8);
if (copy == utf16)
::MessageBoxW(0, copy, L"success", 0);
else
::MessageBoxW(0, copy, L"failed", 0);
New Windows programs should be in Unicode (UTF-16). Note that Multi-byte and ANSI are deprecated.
To use Multi-Byte in an old program, set code page to CP_ACP or another language. For example, WideCharToMultiByte(CP_ACP, ...). However this depends on codepage for the current thread and is not reliable.
#include <iostream>
#include <atlstr.h>
#include <Windows.h>
int main()
{
//ANSI codepage for Greek language:
int codepage = 1253;
CStringW utf16 = L"ελληνική";
CStringA ansi = CW2A(utf16, codepage);
SetConsoleOutputCP(codepage);
//This should appear correctly
std::cout << ansi << "\n";
//This won't show correctly in English language computer
MessageBoxA(0, ansi, 0, 0);
return 0;
}
Lastly, if project is not Unicode, and for some reason you cannot change it to Unicode, you still have access to Unicode API's (for example MessageBoxW) so you can patch the old program. For example, instead of using CTooltipCtrl::AddTool, use the equivalent WinAPI code which exposes Unicode functions:
CWnd *button = GetDlgItem(IDOK);
if (button)
{
TOOLINFOW ti;
memset(&ti, 0, sizeof(ti));
ti.cbSize = sizeof(ti);
ti.hwnd = ::GetParent(button->m_hWnd);
ti.uFlags = TTF_IDISHWND;
ti.uId = (UINT_PTR)button->m_hWnd;
ti.lpszText = L"汉字 / 漢字";
ToolTip->SendMessage(TTM_ADDTOOLW, 0, (LPARAM)&ti);
}

Related

Getting weird characters from conversion of char to TCHAR

I'm getting really weird characters from the conversion. Any idea why? I get the "data" from an external device and i need to display it to win32 GUI. It had no problem when i
printf("%s\n",data);
in the console mode but having trouble when i migrate it to win32 which requires me to convert to TCHAR to display.
CHAR data[256];
TCHAR data1[256];
MultiByteToWideChar(CP_ACP,MB_COMPOSITE,data,-1,data1,0);
CreateWindow(TEXT("STATIC"), data1, WS_VISIBLE | WS_CHILD |
10, 50,300,300,hWnd, (HMENU) none, NULL, NULL);
By the way, using
hDLL=LoadLibrary("MyKad.dll");
in win32 couldn't work so I had to used
hDLL=LoadLibrary(TEXT("MyKad.dll"));
May I know is this right? Thanks
The reason that your code fails is that you pass 0 in the final parameter of MultiByteToWideChar. You can fix your code by passing the length if data1:
MultiByteToWideChar(CP_ACP, MB_COMPOSITE, data, -1, data1, 256);
Note also that you should be checking for errors when calling API functions. Had you done so you would have discovered that MultiByteToWideChar was failing.
I use the following function to convert to UTF-16:
std::wstring MultiByteStringToWideString(const std::string& MultiByte,
const UINT CodePage)
{
std::wstring result;
int cchWideChar = MultiByteToWideChar(CodePage, 0, MultiByte.c_str(), -1,
NULL, 0);
if (cchWideChar > 0)
{
wchar_t* bufferW = new wchar_t[cchWideChar];
if (MultiByteToWideChar(CodePage, 0, MultiByte.c_str(), -1, bufferW,
cchWideChar) != 0)
{
result = std::wstring(bufferW);
}
delete[] bufferW;
}
return result;
}
So you could use this as follows:
std::wstring windowName = MultiByteStringToWideString(data, CP_ACP);
CreateWindow(L"STATIC", windowName.c_str(), ...);

Output LRESULT to console

I'm trying to output text from Notepad window to console and it's always 0.
What I'm doing wrong?
int main()
{
HWND hwnd = (HWND)0x0031019C; // Window Handler of Notepad
char szBuf[4096];
HWND hwndEdit;
LRESULT result;
hwndEdit = FindWindowEx(hwnd, NULL, L"Edit", NULL); // Class for edit box
result = SendMessage(hwndEdit, WM_GETTEXT, sizeof(szBuf) / sizeof(szBuf[0]), (LPARAM)szBuf);
cout<<"Contents: \n"<<result;
cin.get();
return 0;
}
I tried print_f, but it outputs unreadable characters:
printf( "Contents: %s\n", result, szBuf );
It looks to me like you probably have a little bit of a mismatch happening.
Based on the L"Edit", you seem to be doing a Unicode build (otherwise, you'd get an error message about not being able to convert an wchar_t const[5] to LPCSTR, and the code wouldn't compile.
If you do a Unicode build, however, WM_GETTEXT is going to write Unicode data to your buffer, so you need to prepare for and use Unicode instead of narrow characters for your buffer.
For convenience, I've modified it a little to find Notepad instead of using a hard-coded Window handle.
#include <windows.h>
#include <stdio.h>
#define elements(b) (sizeof(b)/sizeof(b[0]))
int main() {
HWND hwnd; // Window Handler of Notepad
wchar_t buf[4096]={0};
HWND hwndEdit;
LRESULT result;
hwnd=FindWindowEx(NULL, NULL, L"Notepad", NULL);
hwndEdit=FindWindowEx(hwnd, NULL, L"Edit", NULL); // Class for edit box
result = SendMessage(hwndEdit, WM_GETTEXT, elements(buf), (LPARAM)buf);
printf("%S", buf);
return 0;
}
I built with:
cl /DUNICODE whatever.cpp user32.lib
Then I did a quick test that printed out exactly the text I'd typed into Notepad. To verify the result, I then edited the text in notepad, ran it again, and it printed out the modified text.

c++/win32 - LB_GETTEXT only returns one character

HWND listBox = GetDlgItem(hDlg, IDC_SCORES_LIST);
LRESULT r = SendMessage (listBox , LB_GETCURSEL, 0, 0);
LRESULT l = SendMessage (listBox , LB_GETTEXTLEN, (WPARAM)r, 0);
char* text = new char [l +1];
SendMessage (listBox, LB_GETTEXT, (WPARAM)r, (LPARAM)text);
The result of this code should be that something like "Level 2" is stored in "text". However for some reason at the end of this, only "L" is stored in "text". I'm racking my brains for what could be causing this, but I can't think of anything.
You are probably compiling in UNICODE, so the functions you are calling operate with UTF-16 text. You should use wchar_t instead of char, or even better, the TCHAR macro (which maps to either wchar_t of char depending on whether you compile in UNICODE or ANSI).

C++/Win32 Finding all keyboard input languages?

I want to find all the input languages for the keyboard, the ones that you switch between with LEFT ALT + SHIFT.
I can get the default locale and the installed/supported locales with win API but I could not find anywhere anything about the input locales for the keyboard.
You have to use GetKeyboardLayoutList function.
For example, to output in console all keyboard input languages you can use this code:
UINT uLayouts;
HKL *lpList = NULL;
wchar_t szBuf[512];
uLayouts = GetKeyboardLayoutList(0, NULL);
lpList = (HKL*)LocalAlloc(LPTR, (uLayouts * sizeof(HKL)));
uLayouts = GetKeyboardLayoutList(uLayouts, lpList);
for(int i = 0; i < uLayouts; ++i)
{
GetLocaleInfo(MAKELCID(((UINT)lpList[i] & 0xffffffff),
SORT_DEFAULT), LOCALE_SLANGUAGE, szBuf, 512);
wprintf(L"%s\n", szBuf);
memset(szBuf, 0, 512);
}
if(lpList)
LocalFree(lpList);

CRichEditCtrl::GetSelText() is not working right

MFC File: winctrl4.cpp
(C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc)
CString CRichEditCtrl::GetSelText() const
{
ASSERT(::IsWindow(m_hWnd));
CHARRANGE cr;
cr.cpMin = cr.cpMax = 0;
::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);
CStringA strText;
LPSTR lpsz=strText.GetBufferSetLength((cr.cpMax - cr.cpMin + 1)*2);
lpsz[0] = NULL;
::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpsz);
strText.ReleaseBuffer();
return CString(strText);
}
I am having a weird problem, when I call this it only returns the first character of the selected string. cr is correctly being set but after ::SendMessage(m_hWnd, EM_GETSELTEXT,... the whole string is not present.
I saw similar behavior in my custom code due to WCHAR issues (two-byte character containing a zero in one byte) when CHAR was expected. But this is part of MFC/Win32! Is it possible my .rc file sets something wrong? Is there a Create style relating to this? Or since we create a CFont for the control in question, could that screw it up?
This is not the correct MFC source code, have you edited it? Using CStringA and LPSTR is quite inappropriate, the real code uses CString and LPTSTR so that Unicode is correctly handled. Yes, as posted the code would only return one character.
Seeing the version helped. The bug is described in this feedback article. If you can't reasonably upgrade to VS2008 SP1, you could derive your own class from CRichEditCtrl and replace the function. For example:
CString CRichEditCtrlFix::GetSelText() const
{
ASSERT(::IsWindow(m_hWnd));
CHARRANGE cr;
cr.cpMin = cr.cpMax = 0;
::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);
CString strText;
LPTSTR lpsz=strText.GetBufferSetLength((cr.cpMax - cr.cpMin + 1) * 2);
lpsz[0] = NULL;
::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpsz);
strText.ReleaseBuffer();
return CString(strText);
}
To get a wide char string you have to use the EM_GETTEXTEX message. CRichEditCtrl source does not contain a method which utilizes such message.
Here is a correct implementation of GetSelText() which actually does return Unicode characters:
CString CRichEditCtrlFix::GetSelText() const
{
ASSERT(::IsWindow(m_hWnd));
CHARRANGE cr;
cr.cpMin = cr.cpMax = 0;
::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);
CString strText;
int sz = (cr.cpMax - cr.cpMin + 1) * sizeof(tchar);
LPTSTR lpsz = strText.GetBufferSetLength(sz);
lpsz[0] = NULL;
GETTEXTEX gte;
memset( &gte, 0, sizeof(GETTEXTEX) );
gte.cb = sz;
gte.flags = GT_SELECTION;
if( sizeof(tchar) == 2 ) gte.codepage = 1200;
::SendMessage(m_hWnd, EM_GETTEXTEX, (WPARAM)&gte, (LPARAM)lpsz);
strText.ReleaseBuffer();
return CString(strText);
}
1200 here means UTF-16LE