Адрес метода
От: Vovchik  
Дата: 31.05.01 07:10
Оценка:
К куче существующих окон необходимо заменить WindowProc.
Первое, что пришло в голову, создать класс CMyWnd,
содержащий метод MyWndowProc и атрибут OldWindwProc.
Одна универсальная функция неэффективна, т.к. единственное
различие для вызовов из разных окон это значение hWnd?
а по нему искать каждый раз OldWindwProc не хочется.

Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?
Вопрос 2: Как иначе организовать множество функций различающихся только
значением атрибута OldWindwProc.
Re: Адрес метода
От: Willi  
Дата: 31.05.01 08:19
Оценка:
Здравствуйте Vovchik, вы писали:

V>К куче существующих окон необходимо заменить WindowProc.

V>Первое, что пришло в голову, создать класс CMyWnd,
V>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>Одна универсальная функция неэффективна, т.к. единственное
V>различие для вызовов из разных окон это значение hWnd?
V>а по нему искать каждый раз OldWindwProc не хочется.

V>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?

V>Вопрос 2: Как иначе организовать множество функций различающихся только
V> значением атрибута OldWindwProc.

а если попробовать так

oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC);
::SetWindowLong(hWnd, GWL_WNDPROC, newProc);
\/\/i||i
Re[2]: Адрес метода
От: Willi  
Дата: 31.05.01 08:34
Оценка:
Здравствуйте 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
\/\/i||i
Re[2]: Адрес метода
От: Vovchik  
Дата: 31.05.01 08:34
Оценка:
Здравствуйте 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 *'
Re[3]: Адрес метода
От: Willi  
Дата: 31.05.01 08:37
Оценка:
Здравствуйте 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 *'

см. выше
\/\/i||i
Re[3]: Адрес метода
От: Vovchik  
Дата: 31.05.01 08:40
Оценка:
Здравствуйте Willi, вы писали:

W>то тебе достаточно иметь соответствие экземпяр класса <-> hWnd

W>И когда в твою новую процедуру свалится сообщение, отыскивать
W>нужный клас по пришедшему hWnd

Я не совсем тебя понял, если ты имел в виду static функцию, то
она не имеет точно также доступа к не-static атрибутам :(
Или ты имел в виду что-то типа создания функции FindOldWindowProc (HWND hWnd) ?
Re[4]: Адрес метода
От: Willi  
Дата: 31.05.01 08:55
Оценка:
Здравствуйте Vovchik, вы писали:

V>Здравствуйте Willi, вы писали:


W>>то тебе достаточно иметь соответствие экземпяр класса <-> hWnd

W>>И когда в твою новую процедуру свалится сообщение, отыскивать
W>>нужный клас по пришедшему hWnd

V>Я не совсем тебя понял, если ты имел в виду static функцию, то

V>она не имеет точно также доступа к не-static атрибутам :(
совершенно верно, зато не принимает this :)

V>Или ты имел в виду что-то типа создания функции FindOldWindowProc (HWND hWnd) ?

что-то вроде того

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

А в своей оконной процедуре достаешь из коллекцию нужную тебе информацию
\/\/i||i
Re[3]: Адрес метода
От: Vitek  
Дата: 31.05.01 09:00
Оценка:
Здравствуйте 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;

Что не понял — спрашивай.
Re: Адрес метода
От: nickp  
Дата: 31.05.01 11:24
Оценка:
Здравствуйте Vovchik, вы писали:

V>К куче существующих окон необходимо заменить WindowProc.

V>Первое, что пришло в голову, создать класс CMyWnd,
V>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>Одна универсальная функция неэффективна, т.к. единственное
V>различие для вызовов из разных окон это значение hWnd?
V>а по нему искать каждый раз OldWindwProc не хочется.

Если тебе надо только узнать значение OldWindowProc,
то почему-бы не сохранить его используя
SetWindowLong(...,GWL_USERDATA,...)
Re[4]: Адрес метода
От: Vovchik  
Дата: 31.05.01 11:30
Оценка:
Здравствуйте Vitek, вы писали:

V>Либо, как в билдере, все они должны наследоваться от одного класса

IMHO, это не возможно, т.к. окно уже существует и это не мое окно.

Но проблема решена:
CWnd::SubclassWindow( HWND hWnd) — захватывает существующее окно
и обрабатывает сообщения переписанной WindowProc

Всем кто откликнулся большое спасибо.
Re[4]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 16.07.01 06:08
Оценка:
Здравствуйте 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-битными? Еще даже винды таковой нету!
Re: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 16.07.01 06:30
Оценка:
Здравствуйте Vovchik, вы писали:

V>К куче существующих окон необходимо заменить WindowProc.

V>Первое, что пришло в голову, создать класс CMyWnd,
V>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>Одна универсальная функция неэффективна, т.к. единственное
V>различие для вызовов из разных окон это значение hWnd?
V>а по нему искать каждый раз OldWindwProc не хочется.

V>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?

V>Вопрос 2: Как иначе организовать множество функций различающихся только
V> значением атрибута OldWindwProc.

Я делал так:

//////////////////////////////////////////////////////////////////////

class CButton
{
friend LRESULT CALLBACK __ButtonProc__ (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

public:
// ... всякие там методы
public:
// Windowing
HWND hWnd; // для удобства (или для проверки правильности соответсвия экземпляра окну)
HWND Create (HINSTANCE hInstance, HWND hWndParent);

//Message handling
WNDPROC fnDefButtonProc;
LRESULT OnWndMessage (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
}

//...............................................

//////////////////////////////////////////////////////////////////////

HWND CButton::Create (HINSTANCE hInstance, HWND hWndParent)
{
hWnd = CreateWindowEx (0,"BUTTON", NULL,
WS_CHILD /*| WS_VISIBLE*/ | BS_FLAT | BS_NOTIFY,
4,24,48,18,
hWndParent, NULL, hInstance, NULL);
assert (hWnd != NULL);

fnDefButtonProc = (WNDPROC) SetWindowLongA (hWnd, GWL_WNDPROC, (LONG) __ButtonProc__);
SetWindowLong (hWnd, GWL_USERDATA, (LONG) this);

return hWnd;
}

//////////////////////////////////////////////////////////////////////

LRESULT CButton::OnWndMessage (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// ............
}
return CallWindowProc (fnDefButtonProc, hWnd, uMsg, wParam, lParam);
}


//////////////////////////////////////////////////////////////////////

LRESULT CALLBACK __ButtonProc__ (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CButton *btn = (CButton*) GetWindowLong (hWnd, GWL_USERDATA);
assert (btn != NULL);
return btn->OnWndMessage (hWnd, uMsg, wParam, lParam);
}

//////////////////////////////////////////////////////////////////////

В случае с собственным классом окна можно пойти двуме путями:
1. Зарегистрировать класс с процедурой DefWindowProc, а потом см. выше
2. Пердавать this в последнем параметре CreateWindowEx:

hWnd = CreateWindowEx (WS_EX_TOOLWINDOW,
"MBSAppWClass",
"Multi Block Storage",
WS_POPUP|WS_DLGFRAME,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
65,
NULL,
NULL,
(HINSTANCE) hInstance,
this); // <---

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

LRESULT CALLBACK
__MainWndProcCaller__ (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static CYourClass* YourClass;
if (uMsg == WM_CREATE)
{
CREATESTRUCT *cs = (CREATESTRUCT*) lParam;
YourClass = (CYourClass*) cs->lpCreateParams;
assert (Engine != NULL);
return 1;
}
return YourClass->OnWndMessage (hWnd, uMsg, wParam, lParam);
}

Для диалога это будет выглядеть немного по-другому:


void COptions::BringDialog(HWND hWndParent, PBYTE pbLocalSpacingType)
{
m_pbLocalSpacingType = pbLocalSpacingType;
DialogBoxParam (
(HINSTANCE) GetWindowLong (hWndParent, GWL_HINSTANCE),
MAKEINTRESOURCE (IDD_OPTIONS),
hWndParent,
__OptionsDlgProcCaller__,
(LPARAM) this);
}

//////////////////////////////////////////////////////////////////////

BOOL CALLBACK
__OptionsDlgProcCaller__ (
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
static COptions* This = false;
if (uMsg == WM_INITDIALOG)
This = (COptions*) lParam;
if (This)
return This->OnDlgMessage (hDlg, uMsg, wParam, lParam);
else
return FALSE;
}
Re[5]: Адрес метода
От: Alex Fedotov США  
Дата: 16.07.01 07:13
Оценка:
Здравствуйте Yarik, вы писали:

V>>Ясен пень.

V>>Указатель на метод класса реально состоит из 8-ми байт.
V>>Не буду объяснять почему! Тут всё логично!

Y>Не ври! С каких это пор указатели стали 64-битными? Еще даже винды таковой нету!


А sizeof слабо сделать? (А винда уже есть, правда не у всех).
-- Alex Fedotov
Re[6]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 16.07.01 12:35
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Здравствуйте Yarik, вы писали:


V>>>Ясен пень.

V>>>Указатель на метод класса реально состоит из 8-ми байт.
V>>>Не буду объяснять почему! Тут всё логично!

Y>>Не ври! С каких это пор указатели стали 64-битными? Еще даже винды таковой нету!


AF>А sizeof слабо сделать? (А винда уже есть, правда не у всех).


А ты влепи #pragma pack (1) и посмотри, что получится!!!
Поинтеры на наших PC были, есть и будут 32-битными. Потому что процы у нас такие.
Вот войдет в жизнь (нашу) Пень4 (tm), тогда и говори, что this размером 8 байт.
Re[7]: Адрес метода
От: Alex Fedotov США  
Дата: 16.07.01 14:37
Оценка:
Здравствуйте 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
Re[8]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 16.07.01 15:17
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Здравствуйте Yarik, вы писали:


AF>Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита.

AF>Но указатель на функцию-член класса действительно 8 байт (собственно
AF>указатель + смещение от this), что проверяется простым sizeof и от
AF>#pragma pack это не зависит.

Вот это уже на правду похоже. А ещё похоже, что мы с вами спорим каждый о своём.
О чем глубоко сожалею. :-(
Зато как интересно было :-)

AF>BTW, P4 — 32-разрядный, это ты видно с Itanium путаешь.

Да, ты прав. Но это уже не имеет значения. :-)

НО: Если не затруднит намылить, как же всё-таки брать такой указатель на член класса?
Re[9]: Адрес метода
От: Alex Fedotov США  
Дата: 16.07.01 19:35
Оценка: 10 (1)
Здравствуйте Yarik, вы писали:

Y>НО: Если не затруднит намылить, как же всё-таки брать такой указатель на член класса?


class CA
{
public:
int A()
{ return 1; }
protected:
int m_a;

};

class CB
{
public:
int B()
{ return 2; }
protected:
int m_b;

};

class CC : public CA, public CB
{
public:
int C()
{ return 3; }
protected:
int m_c;
};

// так объявляется указатель на член класса
typedef int (CC::*CPtr)();


int
_tmain(
int argc,
_TCHAR * argv[]
)
{
// это к вопросу о размере указателя
printf("sizeof(CPtr) = %d\n", sizeof(CPtr));

// так инициализируется указатель на член класса
CPtr p = CC::A;

CC c;

// a так вызывается функция по указателю
return (c.*p)();
}
-- Alex Fedotov
Re[8]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 16.07.01 20:03
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита.

AF>Но указатель на функцию-член класса действительно 8 байт (собственно
AF>указатель + смещение от this), что проверяется простым sizeof и от
AF>#pragma pack это не зависит.

Все почти верно, но не "указатель + смещение от this", а указатель + this. Указатель на функцию это простой указатель, а this нужен для пердачи в качестве скрытого параметра.

Кстати, а ни кто не пробывал создать унивирсальный указатель на функцию любого класса. На asm-е я такие штуки прокатывал, а как на Плюсах?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 16.07.01 20:06
Оценка:
Здравствуйте Yarik, вы писали:

Y> hWnd = CreateWindowEx (0,"BUTTON", NULL,

Y> WS_CHILD /*| WS_VISIBLE*/ | BS_FLAT | BS_NOTIFY,
Y> 4,24,48,18,
Y> hWndParent, NULL, hInstance, NULL);
Y> assert (hWnd != NULL);
Y>
Y> fnDefButtonProc = (WNDPROC) SetWindowLongA (hWnd, GWL_WNDPROC, (LONG) __ButtonProc__);
Y> SetWindowLong (hWnd, GWL_USERDATA, (LONG) this);

Интересно! А что произойдет если в GWL_USERDATA окна к которому Вы подключаетесь будет лежать какая то информация?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Адрес метода
От: Alex Fedotov США  
Дата: 16.07.01 21:12
Оценка:
Здравствуйте 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
-- Alex Fedotov
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.