gpt4 book ai didi

delphi - 使用 Delphi 内联汇编器创建类实例

转载 作者:行者123 更新时间:2023-12-03 15:05:48 28 4
gpt4 key购买 nike

我想做的是,使用程序集创建一个类实例,调用它的方法之一,然后释放该实例。

我知道我错过了一些非常重要且可能非常简单的东西,但我不知道是什么。

program Project2;

{$APPTYPE CONSOLE}

uses
SysUtils;

type
TSomeClass = class(TObject)
private
FCreateDateTime: string;
public
constructor Create;
procedure SayYeah;
end;

constructor TSomeClass.Create;
begin
FCreateDateTime := DateTimeToStr(Now);
end;

procedure TSomeClass.SayYeah;
begin
Writeln('yeah @ ' + FCreateDateTime);
end;

procedure Doit;
asm
CALL TSomeClass.Create; // <= Access Violation
CALL TSomeClass.SayYeah;
CALL TSomeClass.Free;
end;

begin
try
Doit;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.

仅供引用:我想了解如何在低级别实现这一目标,而不是其他方法。

更新:

感谢 Andreas Rejbrand,我成功找到了罪魁祸首:

更新2:

感谢 Arnaud 使用 EBX 而不是 PUSH/POP EAX 来发现缺陷

var
TSomeClass_TypeInfo: Pointer;

procedure Doit;
asm
MOV DL, $01;
MOV EAX, TSomeClass_TypeInfo;
CALL TSomeClass.Create;
PUSH EAX;
CALL TSomeClass.SayYeah; // call method
POP EAX;
MOV DL, $01;
CALL TSomeClass.Free; // pointer to instance(Self) is expected in EAX
end;

begin
TSomeClass_TypeInfo := TSomeClass;
try
Doit;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.

最佳答案

您的 asm 代码不正确。

您正在重载 ebx 寄存器,必须保留该寄存器。并且全局变量技巧没有意义。

更好的编码应该是:

procedure Doit(ClassType: pointer);
asm // eax=TList
mov dl,true // hidden boolean 2nd parameter
call TObject.Create
push eax
call TList.Pack
pop eax
call TObject.Free
end;

DoIt(TList);

但它不会通过 try...finally 保护实例。 :)

关于mov dl,true参数,请参见this official page from the EMB wiki :

Constructors and destructors use the same calling conventions as other methods, except that an additional Boolean flag parameter is passed to indicate the context of the constructor or destructor call.

A value of False in the flag parameter of a constructor call indicates that the constructor was invoked through an instance object or using the inherited keyword. In this case, the constructor behaves like an ordinary method. A value of True in the flag parameter of a constructor call indicates that the constructor was invoked through a class reference. In this case, the constructor creates an instance of the class given by Self, and returns a reference to the newly created object in EAX.

A value of False in the flag parameter of a destructor call indicates that the destructor was invoked using the inherited keyword. In this case, the destructor behaves like an ordinary method. A value of True in the flag parameter of a destructor call indicates that the destructor was invoked through an instance object. In this case, the destructor deallocates the instance given by Self just before returning.

The flag parameter behaves as if it were declared before all other parameters. Under the register convention, it is passed in the DL register. Under the pascal convention, it is pushed before all other parameters. Under the cdecl, stdcall, and safecall conventions, it is pushed just before the Self parameter.

Since the DL register indicates whether the constructor or destructor is the outermost in the call stack, you must restore the value of DL before exiting so that BeforeDestruction or AfterConstruction can be called properly.

因此,由于 eax 我们的对象不是 nil,因此我们可以直接调用析构函数,因此替代的有效编码可能是:

procedure Doit(ClassType: pointer);
asm // eax=TList
mov dl,true
call TObject.Create
push eax
call TList.Pack
pop eax
mov dl,true
call TList.Destroy
end;

在所有情况下,从 asm 访问对象并不意味着以这种方式完成。您无法直接访问类型信息,因此使用它可能非常困难。使用现有的class实例,您可以使用asm方法做任何您想做的事情;但是要创建实例并使用类类型,asm 绝对不是自然的方式!

关于delphi - 使用 Delphi 内联汇编器创建类实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10620072/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com