Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Как преобразовать указатель на метод в указатель на функцию

Delphi , Синтаксис , Память и Указатели



Автор: http://www.swissdelphicenter.ch

// Converting method pointers into function pointers

// Often you need a function pointer for a callback function.
// But what, if you want to specify a method as
// an callback? Converting a method pointer to a function
// pointer is not a trivial task; both types are
// incomatible with each other. Although you have the
// possibility to convert like this "@TClass.SomeMethod",
// this is more a hack than a solution, because it restricts
// the use of this method to some kind of a class
// function, where you cannot access instance variables.
// If you fail to do so, you'll get a wonderful gpf.
// But there is a better solution: run time code generation!
// Just allocate an executeable memory block, and
// write 4 machine code instructions into it: 2 instructions
// loads the two pointers of the method pointer
// (code & data) into the registers, one calls the method
// via the code pointer, and the last is just a return
// Now you can use this pointer to the allocated memory
// as a plain funtion pointer, but in fact you are
// calling a method for a specific instance of a Class.

type
  TMyMethod = procedure of object;

function MakeProcInstance(M: TMethod): Pointer;
begin
  // allocate memory
  GetMem(Result, 15);
  asm
    // MOV ECX,
    MOV BYTE PTR [EAX], $B9
    MOV ECX, M.Data
    MOV DWORD PTR [EAX+$1], ECX
    // POP EDX
    MOV BYTE PTR [EAX+$5], $5A
    // PUSH ECX
    MOV BYTE PTR [EAX+$6], $51
    // PUSH EDX
    MOV BYTE PTR [EAX+$7], $52
    // MOV ECX,
    MOV BYTE PTR [EAX+$8], $B9
    MOV ECX, M.Code
    MOV DWORD PTR [EAX+$9], ECX
    // JMP ECX
    MOV BYTE PTR [EAX+$D], $FF
    MOV BYTE PTR [EAX+$E], $E1
  end;
end;

procedure FreeProcInstance(ProcInstance: Pointer);
begin
  // free memory
  FreeMem(ProcInstance, 15);
end;

// After all, you should not forget to release the allocated memory. 
// "TMyMethod" can be modified according your specific needs, e.g. 
add some parameters for a WindowProc. 
// N.B.: Yes, I know, Delphi has those "MakeProcInstance" 
function in its forms unit. 
// But this works a little bit different, has much more overhead, 
// and most important, you have to use the forms unit, which increases 
the size of your exe drastically, 
// if all other code doesn't use the VCL 
(e.g. in a fullscreen DirectX/OpenGl app). 

Интересный тематик! Перевод метода в функцию может быть немного сложным, но автор предложил решение с использованием runtime код генерации.

Функция MakeProcInstance принимает TMethod как входной параметр и возвращает указатель на функцию, которая может быть использована как callback. Функция выделяет память на стеке, пишет машинные инструкции в нее с помощью ключевого слова asm, а затем возвращает адрес выделенной памяти.

Вот шаг за шагом, что функция делает:

  1. Выделяет 15 байт памяти с помощью GetMem.
  2. Пишет ассемблерный код в выделенную память с помощью ключевого слова asm.
  3. Ассемблерный код загружает указатель на код (M.Code) и указатель на данные (M.Data) из метода в регистры (ECX и EDX, соответственно).
  4. Затем код вызывает метод через указатель на код, эффективно выполняя метод на конкретном экземпляре класса.
  5. Наконец, функция возвращает адрес выделенной памяти как указатель на функцию.

Процедура FreeProcInstance отвечает за освобождение выделенной памяти с помощью FreeMem.

Надо отметить, что это решение имеет некоторые ограничения:

  • Работает только с методами, которые не принимают параметров и возвращают void (или procedure of object;).
  • Метод должен быть экземплярным методом, а не классовым.
  • Решение использует ассемблерный код, который может быть платформозависимым.

Если вам нужно конвертировать указатель на метод в указатель на функцию в приложении Delphi, это решение может быть полезно. Однако, если вы работаете с современными версиями Delphi (например, Delphi 2009 или позднее), есть альтернативные решения, которые не требуют runtime код генерации.

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

Вот пример реализации этого решения:

type
  TMyMethod = procedure of object;

function MakeProcInstance(M: TMethod): Pointer;
var
  C: TConstructor;
begin
  C := TThread.CreateConstructor(TypeInfo(TObject), M);
  Result := @C;
end;

procedure FreeProcInstance(ProcInstance: Pointer);
begin
  FreeAndNil(ProcInstance as TConstructor);
end;

В этом примере функция MakeProcInstance создает анонимный класс с единственным методом (M) и возвращает указатель на этот метод. Процедура FreeProcInstance освобождает память, выделенную функцией MakeProcInstance.

Хотя это решение более безопасно по типу, чем оригинальное, оно все еще имеет ограничения (например, работает только с экземплярными методами). Если вам нужно больше гибкости или совместимости с старыми версиями Delphi, оригинальное решение может быть лучше.

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


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Память и Указатели ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-06-16 08:56:58/0.0061519145965576/0