c++winapi

c++ winapi Combo box displaying oriental chars


Let me preface by saying I have tried to find a solution, seen similar queries but, being new to win32, failed to transport those answers into my own problem. I also noticed that every answer seems to have a common root for the issue (unicode) but slightly different technical solutions.

This is an old game institute tutorial. Objective is to create a dialog(1), which contains a combo box, to be populated via a text box for the user to enter a bit of text. Selecting items from the combo box displays a new dialog(2) showing the contents of the selected item. Everything works, bar the display of the items in the combo box, they don;t display correctly instead displaying oriental chars. Text in dialog (2) displays correctly.

#include <windows.h>
#include <string>
#include "resource.h"
using namespace std;
// Dialog handle.
HWND ghDlg = 0;
// Dialog window procedure.
INT_PTR CALLBACK
MsgDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // Text buffer to be filled with string user entered
    // into edit control.
    char msgText[256];
    // Handles to the combo box controls.
    static HWND hComboBox = 0;
    static HWND hEditBox = 0;
    static HWND hAddButton = 0;
    int index = 0;
    switch (msg)
    {
    case WM_INITDIALOG:
        // Controls are child windows to the dialog they lie on.
        // In order to get and send information to and from a
        // control we will need a handle to it. So save a handle
        // to the controls as the dialog is being initialized.
        // Recall that we get a handle to a child control on a
        // dialog box with the GetDlgItem.
        hComboBox = GetDlgItem(hDlg, IDC_COMBOBOX);
        hEditBox = GetDlgItem(hDlg, IDC_EDIT_MSG);
        hAddButton = GetDlgItem(hDlg, IDC_ADDBUTTON);
        return true;
    case WM_COMMAND:
        switch (HIWORD(wParam))
        {
            // User selected a combo box item.
        case CBN_SELENDOK:
            index = SendMessage(hComboBox, CB_GETCURSEL, 0, 0);
            SendMessage(hComboBox, CB_GETLBTEXT, (WPARAM)index,
                (LPARAM)msgText);
            MessageBoxA(0, msgText, "Combo Message", MB_OK);
            return true;
        }
        switch (LOWORD(wParam))
        {
            // User pressed the "Add" button.
        case IDC_ADDBUTTON:
            // Get the text from the edit box.
            GetWindowTextA(hEditBox, msgText, 256);
            // Add the text to the combo box only if the
            // user entered a string that is greater than zero.
            if (strlen(msgText) > 0)
                SendMessage(
                    hComboBox,
                    CB_ADDSTRING,
                    0,
                    (LPARAM)msgText);
            return true;
        }
        return true;
    case WM_CLOSE:
        DestroyWindow(hDlg);
        return true;
    case WM_DESTROY:
        PostQuitMessage(0);
        return true;
    }
    return false;
}
int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR cmdLine, int showCmd)
{
    // Create the modeless dialog window.
    ghDlg = CreateDialog(
        hInstance, // Application instance.
        MAKEINTRESOURCE(IDD_COMBODLG), // Dialog resource ID.
        0, // Parent window--null for no parent.
        MsgDlgProc); // Dialog window procedure.
    // Show the dialog.
    ShowWindow(ghDlg, showCmd);
    // Enter the message loop.
    MSG msg;
    ZeroMemory(&msg, sizeof(MSG));
    while (GetMessage(&msg, 0, 0, 0))
    {
        // Is the message a dialog message? If so the function
// IsDialogMessage will return true and then dispatch
// the message to the dialog window procedure.
// Otherwise, we process as the message as normal.
        if (ghDlg == 0 || !IsDialogMessage(ghDlg, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}

Solution

  • 潍楪慢敫

    This is Mojibake for "Mojibake" when its ASCII, ANSI, or UTF-8 encoding is interpreted as UTF-16LE. This should look familiar.

    The (unintended) transliteration is instigated here:

    char msgText[256];
    GetWindowTextA(hEditBox, msgText, 256);
    if (strlen(msgText) > 0)
        SendMessage(hComboBox, CB_ADDSTRING, 0, (LPARAM)msgText);
    

    GetWindowTextA instructs the system to return the "window text" and (optionally) convert it to the system default ANSI code page encoding (CP_ACP). The SendMessage preprocessor macro expands to SendMessageW (assuming that UNICODE is defined), and subsequently fools the recipient into interpreting the lParam argument as UTF-16-encoded.

    This is how to generate Mojiabke (and a potential buffer overrun vulnerability on the side).

    The "least amount of keystrokes required to address the issue" solution would be to replace SendMessage with SendMessageA. A more rational approach is to acknowledge, that UTF-16 is the internal character encoding pretty much everywhere. The following is both correct as well as computationally cheaper:

    wchar_t msgText[256] = {};
    if (::GetWindowTextW(hEditBox, msgText, std::size(msgText)) > 0)
        ::SendMessageW(hComboBox, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(msgText));