К куче существующих окон необходимо заменить WindowProc.
Первое, что пришло в голову, создать класс CMyWnd,
содержащий метод MyWndowProc и атрибут OldWindwProc.
Одна универсальная функция неэффективна, т.к. единственное
различие для вызовов из разных окон это значение hWnd?
а по нему искать каждый раз OldWindwProc не хочется.
Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?
Вопрос 2: Как иначе организовать множество функций различающихся только
значением атрибута OldWindwProc.
Здравствуйте Vovchik, вы писали:
V>К куче существующих окон необходимо заменить WindowProc. V>Первое, что пришло в голову, создать класс CMyWnd, V>содержащий метод MyWndowProc и атрибут OldWindwProc. V>Одна универсальная функция неэффективна, т.к. единственное V>различие для вызовов из разных окон это значение hWnd? V>а по нему искать каждый раз OldWindwProc не хочется.
V>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong? V>Вопрос 2: Как иначе организовать множество функций различающихся только V> значением атрибута OldWindwProc.
Здравствуйте Willi, вы писали:
W>Здравствуйте Vovchik, вы писали:
V>>К куче существующих окон необходимо заменить WindowProc. V>>Первое, что пришло в голову, создать класс CMyWnd, V>>содержащий метод MyWndowProc и атрибут OldWindwProc. V>>Одна универсальная функция неэффективна, т.к. единственное V>>различие для вызовов из разных окон это значение hWnd? V>>а по нему искать каждый раз OldWindwProc не хочется.
V>>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong? V>>Вопрос 2: Как иначе организовать множество функций различающихся только V>> значением атрибута OldWindwProc.
W>а если попробовать так
W>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC); W>::SetWindowLong(hWnd, GWL_WNDPROC, newProc);
Пардон не дочитал вопрос до конца.
Функция для SetWindowLong не может быть обычным методом класса.
Она должна быть либо глобыльной, либо static member,
проскольку обычные методы кроме указанных параметров
принимают неявный this
Дело в том что процедура может обслуживать не одно окно
а несколько, соответственно у нескольких hWnd может быть
один и тотже GWL_WNDPROC. Если не заморачиваться на этом,
то тебе достаточно иметь соответствие экземпяр класса <-> hWnd
И когда в твою новую процедуру свалится сообщение, отыскивать
нужный клас по пришедшему hWnd
Здравствуйте Willi, вы писали:
W>а если попробовать так
W>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC); W>::SetWindowLong(hWnd, GWL_WNDPROC, newProc);
В том-то весь и прикол, что newProc — если это
глобальная функция то все ОКей, а если это метод
какого-либо класса то C++ не возвращает указатель
на функцию.
Говорит:
cannot convert from 'void *(__thiscall CMyWnd::*)(void)' to 'void *'
Здравствуйте Vovchik, вы писали:
V>Здравствуйте Willi, вы писали:
W>>а если попробовать так
W>>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC); W>>::SetWindowLong(hWnd, GWL_WNDPROC, newProc); V>В том-то весь и прикол, что newProc — если это V>глобальная функция то все ОКей, а если это метод V>какого-либо класса то C++ не возвращает указатель V>на функцию. V>Говорит: V>cannot convert from 'void *(__thiscall CMyWnd::*)(void)' to 'void *'
Здравствуйте Willi, вы писали:
W>то тебе достаточно иметь соответствие экземпяр класса <-> hWnd W>И когда в твою новую процедуру свалится сообщение, отыскивать W>нужный клас по пришедшему hWnd
Я не совсем тебя понял, если ты имел в виду static функцию, то
она не имеет точно также доступа к не-static атрибутам :(
Или ты имел в виду что-то типа создания функции FindOldWindowProc (HWND hWnd) ?
Здравствуйте Vovchik, вы писали:
V>Здравствуйте Willi, вы писали:
W>>то тебе достаточно иметь соответствие экземпяр класса <-> hWnd W>>И когда в твою новую процедуру свалится сообщение, отыскивать W>>нужный клас по пришедшему hWnd
V>Я не совсем тебя понял, если ты имел в виду static функцию, то V>она не имеет точно также доступа к не-static атрибутам :(
совершенно верно, зато не принимает this :)
V>Или ты имел в виду что-то типа создания функции FindOldWindowProc (HWND hWnd) ?
что-то вроде того
Заводишь глобальную коллекцию
После подмены процедуры сохраняешь туда либибо старый адрес,
тогда твой класс тебе вообще не нужен, либо, если класс все-таки нужен
сохраняешь туда указатель на экземпляр класса.
А в своей оконной процедуре достаешь из коллекцию нужную тебе информацию
Здравствуйте Vovchik, вы писали:
V>Здравствуйте Willi, вы писали:
W>>а если попробовать так
W>>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC); W>>::SetWindowLong(hWnd, GWL_WNDPROC, newProc); V>В том-то весь и прикол, что newProc — если это V>глобальная функция то все ОКей, а если это метод V>какого-либо класса то C++ не возвращает указатель V>на функцию. V>Говорит: V>cannot convert from 'void *(__thiscall CMyWnd::*)(void)' to 'void *'
Ясен пень.
Указатель на метод класса реально состоит из 8-ми байт.
Не буду объяснять почему! Тут всё логично!
И вообще как бы ты хотел, чтоб вызвалась твоя функция-член класса?
Кто ей this-указатель будет передавать?
Реально запускается общая для всех классов ф-я, в которой и происходит
нечто похожее на FindInstanceByHandle(HWND);
А далее запускается нужная функция.
Так что прийдётся тебе реализовать ф-ю подобную описанной
или ...
Коварный метод — засунуть указатель на объект класса куды-нить
вместо например ID (если он конэшна не используется).
Работает быстро, но если объект задействут его — считай
"Программа выполнила..."
Ну и, естественно, чтобы вызвать старый обработчик — метод класса
ты должен знать объект какого класса вызвал твой обработчик.
Либо, как в билдере, все они должны наследоваться от одного класса
и обработчик должен быть виртуальной функцией.
Тогда просто вызываешь обработчик самого верхнего класса.
Реально запустится тот, что тебе нужен (объяснил как умел — а
вообще читай про наследование и виртуальность).
В билдере ((TControl*)unknown_control)->WindowProc;
Здравствуйте Vovchik, вы писали:
V>К куче существующих окон необходимо заменить WindowProc. V>Первое, что пришло в голову, создать класс CMyWnd, V>содержащий метод MyWndowProc и атрибут OldWindwProc. V>Одна универсальная функция неэффективна, т.к. единственное V>различие для вызовов из разных окон это значение hWnd? V>а по нему искать каждый раз OldWindwProc не хочется.
Если тебе надо только узнать значение OldWindowProc,
то почему-бы не сохранить его используя
SetWindowLong(...,GWL_USERDATA,...)
Здравствуйте Vitek, вы писали:
V>Либо, как в билдере, все они должны наследоваться от одного класса
IMHO, это не возможно, т.к. окно уже существует и это не мое окно.
Но проблема решена:
CWnd::SubclassWindow( HWND hWnd) — захватывает существующее окно
и обрабатывает сообщения переписанной WindowProc
Здравствуйте Vitek, вы писали:
V>Здравствуйте Vovchik, вы писали:
V>>Здравствуйте Willi, вы писали:
W>>>а если попробовать так
W>>>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC); W>>>::SetWindowLong(hWnd, GWL_WNDPROC, newProc); V>>В том-то весь и прикол, что newProc — если это V>>глобальная функция то все ОКей, а если это метод V>>какого-либо класса то C++ не возвращает указатель V>>на функцию. V>>Говорит: V>>cannot convert from 'void *(__thiscall CMyWnd::*)(void)' to 'void *'
V>Ясен пень. V>Указатель на метод класса реально состоит из 8-ми байт. V>Не буду объяснять почему! Тут всё логично!
Не ври! С каких это пор указатели стали 64-битными? Еще даже винды таковой нету!
Здравствуйте Vovchik, вы писали:
V>К куче существующих окон необходимо заменить WindowProc. V>Первое, что пришло в голову, создать класс CMyWnd, V>содержащий метод MyWndowProc и атрибут OldWindwProc. V>Одна универсальная функция неэффективна, т.к. единственное V>различие для вызовов из разных окон это значение hWnd? V>а по нему искать каждый раз OldWindwProc не хочется.
V>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong? V>Вопрос 2: Как иначе организовать множество функций различающихся только V> значением атрибута OldWindwProc.
В случае с собственным классом окна можно пойти двуме путями:
1. Зарегистрировать класс с процедурой DefWindowProc, а потом см. выше
2. Пердавать this в последнем параметре CreateWindowEx:
Здравствуйте Yarik, вы писали:
V>>Ясен пень. V>>Указатель на метод класса реально состоит из 8-ми байт. V>>Не буду объяснять почему! Тут всё логично!
Y>Не ври! С каких это пор указатели стали 64-битными? Еще даже винды таковой нету!
А sizeof слабо сделать? (А винда уже есть, правда не у всех).
Здравствуйте Alex Fedotov, вы писали:
AF>Здравствуйте Yarik, вы писали:
V>>>Ясен пень. V>>>Указатель на метод класса реально состоит из 8-ми байт. V>>>Не буду объяснять почему! Тут всё логично!
Y>>Не ври! С каких это пор указатели стали 64-битными? Еще даже винды таковой нету!
AF>А sizeof слабо сделать? (А винда уже есть, правда не у всех).
А ты влепи #pragma pack (1) и посмотри, что получится!!!
Поинтеры на наших PC были, есть и будут 32-битными. Потому что процы у нас такие.
Вот войдет в жизнь (нашу) Пень4 (tm), тогда и говори, что this размером 8 байт.
Здравствуйте Yarik, вы писали:
AF>>А sizeof слабо сделать? (А винда уже есть, правда не у всех).
Y>А ты влепи #pragma pack (1) и посмотри, что получится!!! Y>Поинтеры на наших PC были, есть и будут 32-битными. Потому что процы у нас такие. Y>Вот войдет в жизнь (нашу) Пень4 (tm), тогда и говори, что this размером 8 байт.
Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита.
Но указатель на функцию-член класса действительно 8 байт (собственно
указатель + смещение от this), что проверяется простым sizeof и от
#pragma pack это не зависит.
BTW, P4 — 32-разрядный, это ты видно с Itanium путаешь.
Здравствуйте Alex Fedotov, вы писали:
AF>Здравствуйте Yarik, вы писали:
AF>Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита. AF>Но указатель на функцию-член класса действительно 8 байт (собственно AF>указатель + смещение от this), что проверяется простым sizeof и от AF>#pragma pack это не зависит.
Вот это уже на правду похоже. А ещё похоже, что мы с вами спорим каждый о своём.
О чем глубоко сожалею. :-(
Зато как интересно было :-)
AF>BTW, P4 — 32-разрядный, это ты видно с Itanium путаешь.
Да, ты прав. Но это уже не имеет значения. :-)
НО: Если не затруднит намылить, как же всё-таки брать такой указатель на член класса?
Здравствуйте Alex Fedotov, вы писали:
AF>Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита. AF>Но указатель на функцию-член класса действительно 8 байт (собственно AF>указатель + смещение от this), что проверяется простым sizeof и от AF>#pragma pack это не зависит.
Все почти верно, но не "указатель + смещение от this", а указатель + this. Указатель на функцию это простой указатель, а this нужен для пердачи в качестве скрытого параметра.
Кстати, а ни кто не пробывал создать унивирсальный указатель на функцию любого класса. На asm-е я такие штуки прокатывал, а как на Плюсах?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте VladD2, вы писали:
VD>Все почти верно, но не "указатель + смещение от this", а указатель + this. Указатель на функцию это простой указатель, а this нужен для пердачи в качестве скрытого параметра.
Нет, именно смещение, которое надо прибавить к this во время вызова. This ведь не известен
до момента вызова и к собственно указателю отношения не имеет:
CPtr p = CC::A; // при чем тут this?
Тем не менее размер CPtr — 8 байт, поскольку включает адрес функции CA::A и то самое
смещение (которое в данном случае равно нулю). А вот если бы было
CPtr p = CC::B;
то смещение было бы уже не ноль, а 4 (при выравнивании структур на 4 байта).
Продолжение моего предыдущего примера:
int callme(CPtr p)
{
CC c;
return (c.*p)();
}
CPtr p = CC::B;
return callme(p);
; вот какой код компилятор генерирует для вызова метода
push 4
mov eax, offset CB::B
pop ecx
push ecx ; обрати внимание, в стек идут 8 байт — адрес метода
push eax ; и пресловутое смещение
call callme
; а вот так выглядит реализация функции callme в его исполнении
callme:
push ebp
mov ebp, esp
sub esp, 0Ch
mov eax, dword ptr [ebp + 0Ch] ; это смещение
lea ecx, [ebp + eax — 0Ch] ; вот оно прибавляется к адресу объекта и передается в
; качестве скрытого параметра
call dword ptr [ebp + 08h]
leave
ret