Имя: Пароль:
IT
 
Иногда они возвращаются.... Хранение методов в памяти.
0 H A D G E H O G s
 
01.08.13
01:39
Ночи доброй.
Это было давно и успело зарастись травом, я просто сделал и забыл но все таки.
Ветка:
Delphi. Методы объектов как то по другому хранятся в памяти?

Использование метода объекта, как call-back процедуру
пост (17)

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;


Все понятно, кроме 3-х вещей:
1) Че это такое:
jump.pTarget    := @jump.target;
Указатель на сам себя, я правильно понял? Зачем?
2) Как программа определяет, что пошел код, а не данные?
2) Кто освободит память за собой?
1 NS
 
01.08.13
01:40
программе не надо определять идет код или данные.
2 H A D G E H O G s
 
01.08.13
01:43
(1) Нууу.
Тогда вызвался этот метод, выделилась память под структуру, заполниласб структура и все..
Управление не перешло по адресу jump.target    := methodAddr;

У тут - переходит.
3 ЧеловекДуши
 
01.08.13
06:23
(0) jump.pTarget    := @jump.target;

Запоминает адрес в памяти Родителя :)
4 ЧеловекДуши
 
01.08.13
06:25
+(0) Кто освободит память за собой?

Придется еще чего записать, память никто не освобождает при таком методе :)

Тебе ещн нужно будет организовать...
Функция VirtualFree освобождает выделенную память.
http://www.firststeps.ru/mfc/winapi/r.php?113
5 skunk
 
01.08.13
06:38
(4)это не пхп ... в дельфях вроде как деструктор объекта и освобождает память
6 skunk
 
01.08.13
06:39
хотя там тоже фримем использовать надо
7 Кирпич
 
01.08.13
09:23
понимай эту фигню как программу на асме

   popEax          := $58;
   pushSelf.opcode := $68;
   pushSelf.self  := self;
   pushEax        := $50;
   jump.opcode    := $FF;
   jump.modRm      := $25;
   jump.pTarget    := @jump.target;
   jump.target    := methodAddr;

т.е
pop eax
push self
push eax
jmp methodAddr

функция MethodToProcedure создает этот код и возвращает указатель на него.
этот код помещает self в стек, т.к. метод объекта ожидает первым параметром self. затем управление передается в метод объекта.
удалять эту структуру из памяти ты должен сам (в деструкторе объекта можно это сделать)

я так понимаю.
8 Лефмихалыч
 
модератор
01.08.13
09:26
(0) а проблема-то исходно в чем? В том, что ты в callback суёшь указатель на метод класса и этот метод не выполняется?
9 Кирпич
 
01.08.13
09:26
jmp methodAddr это jmp [jump.target]
т.е. переход по указателю на methodAddr
в jump.target это указатель на methodAddr
10 Кирпич
 
01.08.13
09:32
автор похоже спит. автор спит, а проблемы решаются без него. вот что такое "утро вечера мудреннее" )
11 Кирпич
 
01.08.13
09:34
(8) ну да. он хочет чтобы windows вызывала метод его объекта как простую функцию.
12 Лефмихалыч
 
модератор
01.08.13
09:37
была у меня когда-то такая проблема:
http://www.rsdn.ru/forum/delphi/1409601.flat

так и не понял, как это сотонинство победил и сделал в классе static-функцию в и всё зашиблось. Но у меня было смягчающее обстоятельство - аналоговый модем, в котрый два экземпляра одновременно позвонить гарантированно не могут
13 Лефмихалыч
 
модератор
01.08.13
09:38
так и не понял, как это сотонинство победиТЬ. Там в последнем посте какой-то добрый человек кастанул что-то на дельфях, но я не осилил понять и перевести :)
14 sda553
 
01.08.13
09:39
Освободить память, насколько я помню VirtualFree это противоположность упомянутого в коде VirtualAlloc
15 Лефмихалыч
 
модератор
01.08.13
09:42
там, если я правильно все забыл, проблема в том, что метод класса действительно как-то под другому хранится. Более того - у него всегда первым скрытым параметром - указатель на объект, без которого он не может выполниться. И вот эта бяка jump.pTarget    := @jump.target; нужна, чтобы все окружающее fcмоподобное сотонинство смогло как-то найтить объект от метода и там тудым-сюдым дальше я не понял
16 Лефмихалыч
 
модератор
01.08.13
09:42
fcмоподобное = ASM-оподобное
17 Кирпич
 
01.08.13
09:48
(15) что такое jump.pTarget    := @jump.target я написал в (9)
но автору нужно смотреть на функцию MakeObjectInstance. она ему нужна. а то, что в (0) скорее не подойдет. есть еще заморочки со способами вызова (cdecl, pascal, stdcall).
18 Кирпич
 
01.08.13
09:50
в (12) правильная ссылка
19 Кирпич
 
01.08.13
09:55
хотя MakeObjectInstance тоже не катит :))
20 Кирпич
 
01.08.13
10:22
Разобрался. Короче MethodToProcedure работает правильно, если способ вызова stdcall
А MakeObjectInstance это совсем другая фигня.
21 Кирпич
 
01.08.13
10:24
вот так оно работает

procedure TForm1.Hello(param1, param2: integer);
begin
   ShowMessage('p1:' + IntToStr(param1) + ' p2: ' + IntToStr(param2));
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  P : procedure (param1 : integer; param2 : integer); stdcall;
begin
  @P := MethodToProcedure(Self,@TForm1.Hello);
  P(100,500);
end;
22 H A D G E H O G s
 
01.08.13
14:44
static метод как раз не подходит, мне нужен self в callback.

Ну все работает, просто захотелось понять.
23 Кирпич
 
01.08.13
14:51
при чем тут static. в static тоже есть self

procedure TForm1.Hello(param1, param2: integer); stdcall;
begin
  ShowMessage(Self.Name + ' p1:' + IntToStr(param1) + ' p2:' + IntToStr(param2));
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  P: procedure(param1: integer; param2: integer); stdcall;
begin
  @P := MethodToProcedure(Self, @TForm1.Hello);
  P(100, 500);
end;
24 H A D G E H O G s
 
01.08.13
14:56
http://www.gunsmoker.ru/2008/12/static-delphi.html
Теперь ещё один шажок и мы переходим к тому, о чём говорил Реймонд Чен. Классовый метод можно объявить статическим (только в новых версиях Delphi). В этом случае у него не будет неявного параметра. Разумеется, при этом он не может использовать информацию экземпляра и класса. Зато он и не отличается от обычной функции.


"Разумеется, при этом он не может использовать информацию экземпляра и класса."
25 Кирпич
 
01.08.13
14:57
или ты хочешь свой callback в виртуальный метод засунуть?
26 H A D G E H O G s
 
01.08.13
14:58
Вообще спасибо большое этому головняку, у меня прям прыжок в понимании объектов.
27 Кирпич
 
01.08.13
14:59
тебе нужен self в классовом методе чтоли? нафига это надо, если можно использовать обычный метод.
28 Кирпич
 
01.08.13
15:00
(26) ну это все в книжках есть. просто никто не читает особо.
29 H A D G E H O G s
 
01.08.13
15:03
(27) У. Меня. Все. Работает.
Мне нужно было вооот это:

ThreadProcAddress:=Self.MethodAddress('ThreadProc');
ThreadProcAddress:=MethodToProcedure(self,ThreadProcAddress);
CreateThread(nil,0,ThreadProcAddress,nil,0,PID);

Procedure T_vk_object.ThreadProc();stdcall;
begin
Self.iEvent.ExternalEvent('MyComponent','Создан поток','');
end;
30 H A D G E H O G s
 
01.08.13
15:03
Self.iEvent.ExternalEvent('MyComponent','Создан поток','');

Ухожу от извращений с глобальными переменными.
31 H A D G E H O G s
 
01.08.13
15:04
(28) Некогда читать. Надо писать код!
32 Кирпич
 
01.08.13
15:07
а чо спрашивать если работает :)
33 H A D G E H O G s
 
01.08.13
15:08
(32) Понять хотел, как работает.
34 Кирпич
 
01.08.13
15:13
если у тебя метод классовый, то наверное надо так

Procedure T_vk_object.ThreadProc(myself:TObject);stdcall;
begin
MySelf.iEvent.ExternalEvent('MyComponent','Создан поток','');
end;
35 Кирпич
 
01.08.13
15:14
иначе в стеке так и останется твой self
36 Кирпич
 
01.08.13
15:14
это для дальнейшего понимания :)
37 H A D G E H O G s
 
01.08.13
15:16
Что ты делаешь, все же работает...

Дома отладчиком гляну, что придет в параметр myself
38 Кирпич
 
01.08.13
16:07
(37) если ты будешь использовать MethodToProcedure и твой callback будет
class Procedure T_vk_object.ThreadProc();stdcall; static;

то после N вызовов твоей T_vk_object.ThreadProc стек переполнится и твое творение рухнет.

поэтому нужно делать так
class Procedure T_vk_object.ThreadProc(myself:TObject);stdcall; static;
или просто не использовать метод класса. он нафиг не нужен. Т.е. писать так
Procedure T_vk_object.ThreadProc();stdcall;
39 Принт
 
01.08.13
21:40
Высокий ООП с костылями на асме.. Оно такое точно нужно?
40 Кирпич
 
02.08.13
22:14
(39) ну иногда нужно. только не в этом случае конечно.
тут можно обойтись классом TThread и ничего не выдумывать. Можно писать на api и в функцию потока передавать свой объект. в функции потока для этого есть специальный параметр. у автора этого параметра почему-то нету. затупил наверное)
но это не важно. важно чтобы автор не задавал глупые вопросы типа "2) Как программа определяет, что пошел код, а не данные? "
Пользователь не знает, чего он хочет, пока не увидит то, что он получил. Эдвард Йодан