Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Сделать калбэк функцию окна объектной, велосипед 
:(
    Опции темы
Alexeis
Дата 4.4.2013, 11:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



  Собственно одно решение нашел в исходниках билдера/делфи. Для каждого объекта выделяется блок памяти под функцию и указатель на объект . В область функции зафигачивают машинные коды для перехода по адресу объектного обработчика ну и дополнительно инициализируют this . 
  Может кто-то встречал более переносимое решение чем вставка машинных кодов и правка адресов для перехода? 

  Исходников класса CWnd, так можно было еще там подсмотреть решение.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Dem_max
Дата 4.4.2013, 11:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Смотря что за велосипед


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
GremlinProg
Дата 4.4.2013, 11:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(Alexeis @  4.4.2013,  13:19 Найти цитируемый пост)
Для каждого объекта выделяется блок памяти под функцию и указатель на объект . В область функции зафигачивают машинные коды для перехода по адресу объектного обработчика ну и дополнительно инициализируют this

Зачем это? Обработкой всех сообщений очереди занимается поток. Один конкретный поток!

Есть ситуации, когда несколько потоков имеют очередь сообщений, но любое окно может принадлежать только одному потоку, следовательно защищать передачу объекта в процедуру окна бессмысленно, ему ничего не грозит даже в глобальном scope.


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
borisbn
Дата 4.4.2013, 12:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 6
Всего: 135



а чем std:: или boost:: или на худой конец my_bicycle::bind  не угодил ?



--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
Alexeis
Дата 4.4.2013, 14:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Может не совсем точно объяснил. Задача сделать функцию типа 
Код

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
или
INT_PTR CALLBACK DialogProc(
  _In_  HWND hwndDlg,
  _In_  UINT uMsg,
  _In_  WPARAM wParam,
  _In_  LPARAM lParam
);

Не статическим членом класса.

К примеру взываем функцию 
Код

HWND WINAPI CreateDialog(
  _In_opt_  HINSTANCE hInstance,
  _In_      LPCTSTR lpTemplate,
  _In_opt_  HWND hWndParent,
  _In_opt_  DLGPROC lpDialogFunc
);

или 

::SetWindowLongPtr (hwndWindow, GWLP_WNDPROC, (LONG_PTR) fnNewProcedure);


Для создания диалога, а диалог у нас обернут в класс С++ . 

Очевидно boost::function никак не станет вместо WndProc или DialogProc .

Подсмотрел также решение MFC
Они решили вопрос так. Создали глобальрный ассоциативный массив СHandleMap, который с каждым хэндлом ассоциирует указатель на класс CWnd* . Функция окна одна на всех, при приходе очередного сообщения функция лезет в СHandleMap, находит правильный экземпляр наследник CWnd* и вызывает его виртуальную функцию обработки оконных сообщений. Это решение не такое быстрое, но универсальное. 

Мне хотелось как раз найти вот такое решение как привел borisbn. Cразу вспомнилось boost::function, но видимо не провернуть такой трюк никак. Пока что есть только решение от Microsoft. 



--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
borisbn
Дата 4.4.2013, 14:59 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 6
Всего: 135



Alexeis, я это делал следующим (да... знаю, что извращённым) способом:

Код
class MyClass {
private:
    static LRESULT CALLBACK s_WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {
        MyClass * self = ( MyClass * )GetWindowLongPtr( hWnd, GWL_USERDATA );
        return self->WndProc( hWnd, message, wParam, lParam );
    }
    LRESULT WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {
        ...
    }


    void installWindowProc( HWND hWnd ) {
        SetWindowLongPtr( hWnd, GWL_USERDATA, (LONG_PTR)this );
        SetWindowLongPtr( m_wnd, GWL_WNDPROC, (LONG_PTR)s_WndProc );
    }
};



--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
GremlinProg
Дата 4.4.2013, 15:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(Alexeis @  4.4.2013,  13:19 Найти цитируемый пост)
Для каждого объекта выделяется блок памяти под функцию и указатель на объект . В область функции зафигачивают машинные коды для перехода по адресу объектного обработчика ну и дополнительно инициализируют this

Скорее всего, это реализация событий у борланда, с процедурой окна она не имеет ничего общего, просто борланд унифицирует таким образом делегат (этот подход у него как минимум с Delphi 3-4)

Это когда указатель на метод склеивается с указателем на объект и представляется универсальным функтором, который можно вызвать как простую функцию в любом контексте.

Насколько я помню, у Borland'а существует глобальный указатель, типа pCreateObject, в который записывается указатель на объект, перед вызовом CreateWindow. Первый вызов процедуры окна просто берет этот указатель и пишет в недра свойств окна или WindowLong (не помню точно), при этом, меняет процедуру окна на штатную, которая извлекает указатель из недр и вызывает для него метод WndProc.


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Alexeis
Дата 4.4.2013, 16:05 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Цитата(borisbn @  4.4.2013,  15:59 Найти цитируемый пост)
Alexeis, я это делал следующим (да... знаю, что извращённым) способом:

  А чо, четенькое решенице. Быстрое и довольно честное.

 
Цитата(GremlinProg @  4.4.2013,  16:01 Найти цитируемый пост)
Скорее всего, это реализация событий у борланда, с процедурой окна она не имеет ничего общего, просто борланд унифицирует таким образом делегат (этот подход у него как минимум с Delphi 3-4)

Это когда указатель на метод склеивается с указателем на объект и представляется универсальным функтором, который можно вызвать как простую функцию в любом контексте.

  Делегаты у борлонда существуют ввиде структуры из 2х полей. Делегат имеет размер 8 байт и не может быть записан вместо адреса функции и тем более винда не будет вызывать его. 

Вот собственно привожу весь код создания объектного калбека. Тут правда паскаль, но код такой сиподобный ^. тоже что и -> , знак $ заменяет 0x , Move тоже что и memcpy только аргументы поменяны местами. @ тоже что & 
Код

    SetWindowLongPtr(Result, GWL_WNDPROC, IntPtr(MakeObjectInstance(AMethod)));


Код


function CalcJmpOffset(Src, Dest: Pointer): Longint;
begin
  Result := IntPtr(Dest) - (IntPtr(Src) + 5);
end;

function MakeObjectInstance(AMethod: TWndMethod): Pointer;
const
  BlockCode: array[1..CodeBytes] of Byte = (
{$IF Defined(CPUX86)}
    $59,                       { POP ECX }
    $E9);                      { JMP StdWndProc }
{$ELSEIF Defined(CPUX64)}
    $41,$5b,                   { POP R11 }
    $FF,$25,$00,$00,$00,$00);  { JMP [RIP+0] }
{$IFEND}
  PageSize = 4096;
var
  Block: PInstanceBlock;
  Instance: PObjectInstance;
begin
  if InstFreeList = nil then
  begin
    Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    Block^.Next := InstBlockList;
    Move(BlockCode, Block^.Code, SizeOf(BlockCode));
{$IF Defined(CPUX86)}
    Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdWndProc));
{$ELSEIF Defined(CPUX64)}
    Block^.WndProcPtr := @StdWndProc;
{$IFEND}
    Instance := @Block^.Instances;
    repeat
      Instance^.Code := $E8;  { CALL NEAR PTR Offset }
      Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
      Instance^.Next := InstFreeList;
      InstFreeList := Instance;
      Inc(PByte(Instance), SizeOf(TObjectInstance));
    until IntPtr(Instance) - IntPtr(Block) >= SizeOf(TInstanceBlock);
    InstBlockList := Block;
  end;
  Result := InstFreeList;
  Instance := InstFreeList;
  InstFreeList := Instance^.Next;
  Instance^.FMethod := AMethod;
end;



--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
GremlinProg
Дата 5.4.2013, 08:17 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(Alexeis @ 4.4.2013,  18:05)
Цитата(GremlinProg @  4.4.2013,  16:01 Найти цитируемый пост)
Скорее всего, это реализация событий у борланда, с процедурой окна она не имеет ничего общего, просто борланд унифицирует таким образом делегат (этот подход у него как минимум с Delphi 3-4)

Это когда указатель на метод склеивается с указателем на объект и представляется универсальным функтором, который можно вызвать как простую функцию в любом контексте.

  Делегаты у борлонда существуют ввиде структуры из 2х полей. Делегат имеет размер 8 байт и не может быть записан вместо адреса функции и тем более винда не будет вызывать его.

Alexeis, пытается троллить, забавно. В цитате я об этом и написал smile 
только размер делегата равен не 8 байтам, а равен размеру двух указателей (в x64 это 16 байт)
Цитата(Alexeis @  4.4.2013,  18:05 Найти цитируемый пост)
Тут правда паскаль, но код такой сиподобный ^. тоже что и -> , знак $ заменяет 0x , Move тоже что и memcpy только аргументы поменяны местами. @ тоже что & 

Тут большинство программисты и в общем случае понимают разницу между Object Pascal и Си smile
Цитата(Alexeis @  4.4.2013,  18:05 Найти цитируемый пост)
Вот собственно привожу весь код создания объектного калбека

Не советую этим пользоваться! Это бессмысленная "кучамала", первым комментарием уже сказал почему.


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
xvr
Дата 5.4.2013, 09:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(GremlinProg @  5.4.2013,  08:17 Найти цитируемый пост)
В цитате я об этом и написал

Не корысти ради, а токмо точности для -
У Борланда существуют оба варианта, и делегаты из 2х указателей (которые в С++ у них называются __closure), и эти самые thunk'и, которые позволяют склеить this и метод в один указатель и приделать еще что нибудь. Второе, в частности, когда то использовалось для реализации таблиц виртуальных методов в множественном наследовании. У них thunk'и двигали this

Но все это генерил компилятор в compile time, а то, что показал Alexis делает библиотека VCL в run time

Цитата(GremlinProg @  5.4.2013,  08:17 Найти цитируемый пост)
Не советую этим пользоваться!

Пользоваться можно, но только если других методов уже не осталось  smile 

PM MAIL   Вверх
Dem_max
Дата 5.4.2013, 10:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Я собственно использую __closure в С++ Builder, для того чтобы не делать функцию статической.
Для мелкософтовского компилятора это будет __thiscall 
Но это чисто компиляторозависимый велосипед.


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
GremlinProg
Дата 5.4.2013, 10:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(xvr @  5.4.2013,  11:27 Найти цитируемый пост)
Пользоваться можно, но только если других методов уже не осталось

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



--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
xvr
Дата 5.4.2013, 10:33 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(Dem_max @  5.4.2013,  10:07 Найти цитируемый пост)
Я собственно использую __closure в С++ Builder, для того чтобы не делать функцию статической.
Для мелкософтовского компилятора это будет __thiscall 

Неа. __closure в Builder'е - это модификатор типа указателя, а __thiscall - это calling conversion для функции. На уровне сорцов это сразу видно - __closure указатели вызываются как обычные функции (я имею в виду синтаксис вызова), а __thiscall как члены класса, т.е. для них явно нужно указать this (или вызывать в контексте метода класса, тогда this компилятор подставит сам)

Цитата(GremlinProg @  5.4.2013,  10:11 Найти цитируемый пост)
а я об этом как раз и писал, для передачи объекта в процедуру окна городить такой огород бессмысленно и неразумно

С этим согласен. Если только GWL_USERDATA не занят уже под что то другое. В VCL Borland не мог воспользоваться GWL_USERDATA для передачи this, т.к. GWL_USERDATA находится в распоряжении пользователя библиотеки VCL, а не её самой.

PM MAIL   Вверх
GremlinProg
Дата 5.4.2013, 10:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(xvr @  5.4.2013,  12:33 Найти цитируемый пост)
Если только GWL_USERDATA не занят уже под что то другое.

Для таких случаев существует как минимум 2 пути:
1. использовать свойства окна
2. расширить набор WindowLong (WNDCLASS::cbWndExtra) при регистрации класса (обычный подход)
Цитата(xvr @  5.4.2013,  12:33 Найти цитируемый пост)
В VCL Borland не мог воспользоваться GWL_USERDATA для передачи this, т.к. GWL_USERDATA находится в распоряжении пользователя библиотеки VCL, а не её самой.

Для расширения класса (например, для второго варианта) у Borland'а, насколько помню, всегда имелся метод CreateParams, который можно было переписать на свой вкус.


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
borisbn
Дата 5.4.2013, 13:05 (ссылка) |  (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 6
Всего: 135



Цитата(xvr @  5.4.2013,  10:33 Найти цитируемый пост)
В VCL Borland не мог воспользоваться GWL_USERDATA для передачи this, т.к. GWL_USERDATA находится в распоряжении пользователя библиотеки VCL, а не её самой

дык для этого ж они свойство Tag предоставляют


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.0991 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.