Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2010.08.27;
Скачать: [xml.tar.bz2];

Вниз

Передача указателя на метод как на non-object функцию.   Найти похожие ветки 

 
Дмитрий С ©   (2010-04-07 14:40) [0]

Передать указатель на метод в качестве аргумента функции, например, EnumWindows не выйдет, по понятным причинам: хотя бы потому что метод 8 байт, а указатель 4 :).

Я написал враппер для случаев, когда нужно передать метод экземпляра класса и не хочется городить функции, использовать lparam и еще 100 причин.

У меня была необходимость передавать указатель на cdecl-функцию, а не stdcall.

Выкладываю код на общий суд:

Форма Form2, на ней кнопка Button1.


unit Unit2;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls;

type
 TSomeFunctionType = function (A, B: Cardinal): Cardinal; cdecl;

 TForm2 = class(TForm)
   Button1: TButton;
   procedure FormCreate(Sender: TObject);
   procedure Button1Click(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
 private
   FMul: Integer; { Тестовое значение этого объекта
                     для использования внутри SomeCdeclMethod }

   FSomeCdeclMethodWrapper: Pointer; { Указатель на Wrapper для
                                       метода SomeCdeclMethod,
                                       его нужно передавать в качестве параметра
                                       в Api функции
                                     }
   function SomeCdeclMethod(A, B: Cardinal): Cardinal; cdecl; { Тестовый метод }
 private
   { Функции для создания и освобождения Wrapper-ов }
   function GetWrapper(Method: Pointer): Pointer;
   procedure FreeWrapper(WrapperPtr: Pointer);
 public
   { Как бы Api функция }
   procedure Test(NonObjectProc: TSomeFunctionType);
 end;

var
 Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
 {
   Используем враппер
 }
 Test( FSomeCdeclMethodWrapper );
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
 FMul := 2;

 {
   Инициализируем враппер
 }
 FSomeCdeclMethodWrapper := GetWrapper(@TForm2.SomeCdeclMethod);
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
 FreeWrapper(FSomeCdeclMethodWrapper);
end;

procedure TForm2.FreeWrapper(WrapperPtr: Pointer);
begin
 VirtualFree(WrapperPtr, 0, MEM_RELEASE);
end;

function TForm2.GetWrapper(Method: Pointer): Pointer;
const
 Prg: array[0..37] of byte = (
   $58,                  {pop eax}
   $68, {2} 0,0,0,0,     {push <Self>}
   $50,                  {push eax}
   $B8, {8}0,0,0,0,      {mov eax, <Mtd>}
   $5A,                  {pop edx}
   $BB, {14}0,0,0,0,     {mov ebx, <PointToDummy>}
   $89, $13,             {mov [ebx], edx}
   $FF, $D0,             {call eax}
   $83,$C4,$04,          {add esp, 4}
   $BB, {26}0,0,0,0,     {mov ebx, <PointToDummy>}
   $8B, $13,             {mov edx, [ebx]}
   $52,                  {push edx}
   $C3,                  {ret}
   {34}0,0,0,0);         {Dummy}
var
 B: PByteArray;
 PtrToEnd: Pointer;
begin
 Result := VirtualAlloc(nil, SizeOf(Prg), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 Win32Check(Result <> nil);
 CopyMemory(Result, @Prg, SizeOf(Prg));
 B := Result;
 CopyMemory(@B^[2], @Self, 4);
 CopyMemory(@B^[8], @Method, 4);
 PtrToEnd := @B^[34];
 CopyMemory(@B^[14], @PtrToEnd, 4);
 CopyMemory(@B^[26], @PtrToEnd, 4);
end;

function TForm2.SomeCdeclMethod(A, B: Cardinal): Cardinal;
begin
 Result := (A + B) * FMul;
end;

procedure TForm2.Test(NonObjectProc: TSomeFunctionType);
begin
 ShowMessage(
   IntToStr(
     NonObjectProc(1,2)
   )
 );
end;

end.



Интересует мнения, ваши замечания, более простые решения.

P.S. Еще конкретный, для возврата используется только регистр EAX?


 
oxffff ©   (2010-04-07 14:54) [1]

function Classes.MakeObjectInstance(AMethod: TWndMethod): Pointer;


 
oxffff ©   (2010-04-07 15:05) [2]

The following conventions are used for returning function result values.

Ordinal results are returned, when possible, in a CPU register. Bytes are returned in AL, words are returned in AX, and double-words are returned in EAX.
Real results are returned in the floating-point coprocessor"s top-of-stack register (ST(0)). For function results of type Currency, the value in ST(0) is scaled by 10000. For example, the Currency value 1.234 is returned in ST(0) as 12340.
For a string, dynamic array, method pointer, or variant result, the effects are the same as if the function result were declared as an additional var parameter following the declared parameters. In other words, the caller passes an additional 32-bit pointer that points to a variable in which to return the function result.

Int64 is returned in EDX:EAX.
Pointer, class, class-reference, and procedure-pointer results are returned in EAX.
For static-array, record, and set results, if the value occupies one byte it is returned in AL; if the value occupies two bytes it is returned in AX; and if the value occupies four bytes it is returned in EAX. Otherwise, the result is returned in an additional var parameter that is passed to the function after the declared parameters.


 
oxffff ©   (2010-04-07 15:08) [3]

+ возможно тебе будет интересен модуль ObjAuto для исследований.


 
Дмитрий С ©   (2010-04-07 15:18) [4]


> function Classes.MakeObjectInstance(AMethod: TWndMethod):
>  Pointer;

По-моему это не совсем то, что нужно, хотя я точно так и не понял.


> oxffff ©   (07.04.10 15:05) [2]

А это полезно. Мой враппер испортит возвращаемое Int64 значение функции, если таковое будет. :)


> ObjAuto для исследований.


почитаю


 
Leonid Troyanovsky ©   (2010-04-07 16:23) [5]


> Дмитрий С ©   (07.04.10 15:18) [4]

> > function Classes.MakeObjectInstance(AMethod: TWndMethod):

> По-моему это не совсем то, что нужно, хотя я точно так и
> не понял.

Это - то. Оно стоит изучения.

Но, в подобных  вещах не следует, IMHO:
1. Разрабатывать универсальные методы на все случаи жизни.
2. Полагаться на то, что действующая политика всегда останется
к оным вещам равнодушной.

Сегодня запретили выполнять код из стека, а завтра не разрешат
менять в екзешнике атрибуты страниц.
И выживут самые морозоустойчивые :)

--
Regards, LVT.


 
Игорь Шевченко ©   (2010-04-07 17:43) [6]

Читаем разницу между TWndMethod и "любая callback-функция"


 
oxffff ©   (2010-04-07 19:52) [7]


> Игорь Шевченко ©   (07.04.10 17:43) [6]
> Читаем разницу между TWndMethod и "любая callback-функция"


Где написано, что любая?

[2] - это пример реализации заголовка темы.


 
Игорь Шевченко ©   (2010-04-07 20:52) [8]

oxffff ©   (07.04.10 19:52) [7]

[2] это что где возвращается.


> Где написано, что любая?


в [0]


 
oxffff ©   (2010-04-08 08:31) [9]


> Игорь Шевченко ©   (07.04.10 20:52) [8]
> oxffff ©   (07.04.10 19:52) [7]
>
> [2] это что где возвращается.


Да, моя оплошность. Но вы поняли.


 
oxffff ©   (2010-04-08 08:39) [10]


> Игорь Шевченко ©   (07.04.10 20:52) [8]
> oxffff ©   (07.04.10 19:52) [7]
> > Где написано, что любая?
>
>
> в [0]


И?
В чем вы видите сложность обобщенного кода при реализации произвольной трансляции, учитывая [2],[3] и RTTI?


 
Юрий Зотов ©   (2010-04-08 09:28) [11]

> Интересует мнения, ваши замечания, более простые решения.

Непонятно, зачем нужно городить огород. Можно же передать адрес обычной callback-функции, а уже в ней никто не мешает вызвать любой метод любого объекта. Это и есть то самое "более простое решение" (и вполне безопасное, к тому же).


 
oxffff ©   (2010-04-08 09:34) [12]


> Юрий Зотов ©   (08.04.10 09:28) [11]
> > Интересует мнения, ваши замечания, более простые решения.
>
>
> Непонятно, зачем нужно городить огород. Можно же передать
> адрес обычной callback-функции, а уже в ней никто не мешает
> вызвать любой метод любого объекта. Это и есть то самое
> "более простое решение" (и вполне безопасное, к тому же).
>


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


 
Дмитрий С ©   (2010-04-08 10:38) [13]


> а завтра не разрешат
> менять в екзешнике атрибуты страниц.

Будем DLL генерить :) Прикольно получится - для каждого экземпляра класса своя dll :)


 
Дмитрий С ©   (2010-04-19 17:33) [14]

Код в шапке ошибочный, как выяснилось, вызывает потерю ebx, что недопустимо для вызова cdecl. Константу Prg нужно заменить на

 Prg: array[0..37] of byte = (
   $58,                  {pop eax}      
   $68, {2} 0,0,0,0,     {push <Self>}
   $50,                  {push eax}    
   $B8, {8}0,0,0,0,      {mov eax, <Mtd>}
   $5A,                  {pop edx}       //
   $B9, {14}0,0,0,0,     {mov ecx, <PointToDummy>}
   $89, $11,             {mov [ecx], edx}
   $FF, $D0,             {call eax}
   $83,$C4,$04,          {add esp, 4}
   $B9, {26}0,0,0,0,     {mov ecx, <PointToDummy>}
   $8B, $11,             {mov edx, [ecx]}
   $52,                  {push edx}
   $C3,                  {ret}
   {34}0,0,0,0);         {Dummy}


P.S. Это для тех, кто тему по поиску вдруг найдет


 
RC   (2010-04-20 17:41) [15]

To Дмитрий С ©

http://delphikingdom.com/asp/answer.asp?IDAnswer=58782



Страницы: 1 вся ветка

Форум: "Прочее";
Текущий архив: 2010.08.27;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.5 MB
Время: 0.084 c
15-1270102589
AlexDan
2010-04-01 10:16
2010.08.27
Думаю поспамить


2-1266484344
Kiril123
2010-02-18 12:12
2010.08.27
Последовательность событий жизненного цикла форм


2-1275655873
SIV5000
2010-06-04 16:51
2010.08.27
Плавная промотка


2-1271922190
Knob
2010-04-22 11:43
2010.08.27
Открыть с помощью...


2-1268243885
Haidukzz
2010-03-10 20:58
2010.08.27
Form1.Label_nr_$i.Caption := Hello World ; ?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский