Начал изучать 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
то текст редактируется уже нормально, НО меню сверху вообще пропадет будто там его и не было
В чем ошибка у меня, как это пофиксить?
Ваша программа страдает он неопределенного поведения, так как в функция WindowProcedure
возвращает мусор при обработке всех сообщений вне блока default
.
Обработка сообщения WM_COMMAND
тоже неверная. в wparam
передается два значения - нижние два байта содержат идентификатор контрола, а верхние два байта содержат код оповещения. Вам надо обрабатывать оповещение EN_CHANGE
.
Кроме того, следует проверять возвращаемые значения при вызове системных функций. Сейчас у вас все потенциальные ошибки молча игнорируются.
В примере неправильно обрабатываются сообщения в оконном обработчике 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);
Этой строкой Вы будете передавать все уведомления от дочерних элементов на обработку по умолчанию.
Есть чат, на qtcpserver и qtcpsocket соответственно, клиент нажимает отправить сообщение видно всем, как реализовать сообщения отдельному пользователю?
Нужно передавать функции или конструктору список чисел определенного типа, например, int, uint8_t или uint32_tКак это сделать просто, производительно...