Com*_*sNo 5 delphi inline-assembly basm
我想要做的是,使用assembly,创建一个类实例,调用其中一个方法,然后释放实例.
我知道我错过了一些非常重要的东西,可能很简单,但我不知道是什么.
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.
Run Code Online (Sandbox Code Playgroud)
仅供参考:我想了解如何在低水平实现这一目标,而不是另一种方式.
更新:
感谢Andreas Rejbrand,我找到了罪魁祸首:
Update2:
感谢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.
Run Code Online (Sandbox Code Playgroud)
Arn*_*hez 10
你的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);
Run Code Online (Sandbox Code Playgroud)
但它并不能保护实例try...finally
.:)
关于mov dl,true
参数,请参阅EMB维基的这个官方页面:
构造函数和析构函数使用与其他方法相同的调用约定,除了传递额外的Boolean标志参数以指示构造函数或析构函数调用的上下文.
构造函数调用的flag参数中的值False表示构造函数是通过实例对象或使用inherited关键字调用的.在这种情况下,构造函数的行为类似于普通方法.构造函数调用的flag参数中的值True表示构造函数是通过类引用调用的.在这种情况下,构造函数创建Self提供的类的实例,并返回对EAX中新创建的对象的引用.
析构函数调用的flag参数中的值False表示使用inherited关键字调用析构函数.在这种情况下,析构函数的行为类似于普通方法.析构函数调用的flag参数中的值True表示通过实例对象调用析构函数.在这种情况下,析构函数在返回之前释放Self给出的实例.
flag参数的行为就像在所有其他参数之前声明一样.根据寄存器约定,它在DL寄存器中传递.在pascal约定下,它在所有其他参数之前被推送.在cdecl,stdcall和safecall约定下,它在Self参数之前被推送.
由于DL寄存器指示构造函数或析构函数是否是调用堆栈中的最外层,因此必须在退出之前恢复DL的值,以便可以正确调用BeforeDestruction或AfterConstruction.
所以一个替代的有效编码,因为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;
Run Code Online (Sandbox Code Playgroud)
在所有情况下,对象访问asm
并不意味着以这种方式完成.您无法直接访问类型信息,因此使用它可能非常困难.使用现有class
实例,您可以使用asm
方法执行任何操作; 但是创建实例,并使用类类型,asm
绝对不是自然的方式!
您可以在 Delphi 汇编编程的优秀指南中阅读有关此内容的内容,最初可以在此处找到。不幸的是,该网站已关闭,但您可以在此处找到存档版本。请特别查看第 5 页。