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


Автор: yngwie19 20.5.2009, 00:06
Здравствуйте! В книге Петзольда описывается субклассинг вот на каком примере:
Есть главное окно, в нем создано дочернее окно с классом "scrollbar". У каждого окна есть оконная процедура (я так полагаю что и у окна класса scrollbar тоже). В книге описывается механизм замены оконной процедуры класса scrollbar на свою, для этого делают слежующее:
Код

WNDPROC MyProc;
LRESULT CALLBACK MyProc(HWND,UINT,WPARAM,LPARAM)
int WINAPI WinMain(........)
{
.................
HWND hwndScroll = CreateWindow("scrollbar",NULL,WS_CHILD............);
SetWindowLong(hwndScroll,GWL_WNDPROC,(LONG)MyProc);
.......
return 0;

LRESULT CALLBACK MyProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
      switch(msg)
      {
           case WM_....
                 return 0;
           case ...........
      }
      return CallWindowProc(hwnd,msg,wParam,lParam);
}

}

вот как-то так. У меня вопрос вот в чем, правильно ли Я понимаю, что без субклассинга окно scrollbar обрабатывается в оконной процедуре родительского окна через сообщение WM_VSROLL или WM_HSCROLL соответсвенно, т.е по умолчанию заложего так. А если мы меняем адрес этой процедуры на свою, то она обрабатывается той оконной процедурой, которую мы для нее описали. А если необходимо чтобы какие-то сообщения посылаемые этому окну (scrollbar- у ) обрабатывались процедурой "по-умолчанию", то мы вызываем CallWindowProc() и передаем ей все необходимые параметры. Правильно ли Я понял суть?

Автор: mes 20.5.2009, 00:17
Цитата(yngwie19 @  19.5.2009,  23:06 Найти цитируемый пост)
если мы меняем адрес этой процедуры на свою, то она обрабатывается той оконной процедурой, которую мы для нее описали. А если необходимо чтобы какие-то сообщения посылаемые этому окну (scrollbar- у ) обрабатывались процедурой "по-умолчанию", то мы вызываем CallWindowProc() и передаем ей все необходимые параметры.

Вобще то правильней не "процедурой по умолчанию", а предыдущей оконой функцией и  в примере Вы забыли запомнить эту предыдущую функцию (через GetWindowLong)  и в дальнейшем вызвать ее в CallWindowProc :
Цитата(yngwie19 @  19.5.2009,  23:06 Найти цитируемый пост)
      return CallWindowProc(hwnd,msg,wParam,lParam);

обратите внимание на первый параметр : http://msdn.microsoft.com/en-us/library/ms633571.aspx

Автор: yngwie19 20.5.2009, 07:20
mes, ну да точно совсем забыл, ну а вообще правильно ли Я все понял?
меня вот что интересует, любое дочернее  окно по умолчанию обрабатывается оконной процедурой окна родителя так? а если этот контрол у нас создан в диалоговом окне, то тут можно также менять адрес процедуры на другой?

Автор: Cheloveck 20.5.2009, 07:36
Цитата(yngwie19 @  20.5.2009,  08:20 Найти цитируемый пост)
любое дочернее  окно по умолчанию обрабатывается оконной процедурой окна родителя так?

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

Добавлено через 2 минуты и 52 секунды
Цитата(mes @  20.5.2009,  01:17 Найти цитируемый пост)
Вы забыли запомнить эту предыдущую функцию (через GetWindowLong)

Более правильно запомнить результат функции SetWindowLong - она вернёт предыдущую процедуру.

Автор: Earnest 20.5.2009, 07:40
Цитата(yngwie19 @  20.5.2009,  08:20 Найти цитируемый пост)
любое дочернее  окно по умолчанию обрабатывается оконной процедурой окна родителя так?

Совсем нет. У каждого окна своя процедура. 
В родительское окно дочернее посылает различные уведомления (такие как WM_COMMAND, WM_NOTIFY, WM_DRAWITEM, ..., скролл-бары посылают WM_SCROLL). А обрабатываемых сообщений сотни.

Кроме того, скролл-бары не обязательно являются окнами. Иногда - это просто часть неклиентской области. Например у окон со стилем WS_SCROLL.

Автор: yngwie19 20.5.2009, 08:33
Earnest, 
Цитата(Earnest @  20.5.2009,  07:40 Найти цитируемый пост)
Совсем нет. У каждого окна своя процедура. 

ну т.е у всех окон своя оконная процедура,  которая по умолчанию описана так (например для ScrollBar - a  ):
Код

LRESULT CALLBACK ScrollBar(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
      return CallWindowProc('адрес процедуры окна родителя',hwnd,msg,wParam,lParam)
}


Правильно Я понял, т.е она ничего не делает кроме как вызывает оконную функцию окна родителя и передает ей параметры?

Автор: mes 20.5.2009, 09:15
Цитата(Cheloveck @  20.5.2009,  06:36 Найти цитируемый пост)
Более правильно запомнить результат функции SetWindowLong - она вернёт предыдущую процедуру.

точно, подзабыл.. 100 лет не программил на WinApi.

Цитата(yngwie19 @  20.5.2009,  07:33 Найти цитируемый пост)
'адрес процедуры окна родителя'

нет, процедура родителя (а в WinApi  этому званию соответствует окно, на котором создали дочернее) не вызывается. 

Цитата(Cheloveck @  20.5.2009,  06:36 Найти цитируемый пост)
Разница лишь в том, что дочерние окна извещают своих родителей о своих изменениях и статусах...

Т.е родитель "насладжается" лишь получаемыми сообщениями и никакого другого контакта.

Каждое окно вызывает оконную процедуру предка по наследованию т.е есть главная процедура всех окон DefWindowProc ()
от нее (не важна на прямую или через посредников) отнаследованы:
 DefDialogWindowProc - которую в конечном итоге должен вызвать контрол желающий быть диалогом
 DefScrollBarProc () - которую вызывают скролы,
 DefTextCtrlProc () - ..
и так далее.
(названия даны условны, и с истинами именами могут не совпадать)

При создании нового типа окна, в оконной процедуре мы вызываем DefWindowProc, a при субклассировании фактически создается стек окон процедур, обеспечивающий наследованное поведение,
условно такоe :
MyWindowProc -> SavedWindowProc () -> ScrolledWindowProcEx () -> ScrolledWindowProc() -> DefScrollWindowProc ();

Цитата(yngwie19 @  20.5.2009,  06:20 Найти цитируемый пост)
а если этот контрол у нас создан в диалоговом окне, то тут можно также менять адрес процедуры на другой? 

Разницы нет кто впоследствии будет родителем, важно от кого отнаследовано.



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




Автор: yngwie19 20.5.2009, 10:36
mes, да что-то не очень понятно, запутано очень или надо кертинку рисовать. Можно где-нибудь на русском про это почитать?

Автор: GremlinProg 20.5.2009, 11:11
все будет гораздо проще, если представить оконную процедуру как полиморфное перекрытие класса:
Код

class CScrollBar{
  virtual LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
    ...
  }
};

тогда субклассинг - это наследование нового класса CMyScrollBar от CScrollBar, с перекрытием главного метода класса:
Код

class CMyScrollBar: public CScrollBar{
  virtual LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
    return CScrollBar::WndProc(...);
  }
};

я уже приводил похожий пример,
return CScrollBar::WndProc(...) - равнозначно CallWindowProc('адрес процедуры окна предка',...)
слово родитель тут совсем не к месту, это предок, т.е. класс окна от которого производится субклассинг
здесь можно вполне легально использовать термины ООП: инкапсуляция, наследование, полиморфизм

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

yngwie19, если понимаешь смысл основных инструментов ООП (инкапсуляция, наследование, полиморфизм), то и с субклассингом все должно быть понятно
если нет, то можешь найти достаточно информации на эту тему в других разделах C/C++

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