Wparam lparam что это
Перейти к содержимому

Wparam lparam что это

  • автор:

Глава 3. Обработка сообщений Windows

Как объяснялось в главе 2, Windows взаимодействует с приложениями, направляя им сообщения. Вы узнали, как строить каркас приложения Windows. Здесь каркас программы будет расширен с целью иллюстрации обработки некоторых часто используемых сообщений.

3.1. Что такое сообщения?

В Windows существует множество сообщений. Каждое из них представляется уникальным 32-разрядным целым числом. В файле Windows.h определены стандар­тные макроимена для всех возможных сообщений. В общем случае для идентифика­ции сообщений всегда используются макроимена, а не числовые значения. Вот некоторые широко используемые в Windows макроимена сообщений:

Каждое сообщение имеет два параметра, которые содержат дополнительную информацию, зависящую от типа сообщения. Один из этих параметров имеет тип WPARAM, другой – LPARAM. В Windows оба этих типа представляют собой 32-разрядные целые. Для этих параметров в программах чаще всего используются имена соответственно wParam и lParam. По мере изучения различных сообщений будет объясняться и значение этих параметров для каждого конкретного сообщения.

Как отмечалось в главе 2, обработку сообщений в программе должна выполнять оконная функция. Эта функция имеет четыре параметра: дескриптор окна, которому направляется сообщение, собственно сообщение, и, наконец, wParam и lParam.

Иногда информация, содержащаяся в wParam и lParam, может состоять из двух частей, которые размещаются в двух 16-разрядных словах, составляющих каждый из этих параметров. Для обеспечения простого доступа к каждой части wParam и lParam в Windows определены два макроса – LOWORD и HIWORD. Они возвращают соответственно старшее и младшее слова длинного целого и используются так:

3.2. Обработка нажатая клавиш

Одно из широко используемых в Windows сообщений порождается при нажатии клавиши. Это сообщение называется WM_CHAR. Следует отметить, что Ваше приложение никогда не получает сигналы и коды клавиш непосредственно от клавиатуры. Вместо них при нажатии клавиши активное окно получает сообщение WM_CHAR. Чтобы показать, как этот процесс работает, мы расширим здесь каркас­ную программу из главы 2 так, что она сможет обрабатывать клавиатурные сообщения.

Для сообщений WM_CHAR параметр wParam содержит ASCII-код нажатой клавиши. LOWORD(lParam) содержит количество повторов, генерируемых при удерживании клавиши в нажатом положении.

HIWORD(lParam) представляет собой битовую карту со следующими значениями битов, структура которой показана в таблице 3.1.

Таблица 3.1

Битовая карта lParam

Равен 1, если клавиша отпущена, и 0, если она нажата

Устанавливается, если клавиша уже была нажата перед посылкой сообщения

Устанавливается в 1, если дополнительно нажата клавиша [Alt]

Используется операционной системой

Устанавливается в 1, если нажата клавиша функциональной или допол­нительной части клавиатуры

Код клавиши (scan-код)

В данном случае для нас важно только значение wParam, поскольку оно содержит собственно вводимый символ. Однако заметьте, насколько подробную информацию о состоянии системы предоставляет Windows. Естественно, Вы можете использо­вать ту часть этой информации, которая Вас интересует.

Чтобы обработать сообщения WM_CHAR, нужно добавить его в оператор switch в функции окна. Приведем оконную функцию, которая обрабатывает ввод символов с клавиатуры и отображает эти символы на экране:

char str[80] = «»; // Буфер для строки вывода

LRESULT CALLBACK WindowFunc (HWND hwnd,

case WM_CHAR: // Обработка нажатия клавиши

hdc=GetDC(hwnd); // Для получ. контекста устр-ва

TextOut(hdc, 1, 1, » «,2); //Стереть старый код

// новый символ в выходной буфер

TextOut (hdc, 1, 1, str, strlen(str)); // Вывод

ReleaseDC (hwnd, hdc); // Освободить контекст

case WM_DESTROY: // Завершение программы

// Все сообщения, не обрабатываемые в данной

// функции, направляются на обработку

return DefWindowProc (hwnd, message,

Вас может удивить тот факт, что для такой простой задачи, как отображение введенного символа, требуется так много операторов. Объясняется это тем, что для вывода на экран Windows должен установить связь между Вашей программой и экраном. Такая связь устанавливается путем создания и получения в программе контекста устройства (Device Context, DC),что происходит при вызове функции GetDC(). В данный момент не важно, что представляет собой контекст устройства, – это будет рассматриваться в следующем разделе. Однако программа может вывести что-либо на экран только получив контекст устройства, а по окончании процесса вывода программа должна этот контекст освободить, вызвав ReleaseDC(). Одновре­менно в Windows может существовать конечное множество контекстов устройств, так что если программа не освобождает DC, у Windows вскоре не останется свободных ресурсов для предоставления DC и очередной вызов GetDC() вернет NULL.

Функции API GetDC() и ReleaseDC() имеют следующие прототипы:

HDC GetDCfHWND hwnd);

int ReleaseDC(HWND hwnd, HDC hdc);

GetDC() возвращает контекст устройства, ассоциированный с окном, дескриптор которого задается параметром hwnd. Тип HDC означает дескриптор контекста устройства.

ReleaseDC() возвращает ненулевое значение, если контекст устройства действительно был освобожден. Параметр hwnd задает дескриптор окна, ассоциированного с освобождаемым DC, a hdc – дескриптор освобожденного DC.

Функция API, выводящая символы на экран, называется TextOut(). Вот ее прототип:

BOOL TextOut(HDC hdc, int x, int у,

LPSTR lpstr, int nlength);

Функция TextOut() выводит на экран строку, задаваемую указателем lpstr, начиная с точки, координаты которой задаются параметрами х и у (по умолчанию эти координаты задаются в пикселах). Длина выводимой строки задается параметром nlength. Функция TextOut() возвращает ненулевое значение при успешном завершении.

В нашем примере каждый раз при получении сообщения WM_CHAR символ, введенный пользователем, преобразуется в строку длиной в один символ при помощи функции sprintf(), а затем выводится на экран при помощи TextOut(), начиная с позиции [1,1].

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

Левый верхний угол рабочей области окна имеет координаты [0,0]. Коор­динаты окна всегда задаются относительно начала рабочей области, а не экрана, поэтому вводимые символы всегда будут отображаться в верхнем левом углу рабочей области окна независимо от его положения на экране.

Причина первого вызова TextOut() заключается в необходимости стереть ранее выведенный символ. Поскольку Windows использует графический режим дисплея и символы шрифта могут иметь различный размер, вывод очередного символа на экран не обязательно приведет к полному стиранию предыдущего символа. Например, если Вы выведете символ i поверх w, часть w все равно останется на экране. (Попробуйте закомментировать первый вызов TextOut() – и увидите, каков будет результат.)

Следует отметить, что никакие функции Windows API не позволяют вывод за границами окна. При попытке пересечь границы окна выводимое изображение будет автоматически усекаться.

Вы можете подумать, что вызов TextOut() для вывода единственного символа не является эффективным использованием этой функции. Однако в Windows отсутствует функция вывода одного символа. Впоследствии Вы увидите, что для большинства операций взаимодействия с пользователем используются диалоги, меню, инструментальные панели и т.п. Поэтому Windows предусматривает всего несколько функций для вывода текста в рабочую область окна.

Пример 3-1. Приведем полную программу обработки ввода символов с клавиатуры.

// Обработка сообщений WM_CHAR

LRESULT CALLBACK WindowFunc(HWND, UINT,

char szWinName[]=»МоеОкно»; // Имя класса окна

char str[80]=»»; // Буфер для строки вывода

int WINAPI WinMain(HINSTANCE hThisInst,

WNDCLASS wcl; // Определить класс окна

wcl.hInstance=hThisInst; // Дескриптор приложения

wcl.lpszClassName=szWinName; // Имя класса окна

wcl.lpfnWndProc=WindowFunc; // Функция окна

wcl.style=0; // Стиль по умолчанию

wcl.lpszMenuName=NULL; // Без меню

wcl.cbClsExtra=0; // Без дополнительной информации

(HBRUSH)GetStockObject(WHITE_BRUSH); //Белый фон

if(!RegisterClass(&wcl)) // Регистрируем класс окна

hwnd=CreateWindow(szWinName, // Создать окно

«Обработка сообщений WM_CHAR»,

WS_OVERLAPPEDWINDOW, // Стиль окна

HWND_DESKTOP, // Нет родител. окна

hThisInst,// Дескриптор приложения

NULL); // Нет дополнит. аргументов

ShowWindow (hwnd, nWinMode); // Показать окно

UpdateWindow (hwnd); // и перерисовать

while(GetMessage(&msg,NULL,0,0)) // Запустить цикл

TranslateMessage(&msg); // Разреш. исп. клавиатуры

DispatchMessage (&msg); // Вернуть управл. Windows

// Следующая функция вызывается операционной

// системой Windows и получает в качестве

// параметров сообщения из очереди сообщений

LRESULT CALLBACK WindowFunc(HWND hwnd,

case WM_CHAR: // Обработка нажатия клавиши

hdc=GetDC(hwnd); // Для получ. контекста устр-ва

TextOut(hdc, 1, 1, » «,2); //Стереть старый код

sprintf(str,»%c»,(char)wParam); //Запись символа

TextOut (hdc, 1, 1, str, strlen(str)); // Вывод

ReleaseDC (hwnd, hdc); // Освободить контекст

case WM_DESTROY: // Завершение программы

// Все сообщения, не обрабатываемые в

// данной функции, направляются на обработку

На рис. 3.1 представлено окно, создаваемое этой программой.

Приведенная программа перед выводом строки на экран получает контекст устройства. Контекст устройства освобождается после того, как вывод на экран закончен. Сейчас самое время объяснить, что представляет собой контекст устройства.

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

Перед тем, как Ваше приложение начнет вывод информации в окно, оно должно получить контекст устройства. До этого связи между программой и окном не существует. Таким образом, перед началом любого процесса вывода необходимо получить контекст устройства. Поскольку TextOut() и другие функции вывода требуют в качестве параметра дескриптор контекста устройства, это правило выпол­няется автоматически.

Типы данных в Win32 API

В WINAPI определено множество типов данных, так же, как и в C/С++ ( int , char , float и т.д.). Учить их определения не обязательно. Достаточно помнить, что они существуют, а когда они появятся или потребуются где-нибудь в программе, посмотреть их определения. В дальнейшем мы будем использовать их все. Условно их можно разделить на несколько видов: основные, дескрипторные, строковые и вспомогательные.

Основные типы

С основными типами данных трудностей возникнуть не должно. Если всё же возникнут, то нужно сюда.

BOOL – этот тип данных аналогичен bool . Он также имеет два значения – 0 или 1. Только при использовании WINAPI принято использовать вместо 0 спецификатор NULL . О нём ниже.

BYTE – байт, ну или восьмибитное беззнаковое целое число. Аналог – unsigned char .

DWORD — 32-битное беззнаковое целое. Аналоги: unsigned long int , UINT .

INT – 32-битное целое. Аналог – long int .

LONG – 32-битное целое – аналог всё также long int .

NULL – нулевой указатель. Вот его объявление:

UINT – 32-битное беззнаковое целое. Аналоги: unsigned long int , DWORD .

Дескрипторные типы данных

Про дескрипторные типы немного рассказывалось на вводном уроке в WINAPI. Дескриптор, как говорилось ранее, — это идентификатор какого-либо объекта. Для разных типов объектов существуют разные дескрипторы. Дескриптор объекта можно описать так:

Есть также дескрипторы кисти, курсора мыши, шрифта и т.д. С их помощью мы можем при инициализации или в процессе работы приложения поменять какие-нибудь настройки, чего, например, мы не могли сделать в консольном приложении. Используются они в описательных функциях, управляющих типа: CreateProcess(), ShowWindow() и т.д. или как возвращаемое значение некоторых функций :

В этой функции мы получили дескриптор считывания потоков std_in и std_out. И можем, например, его использовать в каком-нибудь условии.

Не будем вдаваться в физику создания дескрипторов. Разве что, при необходимости или для большего понимания процессов.

HANDLE – дескриптор объекта.

HBITMAP – дескриптор растрового изображения. От фразы handle bitmap.

HBRUSH – дескриптор кисти. От фразы handle brush.

HCURSOR – дескриптор курсора. От фразы handle cursor.

HDC – дескриптор контекста устройства. От фразы handle device context.

HFONT – дескриптор шрифта. От фразы handle font.

HICONS – дескриптор криптограммы. От фразы handle icons.

HINSTANCE – дескриптор экземпляра приложения. От фразы handle instance.

HMENU – дескриптор меню. От фразы handle menu.

HPEN – дескриптор пера. От фразы handle pen.

HWND – дескриптор окна. От фразы handle window.

Строковые типы данных

Для начала начнём, с того, какие кодировки существуют в Windows ОС.

Есть два вида кодировок символов: ANSI и UNICODE. Однобайтные символы относятся к ANSI, двухбайтные — к кодировке UNICODE. Мы можем с лёгкостью подключить UNICODE кодировку в свойствах проекта. И тогда в коде создать переменную типа char можно будет так:

Если же мы хотим использовать кодировку ANSI, то мы традиционно напишем:

В WINAPI, в зависимости от того, подключён юникод или нет, используются два вида строк UNICODE-ные или TCHAR-ные. Ниже описаны строковые типы данных.

LPCSTR – указатель на константную строку, заканчивающуюся нуль-терминатором. От фразы long pointer constant string.

LPCTSTR – указатель на константную строку, без UNICODE. От фразы long pointer constant TCHAR string. Это надстройка функции LPCSTR.

LPCWSTR – указатель на константную UNICODE строку. От фразы фразы long pointer constant wide character string. Это надстройка функции LPCSTR.

LPSTR – указатель на строку, заканчивающуюся нуль-терминатором. От фразы long pointer string.

LPTSTR – указатель на строку, без UNICODE. От фразы long pointer TCHAR string. Это надстройка функции LPSTR.

LPWSTR – указатель на UNICODE строку. От фразы long pointer wide character string. Это надстройка функции LPSTR.

TCHAR – символьный тип — аналог char и wchar_t.

Вспомогательные типы

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

Работа с данной функцией будет в следующих разделах.

LPARAM – тип для описания lParam (long parameter). Используются вместе с wparam в некоторых функциях.

LRESULT – значение, возвращаемое оконной процедурой имеет тип long .

WPARAM – тип для описания wParam (word parameter). Используются вместе с lParam в некоторых функциях.

На этом типы данных не закончены. В дальнейшем мы обязательно будем обращаться к данной статье.

Wparam lparam что это

typedef UINT_PTR WPARAM;
typedef LONG_PTR LPARAM;
typedef LONG_PTR LRESULT;

What actually there are?

LPARAM & LRESULT. Can i cast of point them to a class object?

The _PTR suffix means the type is big enough to hold a pointer. So, UINT_PTR is a UINT of sufficient size to hold a pointer, and on Win64, UINT and UINT_PTR are different unsigned integer types of 32 and 64 bits, respectively. The _PTR types didn’t exist before 64 bit Windows.

So, all these types are big enough to hold a pointer, but the lifetime issue is the bigger part of your answer. It is relatively uncommon to return a pointer through the LRESULT, but it is very common to pass pointers in LPARAM. It is conventional to use LPARAM for pointers, but WPARAM would work, too (though it depends on the pointer type in 16 bit Windows, from whence the W and L prefixes originate). The crucial thing is the lifetime of the object WRT synchronous (SendMessage) and asynchronous (PostMessage) messages. With SendMessage, you can pass a pointer to a local object, and since it is synchronous, the object outlives the message handler, assuming the handler doesn’t retain a copy of the pointer. However, with PostMessage, you will usually have to use new/delete, e.g. to pass a CString in PostMessage (I use auto_ptr for exception safety):

Sending (This use of auto_ptr could be considered overkill, but the technique is useful in more complex code, or in general, when you want to take the possibility of errors seriously):

std::auto_ptr<CString> p(new CString("whatever"));
if (::PostMessage(. reinterpret_cast<LPARAM>(p.get())))
p.release(); // Relinquish ownership
else
handle error

std::auto_ptr<CString> p(reinterpret_cast<CString*>(lparam));
// The CString is automatically deleted when p goes out of scope.

What you never want to do is this:

CString s("whatever");
PostMessage(. reinterpret_cast<LPARAM>(&s)); // Wrong
PostMessage(. reinterpret_cast<LPARAM>(LPCTSTR(s))); // Wrong

The problem is that the CString will likely have gone out of scope and been destroyed by the time the message handler processes the message, and so its LPARAM will be invalid. This would have been OK with SendMessage.

What are the definitions for LPARAM and WPARAM?

I know I’m being lazy here and I should trawl the header files for myself, but what are the actual types for LPARAM and WPARAM parameters? Are they pointers, or four byte ints? I’m doing some C# interop code and want to be sure I get it working on x64 systems.

6 Answers 6

LPARAM is a typedef for LONG_PTR which is a long (signed 32-bit) on win32 and __int64 (signed 64-bit) on x86_64.

WPARAM is a typedef for UINT_PTR which is an unsigned int (unsigned 32-bit) on win32 and unsigned __int64 (unsigned 64-bit) on x86_64.

These typedefs go back to the 16-bit days. Originally, LPARAM was a long (signed 32-bit) and WPARAM was a WORD (unsigned 16-bit), hence the W and L. Due to the common practice of passing casted pointers as message parameters, WPARAM was expanded to 32 bits on Win32, and both LPARAM and WPARAM were expanded to 64 bits on Win64.

In C#, you should use IntPtr for LPARAM and UIntPtr for WPARAM.

Note that despite the LP prefix, LPARAM is not a far pointer to an ARAM .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *