C++ WinAPI. Неверное отображение текста в списке

У меня есть задача создать программу, которая отображает список дескрипторов всех окон в системе. Я получаю этот вывод:

образ

Может быть, это неправильная кодировка?

Вот мой код:

#include <windows.h>

ATOM RegMyWindowClass(HINSTANCE, LPCTSTR);
HWND hListBox;
HINSTANCE hin;

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    WCHAR str[255];
    if (GetWindowTextW(hwnd, str, 255)) {
        if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER)))
            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
    }
    return 1;
}

LRESULT CALLBACK WndProc(
    HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_LBUTTONUP:
        MessageBox(hWnd, TEXT("Вы кликнули!"), TEXT("событие"), 0);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);  
        break;
    case WM_CREATE:
        hListBox = CreateWindow("LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
            0, 0, 400, 400, hWnd, (HMENU)1111, hin, NULL);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

int APIENTRY WinMain(HINSTANCE hInstance,
    HINSTANCE         hPrevInstance,
    LPSTR             lpCmdLine,
    int               nCmdShow)
{
    LPCTSTR lpzClass = TEXT("My Window Class!");

    if (!RegMyWindowClass(hInstance, lpzClass))
        return 1;

    RECT screen_rect;
    GetWindowRect(GetDesktopWindow(), &screen_rect); 
    int x = screen_rect.right / 2 - 200;
    int y = screen_rect.bottom / 2 - 200;

    HWND hWnd = CreateWindow(lpzClass, TEXT("Window"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 400, 400, NULL, NULL,
        hInstance, NULL);
    ShowWindow(hWnd, SW_SHOW);
    EnumWindows(&EnumWindowsProc, 0);
    if (!hWnd) return 2;

    MSG msg = { 0 };   
    int iGetOk = 0;   
    while ((iGetOk = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (iGetOk == -1) return 3;  
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;  
}

ATOM RegMyWindowClass(HINSTANCE hInst, LPCTSTR lpzClassName)
{
    WNDCLASS wcWindowClass = { 0 };
    wcWindowClass.lpfnWndProc = (WNDPROC)WndProc;
    wcWindowClass.style = CS_HREDRAW | CS_VREDRAW;
    wcWindowClass.hInstance = hInst;
    wcWindowClass.lpszClassName = lpzClassName;
    wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
    return RegisterClass(&wcWindowClass); 
}

Любые идеи, как это исправить?

1 ответ

Решение

Проблема в том, что вы компилируете свой проект для ANSI, где TCHAR это псевдоним для CHARи, таким образом, создаем ListBox на основе ANSI, но вы отправляете в него строки Unicode. Вот почему вы видите мусор на выходе. Вам необходимо отправить строки ANSI в ListBox, например:

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER)))
        CHAR str[255] = {};
        if (GetWindowTextA(hwnd, str, 255)) {
            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
        }
    }
    return TRUE;
}

В качестве альтернативы:

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER))) {
        CHAR str[255] = {};
        if (IsWindowUnicode(hwnd)) {
            WCHAR wstr[255] = {}; 
            int len = GetWindowTextW(hwnd, wstr, 255);
            if (len) {
                len = WideCharToMultiByte(CP_ACP, 0, wstr, len+1, str, 255, NULL, NULL);
            }
            if (!len) {
                return TRUE;
            }
        }
        else if (!GetWindowTextA(hwnd, str, 255)) {
            return TRUE;
        }
        SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
    }
    return TRUE;
}

При этом вы смешиваете ANSI, Unicode и TCHAR API-интерфейсы. Вам нужно выбрать один стиль API и придерживаться его для всего, не смешивайте их (если только вы не АБСОЛЮТНО должны это делать).

Поскольку большая часть кода, который вы показали, уже использует TCHARтогда вы можете использовать TCHAR для всего (хотя, вы действительно не должны, так как TCHAR предназначен для обратной совместимости с Win9x/ME, который больше никто не использует, и предназначен только для того, чтобы помочь людям перенести их код в Unicode. Современный код не должен использовать TCHAR совсем):

#include <windows.h>

ATOM RegMyWindowClass(HINSTANCE, LPCTSTR);
HWND hListBox;
HINSTANCE hin;

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER))) {
        TCHAR str[255];
        if (GetWindowText(hwnd, str, 255)) {
            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
        }
    }
    return TRUE;
}

LRESULT CALLBACK WndProc(
    HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_LBUTTONUP:
        // this is one case where it doesn't make sense to use TCHAR
        MessageBoxW(hWnd, L"Вы кликнули!", L"событие", 0);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);  
        break;
    case WM_CREATE:
        hListBox = CreateWindow(TEXT("LISTBOX"), TEXT(""), WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
            0, 0, 400, 400, hWnd, (HMENU)1111, hin, NULL);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

int APIENTRY WinMain(HINSTANCE hInstance,
    HINSTANCE         hPrevInstance,
    LPSTR             lpCmdLine,
    int               nCmdShow)
{
    LPCTSTR lpzClass = TEXT("My Window Class!");

    if (!RegMyWindowClass(hInstance, lpzClass))
        return 1;

    RECT screen_rect;
    GetWindowRect(GetDesktopWindow(), &screen_rect); 
    int x = screen_rect.right / 2 - 200;
    int y = screen_rect.bottom / 2 - 200;

    HWND hWnd = CreateWindow(lpzClass, TEXT("Window"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 400, 400, NULL, NULL,
        hInstance, NULL);
    ShowWindow(hWnd, SW_SHOW);
    EnumWindows(&EnumWindowsProc, 0);
    if (!hWnd) return 2;

    MSG msg = { 0 };   
    int iGetOk = 0;   
    while ((iGetOk = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (iGetOk == -1) return 3;  
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;  
}

ATOM RegMyWindowClass(HINSTANCE hInst, LPCTSTR lpzClassName)
{
    WNDCLASS wcWindowClass = { 0 };
    wcWindowClass.lpfnWndProc = &WndProc;
    wcWindowClass.style = CS_HREDRAW | CS_VREDRAW;
    wcWindowClass.hInstance = hInst;
    wcWindowClass.lpszClassName = lpzClassName;
    wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
    return RegisterClass(&wcWindowClass); 
}

В противном случае используйте Unicode для всего:

#include <windows.h>

ATOM RegMyWindowClass(HINSTANCE, LPCWSTR);
HWND hListBox;
HINSTANCE hin;

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER))) {
        WCHAR str[255];
        if (GetWindowTextW(hwnd, str, 255)) {
            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
        }
    }
    return TRUE;
}

LRESULT CALLBACK WndProc(
    HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_LBUTTONUP:
        MessageBoxW(hWnd, L"Вы кликнули!", L"событие", 0);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);  
        break;
    case WM_CREATE:
        hListBox = CreateWindowW(L"LISTBOX", L"", WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
            0, 0, 400, 400, hWnd, (HMENU)1111, hin, NULL);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

int APIENTRY WinMain(HINSTANCE hInstance,
    HINSTANCE         hPrevInstance,
    LPSTR             lpCmdLine,
    int               nCmdShow)
{
    LPCWSTR lpzClass = L"My Window Class!";

    if (!RegMyWindowClass(hInstance, lpzClass))
        return 1;

    RECT screen_rect;
    GetWindowRect(GetDesktopWindow(), &screen_rect); 
    int x = screen_rect.right / 2 - 200;
    int y = screen_rect.bottom / 2 - 200;

    HWND hWnd = CreateWindowW(lpzClass, L"Window",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 400, 400, NULL, NULL,
        hInstance, NULL);
    ShowWindow(hWnd, SW_SHOW);
    EnumWindows(&EnumWindowsProc, 0);
    if (!hWnd) return 2;

    MSG msg = { 0 };   
    int iGetOk = 0;   
    while ((iGetOk = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (iGetOk == -1) return 3;  
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;  
}

ATOM RegMyWindowClass(HINSTANCE hInst, LPCWSTR lpzClassName)
{
    WNDCLASSW wcWindowClass = { 0 };
    wcWindowClass.lpfnWndProc = &WndProc;
    wcWindowClass.style = CS_HREDRAW | CS_VREDRAW;
    wcWindowClass.hInstance = hInst;
    wcWindowClass.lpszClassName = lpzClassName;
    wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
    return RegisterClassW(&wcWindowClass); 
}

Или придерживайтесь ANSI для всего, если вам нужно поддерживать совместимость с существующей логикой кода в другом месте вашего проекта:

#include <windows.h>

ATOM RegMyWindowClass(HINSTANCE, LPCSTR);
HWND hListBox;
HINSTANCE hin;

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    if (IsWindowVisible(hwnd) && (!GetWindow(hwnd, GW_OWNER))) {
        CHAR str[255];
        if (GetWindowTextA(hwnd, str, 255)) {
            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)str);
        }
    }
    return TRUE;
}

LRESULT CALLBACK WndProc(
    HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_LBUTTONUP:
        // this is one case where it doesn't make sense to use ANSI
        MessageBoxW(hWnd, L"Вы кликнули!", L"событие", 0);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);  
        break;
    case WM_CREATE:
        hListBox = CreateWindowA("LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
            0, 0, 400, 400, hWnd, (HMENU)1111, hin, NULL);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

int APIENTRY WinMain(HINSTANCE hInstance,
    HINSTANCE         hPrevInstance,
    LPSTR             lpCmdLine,
    int               nCmdShow)
{
    LPCSTR lpzClass = "My Window Class!";

    if (!RegMyWindowClass(hInstance, lpzClass))
        return 1;

    RECT screen_rect;
    GetWindowRect(GetDesktopWindow(), &screen_rect); 
    int x = screen_rect.right / 2 - 200;
    int y = screen_rect.bottom / 2 - 200;

    HWND hWnd = CreateWindowA(lpzClass, "Window",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, 400, 400, NULL, NULL,
        hInstance, NULL);
    ShowWindow(hWnd, SW_SHOW);
    EnumWindows(&EnumWindowsProc, 0);
    if (!hWnd) return 2;

    MSG msg = { 0 };   
    int iGetOk = 0;   
    while ((iGetOk = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (iGetOk == -1) return 3;  
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;  
}

ATOM RegMyWindowClass(HINSTANCE hInst, LPCSTR lpzClassName)
{
    WNDCLASSA wcWindowClass = { 0 };
    wcWindowClass.lpfnWndProc = &WndProc;
    wcWindowClass.style = CS_HREDRAW | CS_VREDRAW;
    wcWindowClass.hInstance = hInst;
    wcWindowClass.lpszClassName = lpzClassName;
    wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
    return RegisterClassA(&wcWindowClass); 
}
Другие вопросы по тегам