Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > CallWindowProc для диалогов?


Автор: Alca 18.6.2009, 12:24
CallWindowProc для диалогов? Как можно заменить процедуру диалога?

Автор: GremlinProg 18.6.2009, 12:48
Цитата(Alca @  18.6.2009,  14:24 Найти цитируемый пост)
Как можно заменить процедуру диалога?

в рантайме - как и для всех остальных окон, через SetWindowLong и GWL_WNDPROC

а вот кгда ты сам управляешь ресурсом, то можно диалог просто наследовать (суперклассить)
для этого нужно: зарегистрировать класс-потомок от диалога, а в ресурсах самого диалога заполнить свойство "Class"
тут нужно указать имя этого зарегистрированного класса

в программе все аналогично, как с обычным диалогом, за исключением последнего параметра (lpDialogFunc),
в него можно передать NULL:
Код

DialogBox(hInstance, lpTemplate, hWndParent, NULL);


Добавлено через 1 минуту и 39 секунд
да, вместо DefWindowProc, в перекрытой процедуре окна нужно уже вызывать DefDlgProc 

Автор: Alca 19.6.2009, 10:56
Цитата

да, вместо DefWindowProc, в перекрытой процедуре окна нужно уже вызывать DefDlgProc 

Я создаю главное окно через CreateDialog. Использовать DefWindowProc или DefDlgProc?

Добавлено через 1 минуту и 14 секунд
Чета у меня не получается CreateDialog и DefWindowProc в класс завернуть...

Автор: GremlinProg 19.6.2009, 11:03
без разницы, какой функцией создавать диалог,
при перекрытии, в любом случае нужно уже вызывать DefDlgProc,
напоминаю условия суперклассинга диалогов: http://msdn.microsoft.com/en-us/library/ms644995(VS.85).aspx#custom_boxes

Автор: Alca 19.6.2009, 11:12
Для начала, сдесь все правильно?
Код

#include "Test.h"

#include <windows.h>
#include <assert.h>
#include <iostream>
#include <stdlib.h>
#include <commctrl.h>
//---------------------------------------------------------------------------
HINSTANCE        g_hInstance  = NULL; 
HWND             g_hMainWnd   = NULL; 

int     WINAPI   WinMain     (HINSTANCE hInstance,    HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); 
LRESULT CALLBACK lpMainProc  (HWND hWnd, UINT message, UINT wParam, LONG lParam);
BOOL             bRegWndClass(HINSTANCE hInstance);
//---------------------------------------------------------------------------
BOOL bRegWndClass(HINSTANCE hInstance) {
    BOOL bRes = FALSE;

    //-------------------------------------
    //The Window structure
    WNDCLASSEX wcex = {0};

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = lpMainProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = NULL;
    wcex.hCursor        = ::LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName    = NULL;
    wcex.lpszClassName    = "TestWindowClass";
    wcex.hIconSm        = NULL;

    //-------------------------------------
    //Register the window class
    bRes = ::RegisterClassEx(&wcex);
    assert(FALSE != bRes);

    return TRUE;
}
//---------------------------------------------------------------------------
LRESULT CALLBACK lpMainProc(HWND hWnd, UINT uiMsg, UINT wParam, LONG lParam) {
    INT wmId    = - 1;
    INT wmEvent = - 1;

    switch (uiMsg) {
        case WM_COMMAND:
            wmId    = LOWORD(wParam);
            wmEvent = HIWORD(wParam);
            switch (wmId) 
            {
                case IDC_btnTest:
                    ::MessageBox(0, "Test", "ID_btnTest", MB_OK);
                    break;

                case IDC_btnExit:
                    ::DestroyWindow(hWnd);
                    break;
            }
            break;

        case WM_CLOSE: 
            ::DestroyWindow(hWnd); 
            break;

        case WM_DESTROY:
            ::PostQuitMessage(0);
            break;

        default:
            ////    return ::DefWindowProc(hWnd, uiMsg, wParam, lParam);      /*>>Так нормально<<*/
            return ::DefDlgProc(hWnd, uiMsg, wParam, lParam);     /*>>Валиться здесь<<*/
            /*Unhandled exception at 0x004116e6 in Test.exe: 0xC00000FD: Stack overflow.*/
    }

    return 0;
}
//---------------------------------------------------------------------------
INT WINAPI WinMain(HINSTANCE hInstance,    HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) {
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    UNREFERENCED_PARAMETER(nCmdShow);

    INT  iRes = - 1;
    BOOL bRes = FALSE;
    MSG  msg  = {0};

    g_hInstance = hInstance;
    assert(0 != g_hInstance);

    bRes = bRegWndClass(g_hInstance);
    assert(FALSE != bRes);

    //-------------------------------------
    //Создаем диалог
    g_hMainWnd = ::CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_dlgMain), NULL, (DLGPROC)lpMainProc);
    assert(NULL != g_hMainWnd);

    bRes = ::ShowWindow(g_hMainWnd, SW_SHOW);
    assert(FALSE == bRes);

    bRes = ::UpdateWindow(g_hMainWnd);
    assert(FALSE != bRes);

    //-------------------------------------
    //Вертушка
    while (iRes = ::GetMessage(&msg, NULL, 0, 0 )) {
        if (- 1 == iRes) {
            assert(false);
        } else {
            ::TranslateMessage(&msg);
            ::DispatchMessage (&msg);
        }
    }

    return (INT)msg.wParam;;
}
//---------------------------------------------------------------------------

Автор: GremlinProg 19.6.2009, 11:31
нет,
поправка 1:
Код

wcex.cbWndExtra        = DLGWINDOWEXTRA;

поправка 2:
Цитата(Alca @  19.6.2009,  13:12 Найти цитируемый пост)

default:
  return::DefDlgProc(hWnd, uiMsg, wParam, lParam);

сколько раз повторять, не используйте условную посылку системных функций типа DefWindowProc, это источник массы неявных ошибок
поправка 3:
Цитата(Alca @  19.6.2009,  13:12 Найти цитируемый пост)
Stack overflow

неудивительно, тут же рекурсия
Цитата

::CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_dlgMain), NULL, NULL);

а в остальном, как я понимаю, раз идет рекурсия, значит свойство Class выставлено правильно, т.е. в TestWindowClass
после поправок 1 и 3, все должно работать

Автор: Alca 19.6.2009, 12:03
::DefDlgProc действительно был лишний и 
если диалог создается с ресурсов, то:
Код

wcex.cbWndExtra        = DLGWINDOWEXTRA;

GremlinProg, спасибо! Буду дальше разбираться.

Добавлено через 9 минут и 3 секунды
Хочу вступить в клуб волшебников!!!!  smile 

Автор: GremlinProg 19.6.2009, 12:35
Цитата(Alca @  19.6.2009,  14:03 Найти цитируемый пост)
::DefDlgProc действительно был лишний

нет, Alca, ты не понял
DefDlgProc тут не лишний,
просто, когда в последний параметр CreateDialog ты ставишь lpMainProc,
то, тем самым, из lpMainProc делаешь обычную диалоговую процедуру (при этом, обычно компилятор ругается на несоответствие типов, там у колбэков типы обратных результатов разные),
а раз эта процедура является и процедурой окна,
то DefDlgProc, на всякий нулевой возврат из контекста диалоговой функции, будет вызывать его вновь и вновь, т.е. зациклится
можешь это проверить простым статическим счетчиком на любом единичном сообщении, типа WM_LBUTTONDOWN или WM_SHOWWINDOW

а про default, я все подробно объяснил тут: http://forum.vingrad.ru/forum/topic-260881/hl/wm_size/index.html
неочевидно, но, как видишь, есть очевидные прецеденты

Автор: Alca 19.6.2009, 12:54
Цитата

просто, когда в последний параметр CreateDialog ты ставишь lpMainProc,
то, тем самым, из lpMainProc делаешь обычную диалоговую процедуру 
(при этом, обычно компилятор ругается на несоответствие типов, там у колбэков типы обратных результатов разные)

Так а чего туда писать надо?

Добавлено через 43 секунды
Цитата

там у колбэков типы обратных результатов разные

Видел WINPROC и DLGPROC.

Добавлено через 2 минуты и 59 секунд
Я просто убрал:
Код

        default:
            return ::DefDlgProc(hWnd, uiMsg, wParam, lParam);

Автор: GremlinProg 19.6.2009, 13:16
Цитата(Alca @  19.6.2009,  14:54 Найти цитируемый пост)
Так а чего туда писать надо?

NULL
Цитата(Alca @  19.6.2009,  14:54 Найти цитируемый пост)
Я просто убрал:

это означает лишь то, что диалоговая процедура не будет вызвана, но и стандартный DefWindowProc - тоже

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

Автор: Alca 19.6.2009, 15:10
Цитата

Цитата

Так а чего туда писать надо?

NULL

Так что ли? А как я буду обрабатывать сообщния? 
Код

g_hMainWnd = ::CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_dlgMain), NULL, (DLGPROC)NULL);

Автор: Alca 19.6.2009, 16:06
Цитата

Функция DefDlgProc не должна вызваться процедурой диалогового окна; поступив так, вы приведете ее к рекурсивному выполнению. (Примечание переводчика: то есть «подвесите» систему.)

Автор: GremlinProg 21.6.2009, 12:35
Цитата(Alca @  19.6.2009,  17:10 Найти цитируемый пост)
Так что ли?

да, именно так

Цитата(Alca @  19.6.2009,  17:10 Найти цитируемый пост)
А как я буду обрабатывать сообщния?

сообщения здесь обрабатывает уже процедура окна класса TestWindowClass
Цитата

Функция DefDlgProc не должна вызваться процедурой диалогового окна; поступив так, вы приведете ее к рекурсивному выполнению. (Примечание переводчика: то есть «подвесите» систему.)

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

Добавлено через 12 минут и 28 секунд
давай лучше так, чтобы не путаться:
есть процедура диалога,
а есть процедура окна диалога

процедура диалога - это простой колбэк, грубо говоря, не связанный с окном диалога напрямую, он вызывается из процедуры окна диалога всякий раз, когда нужно "дообработать" сообщение,

понимаешь разницу между этими двумя процедурами?

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)