我想要做的是,使用程序集,创建一个类实例,调用其中一个方法,然后释放实例。
我知道我错过了一些非常重要的东西,可能很简单,但我不知道是什么。
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,我找到了罪魁祸首:
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.
答案 0 :(得分: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);
但它不能用try...finally
来保护实例。 :)
关于mov dl,true
参数,请参阅this official page from the EMB wiki:
构造函数和析构函数使用与其他调用约定相同的调用约定 方法,除了传递一个额外的布尔标志参数 表示构造函数或析构函数调用的上下文。
值 构造函数调用的flag参数中的False表示该 构造函数是通过实例对象或使用 继承关键字。在这种情况下,构造函数的行为类似于 普通方法。 a的flag参数中的值为True 构造函数调用表明构造函数是通过一个调用的 课堂参考。在这种情况下,构造函数创建一个实例 Self给出的类,并返回对新创建的引用 EAX中的对象。
析构函数调用的flag参数中的值为False 使用inherited关键字调用析构函数。在这 例如,析构函数的行为类似于普通方法。值为True 在析构函数调用的flag参数中指示 析构函数是通过实例对象调用的。在这种情况下, 析构函数释放出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;
在所有情况下,来自asm
的对象访问并不意味着以这种方式完成。您无法直接访问类型信息,因此使用它可能非常困难。使用现有的class
实例,您可以使用asm
方法执行任何操作;但要创建实例,并使用类类型,asm
绝对不是自然的方式!
答案 1 :(得分:2)