Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

правильная реализация MethodToProcedure [D6, D7, Win2k, WinXP]


MetalFan ©   (2006-10-26 13:28:00) 

решил вынести в отдельную тему.
проблема: приведение метода класса к процедурному типу (простите, если термины неверны) для передачи в кач. callback функции WinApi или в классы. (типа TList.Sort()).

найдена следующая реализация в madCollection ( http://www.madshi.net/)
описание из help'a:
Sometimes you need to give a procedure address in to a Windows API, but you would rather give in a method instead. In such a situation the function "MethodToProcedure" can come in handy. You give in a method address and your current "Self" value and you get a procedure address as the result, which you then can give in to a Windows API.

Please note that this works only for *stdcall* methods! Don't use it for any other calling convention! (But since almost all Windows APIs are stdcall, this should not be a big problem.)

After you don't need the procedure pointer anymore, please free it with "VirtualFree".

function MethodToProcedure (self       : TObject;
                           methodAddr : pointer) : pointer; overload;

function MethodToProcedure (method     : TMethod) : pointer; overload;


Here comes a little example which demonstrates a correct and useful usage of "MethodToProcedure":
unit EnumWindows;

// this unit implements a little class named TEnumWindows, which does nothing
// but enumerate all windows

interface

implementation

uses Windows, SysUtils, madTools;

type
 TEnumWindows = class
 private
   function EnumCallbackMethod(window: dword; lParam: integer) : bool; stdcall;
 public
   constructor Create;
 end;

constructor TEnumWindows.Create;
var EnumCallbackProc : pointer;
begin
 inherited;

 // you must give in a procedure address into EnumWindows
 // if you would give in "@TWindowList.EnumCallbackMethod" your code would crash
 // because methods have an additional hidden "Self" parameter
 // so we need to convert our method to a procedure first
 EnumCallbackProc := MethodToProcedure(Self, @TEnumWindows.EnumCallbackMethod);

 // "EnumCallbackProc" now contains a pointer to a newly allocated procedure
 // which does nothing but jump to our "EnumCallbackMethod" with the correct parameters
 EnumWindows(EnumCallbackProc, 0);

 // don't forget to free the allocated procedure
 VirtualFree(EnumCallbackProc, 0, MEM_RELEASE);
end;

function TEnumWindows.EnumCallbackMethod(window: dword; lParam: integer) : bool; stdcall;
begin
 result := true;

 // do anything with the "window"s you get here...
 [...]
end;

end.


function MethodToProcedure(self: TObject; methodAddr: pointer) : pointer;
type
 TMethodToProc = packed record
   popEax   : byte;                  // $58      pop EAX
   pushSelf : record                 //          push self
                opcode  : byte;      // $B8
                self    : pointer;   // self
              end;
   pushEax  : byte;                  // $50      push EAX
   jump     : record                 //          jmp [target]
                opcode  : byte;      // $FF
                modRm   : byte;      // $25
                pTarget : ^pointer;  // @target
                target  : pointer;   //          @MethodAddr
              end;
 end;
var mtp : ^TMethodToProc absolute result;
begin
 mtp := VirtualAlloc(nil, sizeOf(mtp^), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 with mtp^ do begin
   popEax          := $58;
   pushSelf.opcode := $68;
   pushSelf.self   := self;
   pushEax         := $50;
   jump.opcode     := $FF;
   jump.modRm      := $25;
   jump.pTarget    := @jump.target;
   jump.target     := methodAddr;
 end;
end;


Мастера, объясните ошибочность такого подхода, а так же, если не сложно, покажите пример кода, выполняющий эту задачу


MetalFan   (2006-11-13 08:43:00) [1]


> Ketmar ©   (26.10.06 16:01) [14]
>
> >[13] MetalFan(c) 26-Oct-2006, 15:59
> >а зачем, если можно поизвращаться...
> ну как тебе сказать, чтобы без мата? %-)
>

ну уж как нибудь нельзя чтоль?!
только за метлой не посылай ;)


Ketmar   (2006-10-26 16:01:00) [2]

>[13] MetalFan(c) 26-Oct-2006, 15:59
>а зачем, если можно поизвращаться...
ну как тебе сказать, чтобы без мата? %-)


MetalFan   (2006-10-26 15:59:00) [3]

>Ketmar ©   (26.10.06 15:54) [12]
т.е. скопировать тот же код, немного его изменив?
а зачем, если можно поизвращаться...
вон, Rule говорит, что заработало у него %)


Ketmar   (2006-10-26 15:54:00) [4]

>[11] MetalFan(c) 26-Oct-2006, 15:47
у "наследников TList" никто не мешает сделать дополнительный метод.


MetalFan   (2006-10-26 15:47:00) [5]

Ketmar ©   (26.10.06 15:43) [10]
а почему нет?
чтобы например у наследников TList вызывать Sort  с методом тогоже класса...


Ketmar   (2006-10-26 15:43:00) [6]

>[9] MetalFan(c) 26-Oct-2006, 15:42
>хорошо, а как его(код) модифицировать, чтобы можно было
>использовать и для WinApi и для callback в классах делфи?
ЗАЧЕМ???


MetalFan   (2006-10-26 15:42:00) [7]

хорошо, а как его(код) модифицировать, чтобы можно было использовать и для WinApi и для callback в классах делфи?


Rule   (2006-10-26 15:16:00) [8]

MetalFan ©   (26.10.06 14:55) [6]
зачем ????


Ketmar   (2006-10-26 15:02:00) [9]

>[6] MetalFan(c) 26-Oct-2006, 14:55
>а можно его не для WinApi использовать?
не надо.


MetalFan   (2006-10-26 14:55:00) [10]

а можно его не для WinApi использовать?


Ketmar   (2006-10-26 14:49:00) [11]

>[4] MetalFan(c) 26-Oct-2006, 14:37
>а можете помочь приведенный код до ума довести?
какой? который от madshi? так он отлично работает.


MetalFan   (2006-10-26 14:37:00) [12]

Юрий Зотов ©   (26.10.06 13:42) [1]
можно и так) но всетаки хочется поизвращенней)

Сергей М. ©   (26.10.06 13:55) [2]
сравнил... похоже...
а можете помочь приведенный код до ума довести? мне опыта не хватает(


Ketmar   (2006-10-26 14:11:00) [13]

>[1] Юрий Зотов(c) 26-Oct-2006, 13:42
>Его главная ошибочность в том, что он на фиг не нужен.
madshi не зря назвал это "madCollection". %-)


Сергей М.   (2006-10-26 13:55:00) [14]


> MetalFan ©   (26.10.06 13:28)


Сравни с Make/FreeObjectInstance() (classes.pas), удивись ..)


Юрий Зотов   (2006-10-26 13:42:00) [15]

> объясните ошибочность такого подхода

Его главная ошибочность в том, что он на фиг не нужен. Можно, конечно, чесать правое ухо и левой ногой, но гораздо проще и гораздо удобнее делать это правой рукой.

> покажите пример кода

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


Наверх

Титульная страница Поиск, карта сайта Написать письмо