контекст диалога - как сохранить? - PullRequest
1 голос
/ 27 июля 2011

Мне интересно, как обеспечить контекст для диалоговых процедур?

До сих пор я объявлял статические или глобальные переменные, чтобы предоставить любой контекст, который мне нужен, зная, что процедура диалога может быть активна только один раз. Однако я нахожу это довольно уродливым и задаюсь вопросом, смогу ли я передать контекст в процедуру диалога через механизм DialogBoxParam / WM_INITDIALOG / LPARAM, сохранив его в обработчике INITDIALOG с помощью SetWindowLog / GWL_USERDATA?

Несколько экспериментов показывают, что это может сработать, но я заметил, что процедура диалога получает по крайней мере одно сообщение (а именно 30 часов), прежде чем получает сообщение INITDIALOG.

Так. У меня осталась дилемма. Мне приходит в голову одно из следующих ...

  • возвращает FALSE для любого сообщения, полученного в то время как GetWindowLong / GWL_USERDATA это ноль. Но знаю ли я, что его начальное значение будет равно нулю?

  • вернуть FALSE в сообщение 30h. Но есть ли другие сообщения, отправленные b4 WM_INITDIALOG

  • Запустите процедуру, которая возвращает FALSE, пока не получит WM_INITDIALOG, затем измените процедуру диалога, используя SetWindowLong / GWL_WNDPROC.

Я также обеспокоен тем, что другие системные вызовы или другое программное обеспечение uSoft (вещи, которые я нахожу в MSDN) могут делать с GWL_USERDATA.

1 Ответ

1 голос
/ 27 июля 2011

На самом деле существует довольно много способов сохранить контекст для ваших оконных процедур, и диалоги - это просто особый случай окон, поэтому эти методы применимы.

Одним из способов, как вы упомянули, является использование GWLP_USERDATA / GetWindowLongPtr() / SetWindowLongPtr() для хранения указателя на контекст диалога. Обратите внимание, что функции имеют префикс Ptr(). Эти функции будут работать с 32-битным и 64-битным кодом. Функции «не Ptr()» работают только с 32-битным кодом, поэтому их не следует использовать.

В соответствии с документацией для GetWindowLongPtr() значение GWLP_USERDATA изначально установлено на ноль. Таким образом, вы всегда можете рассчитывать на проверку того, вернет ли GetWindowLongPtr() ноль, и на этом этапе возвращает FALSE из диалоговой процедуры.

Процедура диалога с использованием этих функций может выглядеть примерно так:

// MyDialogProc is a static function in MyDialogClass. Can also be a global function.
INT_PTR CALLBACK MyDialogClass::MyDialogProc(HWND hwndDlg, UINT uMsg,
    WPARAM wParam, LPARAM lParam)
{
    MyDialogClass* target = 0;
    if(uMsg == WM_INITDIALOG)
    {
        target = reinterpret_cast<MyDialogClass*>(lParam);
        ::SetWindowLongPtr(hwndDlg, GWLP_USERDATA,
            reinterpret_cast<LONG_PTR>(target));
    }
    target = reinterpret_cast<MyDialogClass*>(
        ::GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
    if(target != 0)
    {
        // Something like this
        return target->ProcessMessage(hwndDlg, uMsg, wParam, lParam);
    }
    return FALSE;
}

И ваш код создания диалога передает указатель на экземпляр MyDialogClass, используя функцию-член, возможно, такую:

void MyDialogClass::Create()
{
    // ....
    // Use CreateDialogParam() or friends to create a dialog and pass
    // the context pointer.
    HWND h = ::CreateDialogParam(hInstance, lpTemplateName, hWndParent,
        &MyDialogClass::MyDialogProc, reinterpret_cast<LPARAM>(this));
    // ....
}

Если вы предпочитаете другой метод (поскольку вы беспокоитесь, что кто-то другой напишет поверх GWLP_USERDATA), вы можете использовать GetProp () / SetProp () / RemoveProp () и придумайте уникальное имя для идентификации указателя. GetProp() возвращает ноль, если не удается найти свойство, поэтому вы снова можете рассчитывать, что оно вернет ноль Это метод, который я использую в своей библиотеке / фреймворке.

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

const wchar_t* myDialogClassContextPtrName = L"MyDlgClsCxtPtr"; // Unicode
// const char* myDialogClassContextPtrName = "MyDlgClsCxtPtr";  // ANSI

// MyDialogProc is a static function in MyDialogClass. Can also be a global function.
INT_PTR CALLBACK MyDialogClass::MyDialogProc(HWND hwndDlg, UINT uMsg,
    WPARAM wParam, LPARAM lParam)
{
    MyDialogClass* target = 0;
    if(uMsg == WM_INITDIALOG)
    {
        target = reinterpret_cast<MyDialogClass*>(lParam);
        ::SetProp(hwndDlg, myDialogClassContextPtrName,
            reinterpret_cast<HANDLE>(target));
    }
    target = reinterpret_cast<MyDialogClass*>(
        ::GetProp(hwndDlg, myDialogClassContextPtrName));
    if(target != 0)
    {
        // Something like this
        INT_PTR returnValue = target->ProcessMessage(hwndDlg, uMsg, wParam, lParam);
        if(uMsg == WM_NCDESTROY)
        {
            ::RemoveProp(hwndDlg, myDialogClassContextPtrName);
        }
        return returnValue;
    }
    return FALSE;
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...