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(), ...);
Related
Here is an example of Unicode: I avoided to use a win32 application for for brevity sake:
In main I created an edit control, a button to retrieve text from it and adds it to the listbox when pressed. So I used an object of struct MSG and blocked in a while loop peeking the messages from the message queue.
int main(){
// An edit control where I can add any unicode text to.
HWND hEdit = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Edit", L"你好! 你好吗?", WS_BORDER | WS_VISIBLE | WS_SYSMENU,
200, 100, 250, 70, 0, 0, GetModuleHandle(NULL), NULL);
// A listobx to receive the content of the edit control when pressing the button get text.
HWND hLstBx = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Listbox", NULL, WS_BORDER | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
500, 100, 170, 400, 0, 0, GetModuleHandle(NULL), NULL);
// A button when pressed adds the content of the edit to the listbox.
HWND hGetText = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Button", L"中文", WS_BORDER | WS_VISIBLE,
450, 300, 100, 70, 0, 0, GetModuleHandle(NULL), NULL);
// msg struct to pass to GetMessage to receive the messages from the queue.
MSG msg;
// blocking and getting messages from the queue.
while (GetMessageW(&msg, 0, 0, 0)) {
// some virtual keys translation.
TranslateMessage(&msg);
// sending the message to the specified window.
DispatchMessageW(&msg);
// Now after the messages sent to the target Window I check which control the message has been passed to, So if it is the button then:
if (msg.message == WM_LBUTTONDOWN &&
msg.hwnd == hGetText) {
std::wstring wstrBuff;
int txtLen = SendMessageW(hEdit, WM_GETTEXTLENGTH, 0, 0);
// SendMessageW(hEdit, WM_GETTEXT, txtLen + 1, (LPARAM)wstrBuff.c_str());
// MessageBoxW(0, wstrBuff.c_str(), 0, 0);
wchar_t lpTxt[MAX_PATH];
SendMessageW(hEdit, WM_GETTEXT, MAX_PATH, (LPARAM)lpTxt);
SendMessageW(hLstBx, LB_ADDSTRING, 0, (LPARAM)lpTxt);
MessageBoxW(0, lpTxt, L"你好! 你好吗?", 0);
//delete[]lpTxt;
}
}
std::wcout << std::endl << std::endl;
std::wcin.get();
return 0;
}
Every thing works fine except: If I un-comment the lines above I get run-time error facing the assertion message showing me the txtLen and the size of the text of the edit control. Is this because there's some string overlapping?
If I enter a small text it works fine but with a text about 14 characters I get the error.
Also is that the right way to pass std::wstring.c_str() to SendMessageW() to get the text?
One last question: How to correctly and effectively retrieve Unicode text from a control? How to use LPWSTR with dynamic memory: I don't want to exhaust stack.
NB: I saved the source file as utf8 /BOM otherwise I get unreadable characters. Thanks to the members who helped me about that.
Sending (LPARAM)wstrBuff.c_str() will return a pointer to read-only buffer with a single null symbol, not a buffer of txtLen + 1 symbols. If you are using latest VS (with C++17 standard support) you can modify your code to supply a proper pointer:
std::wstring wstrBuff;
wstrBuff.resize(txtLen + 1);
const LRESULT copied_symbols_count = SendMessageW
(
hEdit
, WM_GETTEXT
, static_cast<WPARAM>(wstrBuff.size())
, reinterpret_cast<LPARAM>(wstrBuff.data())
);
if(copied_symbols_count == txtLen)
{
assert(L'\0' == wstrBuff.back());
wstrBuff.pop_back(); // get rid of terminating null
}
else
{
wstrBuff.clear(); // something went wrong...
}
Note that C++17 standard adds non-const-qualified wstring::data() method that can be safely used to obtain a pointer to writable buffer.
I have a vector of thousands of strings:
std::vector<std::wstring> a;
filled with some algorithms.
Following the method described here, here is how I create a ListView as a "virtual list":
hList = CreateWindowEx(0, WC_LISTVIEW, L"", WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_OWNERDATA, 0, 0, 800, 400, hWnd, (HMENU)ID_LISTVIEW, hInst, NULL);
LV_COLUMN lvcol;
...
ListView_InsertColumn(hList, 0, &lvcol);
ListView_SetItemCountEx(hList, 100000, LVSICF_NOSCROLL);
...
// in the message loop
case WM_NOTIFY:
pdi = (NMLVDISPINFO*) lParam;
pi = pdi->item;
switch (pdi->hdr.code)
{
case LVN_GETDISPINFO:
{
pi.mask = LVIF_TEXT;
pi.pszText = a[pi.iItem]; // the nth item should be the nth string in the vector
}
}
I tried a lot of variations on:
pi.pszText = a[pi.iItem];
but they all failed with such kind of errors:
Error C2440: '=' : cannot convert from 'std::basic_string,std::allocator>' to 'LPWSTR'
What could help to do this?
Note: in fact I would like to display on row n of the ListView : the nth string of vector a concatenated with the number n, like this Blabla217 on the row 217.
Note2: even after Igor's suggestion (i.e. a cast pi.pszText = LPWSTR(a[pi.iItem].c_str());), the ListView is still empty, instead of displaying elements.
I'm not exact sure about the problem you're facing, but one thing for sure, you're passing multi-byte string (std::string, using char) while it is asking for wide-char string (std::wstring, using WCHAR).
Here is a handy code that converts std::string to std::wstring.
inline std::wstring WideFromMulti(
std::string const & multi,
UINT codepage)
{
int cchWide = MultiByteToWideChar(codepage, 0, multi.c_str(), -1, nullptr, 0);
LPWSTR szWide = new wchar_t[cchWide];
MultiByteToWideChar(codepage, 0, multi.c_str(), -1, szWide, cchWide);
std::wstring wide(szWide);
delete[] szWide;
return wide;
}
inline std::wstring WideFromUtf8(
std::string const & utf8)
{
return WideFromMulti(utf8, CP_UTF8);
}
Then you can get LPCWSTR by c_str().
std::string test_str;
std::wstring test_wstr = WideFromUtf8(test_str);
LPCWSTR wszTest = test_wstr.c_str();
What about LPWSTR? Well if you're sure that the string won't get modified, you can cast it by const_cast<LPWSTR>(wszTest). If you're strongly against const_cast, you may create a temporary copy of LPWSTR like this:
std::wstring test(L"Hello world");
LPCWSTR szTestConst = test.c_str();
int cchMax = ::lstrlenW(szTestConst) + 1;
std::vector<WCHAR> v(cchMax);
::lstrcpynW(&v[0], szTestConst, cchMax);
LPWSTR szTest = &v[0];
I don't really know why, but this solved it:
case WM_NOTIFY:
pdi = (NMLVDISPINFO*) lParam;
//pi = pdi->item;
switch (pdi->hdr.code)
{
case LVN_GETDISPINFO:
{
//pi.mask = LVIF_TEXT;
pdi->item.mask = LVIF_TEXT;;
//pi.pszText = a[pi.iItem];
pdi->item.pszText = a[pi.iItem];
}
}
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);
}
I am doing an App for laptops Called Batterlizer,
It will get all the information about the battery, So everything was going well until I wanted to get the battery percent and other things , so here is my code:
SYSTEM_POWER_STATUS BatteryPower;
if(GetSystemPowerStatus( &BatteryPower ))
{
long unsigned int BatteryFull = BatteryPower.BatteryLifeTime;
const char BatteryFullTime[900] = {BatteryFull};
BatteryLeftText = CreateWindow("static", "Battery Life:",WS_CHILD |
WS_VISIBLE | WS_TABSTOP, 0, 0, 100, 20,hwnd, (HMENU)(501),(HINSTANCE)
GetWindowLong (hwnd, GWL_HINSTANCE), NULL);
UpdatedBatteryText = CreateWindow("static",BatteryFullTime, WS_VISIBLE |
WS_CHILD | WS_TABSTOP, 90, 0, 50, 20, hwnd, (HMENU)(501),(HINSTANCE)
GetWindowLong (hwnd, GWL_HINSTANCE),NULL);
}
the problem is , it says strange letters when it comes to BatteryFullTime,
Any Ideas guys?
Try the following:
char szBatteryLifeBuffer[900] = {0};
sprintf(szBatteryLifeBuffer, "%lu", BatteryFull);
SetWindowText(UpdatedBatteryText, szBatteryLifeBuffer);
// This also should work, too.
// SendMessage(UpdatedBatteryText, WM_SETTEXT, 0, (LPARAM)(LPTSTR)szBatteryLifeBuffer);
Get rid of
// This does not work.
const char BatteryFullTime[900] = {BatteryFull};
You're using C instead of C++, that makes it harder. In C++ :
std::string BatteryFullTime = std::to_string(BatteryFull);
No need to care about length, or remember details of the conversion function. However:
SetWindowText(UpdatedBatteryText, BatteryFullTime.c_str());
The Windows function does need a C string, but it's easy to get a C string from the C++ string.
i made a small textbox like this
EBX = CreateWindow(TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_NUMBER | WS_BORDER,
client.right - offset[1] - 200, client.top + offset[2] - 27,
45, 25, hwnd, (HMENU)ID_EDIT_SPEED, NULL, NULL);
everything is fine there but when i try to change the text inside like this i got some problems
SendMessage(EBX, WM_SETTEXT, 0, (LPARAM)"12"); // working
int a = 40;
SendMessage(EBX, WM_SETTEXT, 0, (LPARAM)a); // not working
any idea what is wrong ?
40 is not a string, "40" is.
If you want to convert a number to a string you must use a function like sprintf, etc.
E.g.
int a = 40;
char str[20];
StringCchPrintf(str, _countof(str), "%ld", a);
SendMessage(EBX, WM_SETTEXT, 0, (LPARAM)str);
You cannot, blindly typecast int to char*, use sprintf, stringstream or std::to_string to create string that holds literal representation of int value.
Or if you want to otput char with value 40 you need to pass pointer to null terminate array of chars. Like
char str[2];
str[0]=40;
str[1]=0;
convert 40 to c-string and use it in sendmessage function
char buffer [33];
int i =40;
itoa (i,buffer,10);
SendMessage(EBX, WM_SETTEXT, 0, (LPARAM)buffer);