Замыслил я написать класс для авторесайзинга контролов расположенных на окне моего приложения. Для этого мне надо сабклассить мою же оконную процедуру. Т.е. у меня в классе есть оконная процедура и где-нибудь при инициализации объекта класса я сабклассю. Код (Text): class Resizing { public: void SetMainWnd( HWND hWnd ); LRESULT CALLBACK MyWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); ... } void Resizing::SetMainWnd( HWND hWnd ) { ... m_OrigWndProc = SetWindowLongPtr( hWnd, GWLP_WNDPROC, (LONG)MyWndProc ); ... } Ну есесно это не работает: имеем ошибку c2440: "type cast" : cannot convert from LRESULT (__stdcall Resizing::*)(HWND, UINT, WPARAM, LPARAM) to LONG После долгого кумеканья (лазанья по инету) понял, что надо делать MyWndProc статиком и потом в ней получать объект класса как-нить по hWnd. Ето еще пока не реализовал (но вроде это должно работать), просто хотелось бы узнать может есть еще какие-то способы решения данной проблемы...
Есть такое дело. И что плохо - из статической функции к данным класса (и this вообще) доступа не получить. Обычно в колбэках есть параметр, называемый application defined value. В него и передают this, доставая потом в колбэке: Код (Text): DWORD WINAPI CClass::ThreadProc(LPVOID lpVar){ CClass* pThis = reinterpret_cast<CClass*>(lpVar); pThis->DoSomething(); // работаем через pThis ... } В твоём случае сложнее. Можно использовать трюк с подменой адреса возврата, как делает ATL. Это хак. Можно помещать this в само окно (в GWLP_USERDATA), если само окно его не использует, и доставать в колбэке. Или в DWLP_USER, если место под него выделено. Это ненадёжно, т.к. окно может использовать эти ячейки под свои данные. Можно использовать глобальную переменную, если экземпляр твоего класса единственный. Или глобальную хеш-таблицу с парами handle => this, если классов может быть несколько. Кто-то скажет, что глобальные переменные - это плохо. Ну что ж, оборачивать WINAPI классами тоже не лучшая затея
В бормановском VCL делается и то и другое. Для общих целей this\self цепляется к окну через SetProp\GetProp, а для автовызова методов WndProc используется хак-трюк: VirtualAlloc'ом выделяется память c атрибутом EXECUTE и в нее пишутся фиктивные WndProc окон, представляющие собой инструкцию call StdWndProc, за которой идет указатель на метод WndProc и this. StdWndProc попит адрес возврата и вызывает метод WndProc с параметром this
Ну в общем всем спасибо. this буду пихать по-видимому в GWLP_USERDATA. (впрочем вариант с глобальной переменной тоже имеет право на жизнь, хоть это и не есть гут) IceStudent Я об этом как-то особо не задумывался, просто как-то на уровне подсознания чувствую, что это как-то нехорошо, а ЧТО нехорошо выразить не могу Так почему же это не лучшая затея?
MegaDwarf Просто в большинстве случаев это избыточно. Или изобретаешь велосипед (ведь есть WTL/MFC, где то, что ты пытаешься сделать, реализовано). Имхо, конечно. А вот об этом не знал, возьму на заметку.
IceStudent Возьми, может пригодиться В т.ч. и для баловства с VCL-прогами, т.к. через GetProp из них можно выцепить указатели на оконные контролы, а при некоторых ухищрениях и на неоконные типа TLabel. А там и доступ к полям и виртуальным методам - широкий простор для развлечения
Здравствуйте Немного исследую WTL, но к сожалению пока что только начал изучать ассемблер.. Не могу понять один момент в переходнике для оконной процедуры. Вот код переходника: Код (Text): // тут все понятно, hWnd в стеке заменяется на this m_mov = 0x042444C7; //C7 44 24 0C m_this = PtrToUlong(pThis); // команда безусловного перехода m_jmp = 0xe9; // по адресу новой оконной процедуры m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk))); -только непонятна одна вещь что за адрес m_relproc выходит после выполнения операции: Код (Text): DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk))); Почему тут нельзя указывать адресс просто вот так: Код (Text): m_relproc = DWORD((INT_PTR)proc; Помогите пожалуйста разобраться с этим
Потому, что команда jmp = E9h - это не абсолютный, а относительный переход и соотв-но операндом является не сам адрес перехода, а его смещение относительно адреса команды, следующей за jmp
Чтобы понятнее было вот кусок когда с переходником побольше: Код (Text): #pragma pack(push,1) struct _stdcallthunk { DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd) DWORD m_this; // BYTE m_jmp; // jmp WndProc DWORD m_relproc; // relative jmp void Init(DWORD proc, void* pThis) { m_mov = 0x042444C7; //C7 44 24 0C m_this = PtrToUlong(pThis); m_jmp = 0xe9; m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk))); ....
Спасибо Вам за помощь leo! К сожалению я плохо знаю ассемблер, но теперь стало все понятно Прошу пощения, за такой вопрос.
Это не беда, главное иметь под друкой справочник команд IA32 том 2. Тогда по многим вопросам можно самому легко разобраться. В данном случае открываешь описание jmp и видишь, что все jmp с непосредственными операндами - относительные: Усе ясно и понятно
1. Когда регистрируем окно: Код (Text): WNDCLASS wc = {0}; ... wc.cbWndExtra = sizeof (this); ... 2. Когда создаём окно: Код (Text): ... CreateWindowEx (................., this); ... 3. Основная процедура ВСЕХ окон (кроме диалога). Код (Text): ... TWnd* pThis; if ((pThis = (TWnd*) ::GetWindowLong (hWnd, 0)) != NULL) { return pThis -> VirtualMsgMap (msg, wparam, lparam); } else if (msg == WM_CREATE) { pThis = *((TWnd**) lparam); ::SetWindowLong (hWnd, 0, (LONG) pThis); pThis -> VirtualOnWmCreate (hWnd); return 0; } else return ::DefWindowProc (hWnd, msg, wparam, lparam); ...
[offtop] Угу... том 2a и том 2b... Напомнило: Фрунзе А.В. Микроконтроллеры? Это же просто! том 4, 464 с. [/offtop]
Ustus Ну дык в 99.9% можно юзать "древний" мануальчик, в котором все тома состоят из одной части А в 2a, 2b заглядывать только по необходимости (SSE3 и т.д. + 64-битный режим)
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) ; }
Что-то я жестко ступил, после прочтения одной статьи, незнаю как у них это работает вообще.. Только ведь методы неразделяются между объектами. Прошу прощения, за пост