Указатель на метод класса, являющийся оконной процедурой (WNDPROC)

Тема в разделе "WASM.ZEN", создана пользователем MegaDwarf, 1 дек 2006.

  1. MegaDwarf

    MegaDwarf New Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2006
    Сообщения:
    11
    Замыслил я написать класс для авторесайзинга контролов расположенных на окне моего приложения. Для этого мне надо сабклассить мою же оконную процедуру. Т.е. у меня в классе есть оконная процедура и где-нибудь при инициализации объекта класса я сабклассю.

    Код (Text):
    1. class Resizing
    2. {
    3. public:
    4.     void SetMainWnd( HWND hWnd );
    5.     LRESULT CALLBACK MyWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
    6.  
    7. ...
    8. }
    9.  
    10. void Resizing::SetMainWnd( HWND hWnd )
    11. {
    12. ...
    13.     m_OrigWndProc = SetWindowLongPtr( hWnd, GWLP_WNDPROC, (LONG)MyWndProc );
    14. ...
    15. }
    Ну есесно это не работает: имеем ошибку c2440: "type cast" : cannot convert from LRESULT (__stdcall Resizing::*)(HWND, UINT, WPARAM, LPARAM) to LONG

    После долгого кумеканья (лазанья по инету) понял, что надо делать MyWndProc статиком и потом в ней получать объект класса как-нить по hWnd. Ето еще пока не реализовал (но вроде это должно работать), просто хотелось бы узнать может есть еще какие-то способы решения данной проблемы...
     
  2. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Есть такое дело. И что плохо - из статической функции к данным класса (и this вообще) доступа не получить. Обычно в колбэках есть параметр, называемый application defined value. В него и передают this, доставая потом в колбэке:
    Код (Text):
    1. DWORD WINAPI CClass::ThreadProc(LPVOID lpVar){
    2.   CClass* pThis = reinterpret_cast<CClass*>(lpVar);
    3.   pThis->DoSomething(); // работаем через pThis
    4.   ...
    5. }
    В твоём случае сложнее. Можно использовать трюк с подменой адреса возврата, как делает ATL. Это хак. Можно помещать this в само окно (в GWLP_USERDATA), если само окно его не использует, и доставать в колбэке. Или в DWLP_USER, если место под него выделено. Это ненадёжно, т.к. окно может использовать эти ячейки под свои данные.

    Можно использовать глобальную переменную, если экземпляр твоего класса единственный. Или глобальную хеш-таблицу с парами handle => this, если классов может быть несколько. Кто-то скажет, что глобальные переменные - это плохо. Ну что ж, оборачивать WINAPI классами тоже не лучшая затея :)
     
  3. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    В бормановском VCL делается и то и другое. Для общих целей this\self цепляется к окну через SetProp\GetProp, а для автовызова методов WndProc используется хак-трюк: VirtualAlloc'ом выделяется память c атрибутом EXECUTE и в нее пишутся фиктивные WndProc окон, представляющие собой инструкцию call StdWndProc, за которой идет указатель на метод WndProc и this. StdWndProc попит адрес возврата и вызывает метод WndProc с параметром this
     
  4. MegaDwarf

    MegaDwarf New Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2006
    Сообщения:
    11
    Ну в общем всем спасибо. this буду пихать по-видимому в GWLP_USERDATA. (впрочем вариант с глобальной переменной тоже имеет право на жизнь, хоть это и не есть гут)

    IceStudent
    Я об этом как-то особо не задумывался, просто как-то на уровне подсознания чувствую, что это как-то нехорошо, а ЧТО нехорошо выразить не могу :) Так почему же это не лучшая затея?
     
  5. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    SetProp()/GetProp()
     
  6. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    MegaDwarf
    Просто в большинстве случаев это избыточно. Или изобретаешь велосипед (ведь есть WTL/MFC, где то, что ты пытаешься сделать, реализовано). Имхо, конечно.

    А вот об этом не знал, возьму на заметку.
     
  7. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    IceStudent
    Возьми, может пригодиться ;)
    В т.ч. и для баловства с VCL-прогами, т.к. через GetProp из них можно выцепить указатели на оконные контролы, а при некоторых ухищрениях и на неоконные типа TLabel. А там и доступ к полям и виртуальным методам - широкий простор для развлечения ;)
     
  8. Im

    Im New Member

    Публикаций:
    0
    Регистрация:
    19 янв 2008
    Сообщения:
    10
    Здравствуйте
    Немного исследую WTL, но к сожалению пока что только начал изучать ассемблер.. Не могу понять один момент в переходнике для оконной процедуры.
    Вот код переходника:
    Код (Text):
    1.         // тут все понятно, hWnd в стеке заменяется на this
    2.         m_mov = 0x042444C7;  //C7 44 24 0C
    3.         m_this = PtrToUlong(pThis);
    4.  
    5.         // команда безусловного перехода
    6.         m_jmp = 0xe9;
    7.         // по адресу новой оконной процедуры
    8.         m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
    -только непонятна одна вещь что за адрес m_relproc выходит после выполнения операции:
    Код (Text):
    1. DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
    Почему тут нельзя указывать адресс просто вот так:
    Код (Text):
    1. m_relproc = DWORD((INT_PTR)proc;
    Помогите пожалуйста разобраться с этим
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Потому, что команда jmp = E9h - это не абсолютный, а относительный переход и соотв-но операндом является не сам адрес перехода, а его смещение относительно адреса команды, следующей за jmp
     
  10. Im

    Im New Member

    Публикаций:
    0
    Регистрация:
    19 янв 2008
    Сообщения:
    10
    Чтобы понятнее было вот кусок когда с переходником побольше:
    Код (Text):
    1. #pragma pack(push,1)
    2. struct _stdcallthunk
    3. {
    4.     DWORD   m_mov;          // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
    5.     DWORD   m_this;         //
    6.     BYTE    m_jmp;          // jmp WndProc
    7.     DWORD   m_relproc;      // relative jmp
    8.  
    9.     void Init(DWORD proc, void* pThis)
    10.     {
    11.         m_mov = 0x042444C7;  //C7 44 24 0C
    12.         m_this = PtrToUlong(pThis);
    13.         m_jmp = 0xe9;
    14.         m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
    ....
     
  11. Im

    Im New Member

    Публикаций:
    0
    Регистрация:
    19 янв 2008
    Сообщения:
    10
    Спасибо Вам за помощь leo! К сожалению я плохо знаю ассемблер, но теперь стало все понятно
    Прошу пощения, за такой вопрос.
     
  12. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Это не беда, главное иметь под друкой справочник команд IA32 том 2. Тогда по многим вопросам можно самому легко разобраться. В данном случае открываешь описание jmp и видишь, что все jmp с непосредственными операндами - относительные:
    Усе ясно и понятно ;)
     
  13. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    1. Когда регистрируем окно:
    Код (Text):
    1. WNDCLASS wc = {0};
    2. ...
    3. wc.cbWndExtra = sizeof (this);
    4. ...
    2. Когда создаём окно:
    Код (Text):
    1. ...
    2. CreateWindowEx (................., this);
    3. ...
    3. Основная процедура ВСЕХ окон (кроме диалога).
    Код (Text):
    1. ...
    2. TWnd* pThis;
    3. if ((pThis = (TWnd*) ::GetWindowLong (hWnd, 0)) != NULL)
    4. {
    5.     return pThis -> VirtualMsgMap (msg, wparam, lparam);
    6. }
    7. else if (msg == WM_CREATE)
    8. {
    9.     pThis = *((TWnd**) lparam);
    10.     ::SetWindowLong (hWnd, 0, (LONG) pThis);
    11.     pThis -> VirtualOnWmCreate (hWnd);
    12.     return 0;
    13. }
    14. else return ::DefWindowProc (hWnd, msg, wparam, lparam);
    15.  
    16. ...
     
  14. Ustus

    Ustus New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2005
    Сообщения:
    834
    Адрес:
    Харьков
    [offtop]
    Угу... том 2a и том 2b...
    Напомнило:
    Фрунзе А.В. Микроконтроллеры? Это же просто! том 4, 464 с.
    [/offtop]
     
  15. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Ustus
    Ну дык в 99.9% можно юзать "древний" мануальчик, в котором все тома состоят из одной части ;)
    А в 2a, 2b заглядывать только по необходимости (SSE3 и т.д. + 64-битный режим)
     
  16. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    AsmGuru62
    Как вариант можно сохранять указатель в GWL_USERDATA
     
  17. Im

    Im New Member

    Публикаций:
    0
    Регистрация:
    19 янв 2008
    Сообщения:
    10
    leo
    Ustus
    Спасибо! Обязательно раздобуду справочник и буду изучать ассемблер как следует, т.е. уже начал.

    AsmGuru62
    Спасибо за такой вариант

    Great
    Вот сейчас сохраняю в GWL_USERDATA, только решил посмотреть как у други это все делается..

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

    class A
    {
    public:
    BOOL GetWndProc(CWindow* Window)
    {
    ....
    pfnOldWndProc = xWindow -> WndProc;
    ....
    }
    ...
    protected:
    LRESULT (CALLBACK XWindow::* pfnOldWndProc)(HWND,UINT,WPARAM,LPARAM) ;

    }
     
  18. Im

    Im New Member

    Публикаций:
    0
    Регистрация:
    19 янв 2008
    Сообщения:
    10
    Конечно если так вообще можно делать...
     
  19. Im

    Im New Member

    Публикаций:
    0
    Регистрация:
    19 янв 2008
    Сообщения:
    10
    Что-то я жестко ступил, после прочтения одной статьи, незнаю как у них это работает вообще.. Только ведь методы неразделяются между объектами.
    Прошу прощения, за пост