Войти
ПрограммированиеФорумОбщее

GWL_ID и хранение данных

#0
19:10, 23 ноя 2022

У виндовых окошек на удивление мало оффсетов для хранения пользовательской информации, есть только GWL_USERDATA и GWL_ID.
Предполагается, что через указатель на GWL_USERDATA пользователь создат собственную структуру и там сохранит всё что ему потребуется, но в моём случае возникла необходимость хранить еще одно дополнительное число, независимо от GWL_USERDATA. Число вполне укладывается в 16-битный диапазон, я бы мог его сохранить как верхнюю часть GWL_ID, который по счастью 32-х битный. Но вот вопрос:

Как минимум для WM_COMMAND GWL_ID упаковывается в очередной INT, где верхняя часть - notifyCode, а нижняя - собсно наш ID.
В принципе меня такое поведение полностью устраивает, за исключением одной мелочи - как упаковывает GWL_ID?
Пытается засунуть содержимое GWL_ID в 16 бит без каких-либо проверок?
Отсекает верхнюю часть содержимого GWL_ID?
Делает там какой-то жесткий клампинг, чтоб переменная не вышла из 16-битных границ?
Идеальным для меня был бы второй вариант. В MSDN ессно на эту тему ни гу-гу.
Подскажите, может кто-то в курсе как оно там?

#1
(Правка: 22:30) 22:13, 23 ноя 2022

g-cont
> В MSDN ессно на эту тему ни гу-гу.
какая жесть! я вот никогда про GWL_ID не слышал.
https://learn.microsoft.com/en-us/windows/win32/winmsg/window-features

Messages

A child window can have a unique integer identifier. Child window identifiers are important when working with control windows. An application directs a control's activity by sending it messages. The application uses the control's child window identifier to direct the messages to the control. In addition, a control sends notification messages to its parent window. A notification message includes the control's child window identifier, which the parent uses to identify which control sent the message. An application specifies the child-window identifier for other types of child windows by setting the hMenu parameter of the CreateWindowEx function to a value rather than a menu handle.

попахивает 16-битным наследием от Windows 1.0

а вот тут (https://learn.microsoft.com/en-us/windows/win32/winmsg/using-wind… child-windows) пример есть:

#define ID_FIRSTCHILD  100 
#define ID_SECONDCHILD 101 
#define ID_THIRDCHILD  102 
 
LONG APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    RECT rcClient; 
    int i; 
 
    switch(uMsg) 
    { 
        case WM_CREATE: // creating main window  
 
            // Create three invisible child windows. 

            for (i = 0; i < 3; i++) 
            { 
                CreateWindowEx(0, 
                               "ChildWClass", 
                               (LPCTSTR) NULL, 
                               WS_CHILD | WS_BORDER, 
                               0,0,0,0, 
                               hwnd, 
                               (HMENU) (int) (ID_FIRSTCHILD + i), 
                               hinst, 
                               NULL); 
            }
 
            return 0; 
 
        case WM_SIZE:   // main window changed size 
 
            // Get the dimensions of the main window's client 
            // area, and enumerate the child windows. Pass the 
            // dimensions to the child windows during enumeration. 
 
            GetClientRect(hwnd, &rcClient); 
            EnumChildWindows(hwnd, EnumChildProc, (LPARAM) &rcClient); 
            return 0; 

        // Process other messages. 
    } 
    return DefWindowProc(hwnd, uMsg, wParam, lParam); 
} 
 
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam) 
{ 
    LPRECT rcParent; 
    int i, idChild; 
 
    // Retrieve the child-window identifier. Use it to set the 
    // position of the child window. 
 
    idChild = GetWindowLong(hwndChild, GWL_ID); 
 
    if (idChild == ID_FIRSTCHILD) 
        i = 0; 
    else if (idChild == ID_SECONDCHILD) 
        i = 1; 
    else 
        i = 2; 
 
    // Size and position the child window.  
 
    rcParent = (LPRECT) lParam; 
    MoveWindow(hwndChild, 
               (rcParent->right / 3) * i, 
               0, 
               rcParent->right / 3, 
               rcParent->bottom, 
               TRUE); 
 
    // Make sure the child window is visible. 
 
    ShowWindow(hwndChild, SW_SHOW); 
 
    return TRUE;
}

получается что  MN_GETHMENU вернёт GWL_ID

g-cont
> В принципе меня такое поведение полностью устраивает, за исключением одной
> мелочи - как упаковывает GWL_ID?
ответ никак не упаковывает! ты сам контроллируешь это значение.

g-cont
> Как минимум для WM_COMMAND
https://learn.microsoft.com/en-us/windows/win32/controls/control-messages

The lParam parameter of WM_NOTIFY is either the address of an NMHDR structure or the address of a larger structure that includes NMHDR as its first member. The structure contains the notification code and identifies the common control that sent the notification message. The meaning of the remaining structure members, if any, varies depending on the notification code.

в WM_NOTIFY идентификатор полноразмерный:
https://learn.microsoft.com/en-us/windows/win32/api/richedit/ns-richedit-nmhdr

idFrom

Type: UINT

An identifier of the control sending the message.

ЗЫ: я так понимаю, эту фичу ни один из популярных UI фреймворков не использует.
потому что окна встраиваются друг в друга (докинг!), и в один момент окно может использовать меню, а в другой момент уже и нет.

#2
12:49, 24 ноя 2022

skalogryz
> ответ никак не упаковывает! ты сам контроллируешь это значение.
Правильно, если я его задаю и считывают через Get\Set WindowLong. Но есть ещё один кейс. Часть этих параметров задаётся уже при создании окна, те же стили или GWL_WNDPROC.
Но есть еще параметр

CreateWindowExA(
    DWORD dwExStyle,
    LPCSTR lpClassName,
    LPCSTR lpWindowName,
    DWORD dwStyle,
    int X,
    int Y,
    int nWidth,
    int nHeight,
    HWND hWndParent ,
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpParam);

hMenu, который очень часто указывает не на само меню, а на целочисленных идентификатор. Я предполагаю, что именно он и заполянет GWL_ID.
Однако там, получается может быть и указатель на результат CreateMenu(), если мы приаттачили менюбар к этому окошку?
Через AppendMenu туда добавляются новые элементы со своими id. И когда мы их выбираем, то проходит WM_COMMAND, у которой wParam старшие 2 байта, это notify code, например EN_CHANGE или нечто подобное, а младшие два байта - это идентификатор элемента меню.
Хотя я наверное перестраховываюсь, и ID, заданные для меню никак не связаны с GWL_ID самого окна, меня просто смутил сам факт, что приаттаченое к окну меню вероятнее всего использует тот самый GWL_ID для этих целей. Либо же это можно выполнить позже через SetMenu, но вероятно с тем же самым результатом. Я просто ищу чисто пользовательский оффсет, который винда никак не трогает и не использует.
Вообщем надо будет провести эксперименты, позже отпишусь.

#3
(Правка: 10:23) 8:50, 25 ноя 2022

Скорее всего придётся городить параллельную структуру. Извлекать и устанавливать эти дополнительные данные предполагается прямо в WndProc, а Get\SetWindowLong провоцируют его дополнительный вызов, вся конструкция получается слишком сложной и запутанной.
Я думаю просто привязаться к адресу окна, сами адреса отсортирую по возрастанию, чтоб бинарный поиск работал.

PS. Тут до кучи выяснилось что VCL этот GWL_ID сам активно использует, причём для хранения хэндла окна, так что не вариант его использовать в любом случае.

ПрограммированиеФорумОбщее