MakeObjectInstance

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

MakeObjectInstance

Сообщение mike » 26.02.2007 13:11:17

Говорила мне мама: "не используй недокументированные функции", а я не слушал :) и имею теперь большую-прибольшую проблему при переносе некоторых своих тяжелых классов под FPC/WIN32 в лице сабжевой функции (точнее AllocateHwnd, ее использующей).

Если кто не в курсе, то AllocateHwnd кроме создания невидимого окна (что само по себе пустяк) позволяет использовать метод класса в качестве оконной функции. При этом она обращается к сабжу, а тот в свою очередь выворачивается через хитро закрученную задницу чтобы "виртуальная" оконная stdcall-функция культурно вызывала указанный метод. При этом вовсю используются некоторые особености внутренней реализации классов Delphi и (что очень плохо) архитектуры x86.

Проблема конечно решаема, но может не нужно велосипед изобретать и у кого-нибудь уже есть готовые наработки?

P.S. Вообще отказаться от AllocateHwnd не могу - уж очень сильно завязан я на нее, ничего не поделать....
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00

Сообщение Sergei I. Gorelkin » 26.02.2007 15:47:16

В JEDI VCL есть ф-ция AllocateHwndEx(), которая делает то же, что и AllocateHwnd, но обходится без ассемблера.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение mike » 26.02.2007 16:05:44

Спасибо, гляну. Только что попробовал "в лоб" вызывать метод класса по ссылке из WindowProc и, что очень странно, он нормально вызвался. Причем из него я нормально обратился к Self, все отработало, ошибок не было.... Это мне повезло, или так и должно быть? Как FPC передает ссылку на Self при вызове метода?
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00

Сообщение Sergei I. Gorelkin » 26.02.2007 19:44:49

Self передается неявным первым параметром, а как - зависит от объявления метода. По умолчанию - в eax, если метод объявлен как stdcall - то в стеке.
Тут основная проблема - сопоставить hwnd, который передается в WndProc, и экземпляр класса, метод которого надо вызвать. Пока экземпляр один, проблем не будет...
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение mike » 26.02.2007 20:01:20

Хорошо, если у меня есть два экземпляра некоего класса и два экземпляра структуры:
Код: Выделить всё
type
  TItem = record
    hWnd: THandle;
    Obj: TObject;
    Method: TWndMethod;
  end;

как мне культурно вызвать имеющийся в этой структуре Obj.Method()?
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00

Сообщение Sergei I. Gorelkin » 26.02.2007 22:34:02

Так поле Method (если TWndMethod объявлен как procedure(var Message: TMessage) of object), уже включает в себя экземпляр класса. То есть это запись из двух указателей - один суть адрес нужной процедуры, другой - тот Self, который в нее передается. И его можно вызывать "в лоб": Method(TheMessage).
Поле Obj получается как бы лишним.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение shade » 26.02.2007 22:44:17

Код: Выделить всё
type
  TMethod = procedure of object;
  TItem = record
    hWnd: THandle;
{$IF нужна ссылка на Obj и хочеться компактности}
    case Integer of
    0: (Proc: Pointer; Obj: TObject);
    1: (
{$IFEND}
    Method: TMethod
{$IF нужна ссылка на Obj и хочеться компактности}
)
{$IFEND}
;
  end;

procedure CallMethod(const Item: TItem);
begin
  Item.Method();
end;
Аватара пользователя
shade
энтузиаст
 
Сообщения: 879
Зарегистрирован: 21.02.2006 20:15:48
Откуда: http://shamangrad.net/

Сообщение mike » 27.02.2007 12:19:14

Sergei I. Gorelkin писал(а):Так поле Method (если TWndMethod объявлен как procedure(var Message: TMessage) of object), уже включает в себя экземпляр класса.

Вот так у меня и работало парой сообщений выше, без всяких Obj. Однако в Delphi такой код не проканывает, поэтому и спрашиваю.

То есть это запись из двух указателей - один суть адрес нужной процедуры, другой - тот Self, который в нее передается.

А вот Delphi так не считает и Self передает непосредственно при вызове через EBX, в результате вызвать метод можно, но как только из него явно или неявно к Self обратился - получай AV. Тут важно знать чем FP от Delphi отличается.

И его можно вызывать "в лоб": Method(TheMessage).
Поле Obj получается как бы лишним.

Вот без него и работает, просто стресс-тест пока не устраивал, но по одному-два-три вызова проходит просто отлично.

shade, спасибо, пока буду так писать, а там посмотрим. Еще бы это чертов SIGSEGV жизнь не портил, - вообще хорошо было бы :(
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00

Сообщение ZerstoreN » 09.03.2007 20:07:04

Код: Выделить всё

    Stub   = Packed
              Record
//              instr_int3   : Byte; // debugg
              instr_POPEAX : Byte;
              instr_PUSHIMM: Byte;   OBJSELF: Pointer;
              instr_PUSHEAX: Byte;
              instr_JUMP   : SmallWord;   OBJFUNC: Pointer;
              ptr_to_address: Pointer;
              End;

Procedure   SetStub(var stub:stub; self, address : Pointer);
            Begin
            With stub do
                 Begin
//                 instr_int3   :=$CC;
                 instr_POPEAX :=$58;
                 instr_PUSHIMM:=$68; OBJSELF:= self;
                 instr_PUSHEAX:=$50;
                 instr_JUMP   :=$25FF; OBJFUNC:= @stub.ptr_to_address;
                 ptr_to_address:=address;
                 End;
            End;

type       RootClassForControls = object
                  winstub : stub;
                  function wf (.... как оконная функция
                  end;

function    RootClassForControls.CommonInit;
var         s   : string;
            wcl : TWndClass;
            dc  : longint;
            Begin

            SetStub( winstub, @self, wf);

            s := 'ctl' + int2hex(longint(@self) xor $55555555,8) + #0;
            With WCl do
                Begin
                lpfnWndProc := @winstub;
.......
                lpszClassName := @s[1];
                End;
            RegisterClass(wcl);

            hwnd := CreateWindowEx(ws_ex,@s[1],caption,ws,x,y,xx,yy,parent, 0, system. hInstance,nil);

.......
            End;




В свое время извращялся так в VP, чтоб не использовать additional bytes в окне и вытаскивать оттуда поинтер в общей оконной функции а затем звать метод уже ( ну не нравилось мне это , что поделаешь )

сетстуб заполняет структуру инструкциями типа поп еип, пуш self, пуш еип обратно и jmp на метод. т.е., добавляет параметр селф к вызову оконной функции.
ZerstoreN
новенький
 
Сообщения: 53
Зарегистрирован: 30.06.2006 12:05:01

Сообщение mike » 13.03.2007 14:10:22

сетстуб заполняет структуру инструкциями типа поп еип, пуш self, пуш еип обратно и jmp на метод. т.е., добавляет параметр селф к вызову оконной функции

Мне такой способ не подходит. Мне нужно чтобы это работало под ARM, так что ассамблер не воодушевляет совсем :)

P.S. Вопрос снят, благодаря отличиям FPC от Delphi все работет просто замечательно. С быстродействием не заморачивался пока, но полный и кроссплатформенный (Win32/Win64/WinCE) аналог борландовского AllocateHwnd есть, кому надо - обращайтесь :)
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4

Рейтинг@Mail.ru