с++ WINAPI Edit text работает очень криво

211
08 мая 2018, 00:15

Начал изучать winapi в c++ написал простенькую программку которая меняет название окна на то что мы написали в текст боксе:

#include <Windows.h>
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
void AddMenu(HWND);
void AddControl(HWND);

HWND hEdit;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrewInst, LPSTR args, int ncmdshow)
{
    WNDCLASSW wc = { 0 };
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hInstance = hInst;
    wc.lpszClassName = L"class";
    wc.lpfnWndProc = WindowProcedure;
    if (!RegisterClassW(&wc))
        return -1;
    CreateWindowW(L"class", L"Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 500, 500, NULL, NULL, NULL, NULL);
    MSG msg = { 0 };
    while (GetMessage(&msg, NULL, NULL, NULL))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
    case WM_COMMAND:
        switch (wp)
        {
        case 1:
            MessageBeep(MB_SERVICE_NOTIFICATION); break;
        case 2:
            DestroyWindow(hWnd); break;
        case 3:
            wchar_t text[100];
            GetWindowTextW(hEdit, text, 100);
            SetWindowTextW(hWnd, text);
            break;
        }
    case WM_CREATE:
        AddMenu(hWnd);
        AddControl(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProcW(hWnd, msg, wp, lp);
    }
}
void AddMenu(HWND hWnd)
{
    HMENU hMenu;
    hMenu = CreateMenu();
    HMENU file;
    file = CreateMenu();
    HMENU Sub;
    Sub = CreateMenu();
    AppendMenu(hMenu, MF_POPUP, (UINT_PTR)file, "File");
    AppendMenu(file, MF_STRING, 1, "Open");
    AppendMenu(file, MF_POPUP, (UINT_PTR)Sub, "Sub");
    AppendMenu(file, MF_STRING, 2, "Exit");
    AppendMenu(Sub, MF_STRING, 3, "Change");
    SetMenu(hWnd, hMenu);
}
void AddControl(HWND hWnd)
{
    CreateWindowW(L"Static", L"Enter text: ", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER, 200, 100, 100, 20, hWnd, NULL, NULL, NULL);
    hEdit = CreateWindowW(L"Edit", L"...", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL, 200, 152, 100, 50, hWnd, NULL, NULL, NULL);
}

По дефолту в текст боксе стоит текст ... который потом можно изменить. По идее я должен изменить текст и в менюшке сверху выбрать File -> Sub -> Change и он должен изменить название моего окна на то что было в текс боксе. Проблема в том что он ничего не изменяет если я в нем что то пишу и просто остаются теже 3 точки. Но как только выделю эти три точки, сразу появится текст который программа почему то не видит(то есть я меняю название окна но оно меняется не на то что я написал а на три точки).

Однако если из описания этого текст бокса убрать | ES_MULTILINE | ES_AUTOVSCROLL то текст редактируется уже нормально, НО меню сверху вообще пропадет будто там его и не было

В чем ошибка у меня, как это пофиксить?

Answer 1

Ваша программа страдает он неопределенного поведения, так как в функция WindowProcedure возвращает мусор при обработке всех сообщений вне блока default.

Обработка сообщения WM_COMMAND тоже неверная. в wparam передается два значения - нижние два байта содержат идентификатор контрола, а верхние два байта содержат код оповещения. Вам надо обрабатывать оповещение EN_CHANGE.

Кроме того, следует проверять возвращаемые значения при вызове системных функций. Сейчас у вас все потенциальные ошибки молча игнорируются.

Answer 2

В примере неправильно обрабатываются сообщения в оконном обработчике WindowProcedure() и в этом корень зла.

Во-первых, он всегда должен возвращать определенное значение (индивидуальное для каждого сообщения), которое интерпретируется системой соответствующим образом. Смотрите MSDN для каждого сообщения, чтобы понимать смысл возвращаемого значения. В примере добавьте return 0; перед выходом из обработчика, чтобы было вот так:

    ...
    default: return DefWindowProcW(hWnd, msg, wp, lp);
    }
    return 0;
}

Во-вторых, ваша секция WM_COMMAND не заканчивается бриком (break;). Те break, которые в ней имеются, относятся ко вложенным в нее switch / case. Следовательно, все сообщения WM_COMMAND продолжают выполняться в секции WM_CREATE. Это надо исправить: смотрим в MSDN, что возвращает WM_COMMAND. Оно должно возвращать 0, если сообщение было обработано программой. Поэтому в конце WM_COMMAND мы можем поставить break; для всех обработанных сообщений (переход на добавленный ранее return 0 за внешним switch). Получится так:

    ...
    case 3:
        wchar_t text[100];
        GetWindowTextW(hEdit, text, 100);
        SetWindowTextW(hWnd, text);
        break;
    }
    break;
case WM_CREATE:
    ...

В-третьих, в отношении WM_COMMAND в MSDN читаем, что это сообщение комбинированное, может отправляться от меню, от акселераторов и от дочерних элементов управления. Текущий код этого не учитывает. Если lParam содержит 0, то сообщение WM_COMMAND пришло от меню (при HIWORD(wParam)=0) или от акселератора (при HIWORD(wParam)=1). Если lParam не равно 0, то сообщение пришло от дочернего элемента управления. Поэтому часто WM_COMMAND обрабатывают так:

case WM_COMMAND:
    if (lParam)
    {
        // Обработка уведомлений от элементов управления
        ...
        // завершение обработки (break; или return 0; или return DefWindowProcW(hWnd, msg, wp, lp); в зависимости от смысла результата) 
    }
    // Обработка наших команд меню и акселераторов (если они есть):
    switch (LOWORD(wParam))
    {
        ...
    }
    break; // переход на `return 0`, т.е. возврат кода "сообщение обработано"

В вашем примере, если нет намерений обрабатывать уведомления от элементов управления, а только сообщения от меню, добавьте сразу после case WM_COMMAND:

if (lParam) return DefWindowProcW(hWnd, msg, wp, lp);

Этой строкой Вы будете передавать все уведомления от дочерних элементов на обработку по умолчанию.

READ ALSO
Реализвация личных сообщений Qt c++

Реализвация личных сообщений Qt c++

Есть чат, на qtcpserver и qtcpsocket соответственно, клиент нажимает отправить сообщение видно всем, как реализовать сообщения отдельному пользователю?

196
Конструктор копирования

Конструктор копирования

Почему не сработал конструктор копирования?

241
Инициализация класса

Инициализация класса

В чем разница между способами инициализации?

189
C++ передача списка чисел

C++ передача списка чисел

Нужно передавать функции или конструктору список чисел определенного типа, например, int, uint8_t или uint32_tКак это сделать просто, производительно...

201