gpt4 book ai didi

delphi - 具有 'varargs' 的函数如何检索堆栈的内容?

转载 作者:行者123 更新时间:2023-12-03 14:35:57 24 4
gpt4 key购买 nike

通常,在 Delphi 中,我们会使用“const 数组”方法声明一个具有可变数量参数的函数。然而,为了与用 C 编写的代码兼容,可以将一个鲜为人知的“varargs”指令添加到函数声明中(我在阅读 Rudy 的优秀“Pitfalls of convering”文档时了解到这一点)。

举个例子,可以有一个 C 语言函数,声明如下:

void printf(const char *fmt, ...)

在 Delphi 中,这将变成:

procedure printf(const fmt: PChar); varargs;

我的问题是:在实现使用“varargs”指令定义的方法时,如何获取堆栈的内容?

我希望存在一些用于此目的的工具,例如 va_start()、va_arg() 和 va_end() 函数的 Dephi 翻译,但我在任何地方都找不到它。

请帮忙!

PS:请不要在讨论“为什么”或“常量数组”替代方案时偏离主题 - 我需要它来为 Xbox 游戏中的函数编写类似 C 的补丁(请参阅 Delphi Xbox 模拟器项目“Dxbx”) ' 在 sourceforge 上了解详细信息)。

最佳答案

好的,我看到您问题中的澄清意味着您需要在 Delphi 中实现 C 导入。在这种情况下,您需要自己实现可变参数。

所需的基础知识是 x86 上的 C 调用约定:堆栈向下增长,C 将参数从右向左压入。因此,指向最后声明的参数的指针在增加最后声明的参数的大小后,将指向尾部参数列表。从那时起,只需读取参数并将指针增加适当的大小即可深入堆栈。 32位模式下的x86堆栈通常是4字节对齐的,这也意味着字节和字作为32位整数传递。

无论如何,这里有一个演示程序中的帮助记录,展示了如何读取数据。请注意,Delphi 似乎以一种非常奇怪的方式传递扩展类型;但是,您可能不必担心这一点,因为 10 字节 float 在 C 中通常并不广泛使用,甚至在最新的 MS C、IIRC 中也没有实现。

{$apptype console}

type
TArgPtr = record
private
FArgPtr: PByte;
class function Align(Ptr: Pointer; Align: Integer): Pointer; static;
public
constructor Create(LastArg: Pointer; Size: Integer);
// Read bytes, signed words etc. using Int32
// Make an unsigned version if necessary.
function ReadInt32: Integer;
// Exact floating-point semantics depend on C compiler.
// Delphi compiler passes Extended as 10-byte float; most C
// compilers pass all floating-point values as 8-byte floats.
function ReadDouble: Double;
function ReadExtended: Extended;
function ReadPChar: PChar;
procedure ReadArg(var Arg; Size: Integer);
end;

constructor TArgPtr.Create(LastArg: Pointer; Size: Integer);
begin
FArgPtr := LastArg;
// 32-bit x86 stack is generally 4-byte aligned
FArgPtr := Align(FArgPtr + Size, 4);
end;

class function TArgPtr.Align(Ptr: Pointer; Align: Integer): Pointer;
begin
Integer(Result) := (Integer(Ptr) + Align - 1) and not (Align - 1);
end;

function TArgPtr.ReadInt32: Integer;
begin
ReadArg(Result, SizeOf(Integer));
end;

function TArgPtr.ReadDouble: Double;
begin
ReadArg(Result, SizeOf(Double));
end;

function TArgPtr.ReadExtended: Extended;
begin
ReadArg(Result, SizeOf(Extended));
end;

function TArgPtr.ReadPChar: PChar;
begin
ReadArg(Result, SizeOf(PChar));
end;

procedure TArgPtr.ReadArg(var Arg; Size: Integer);
begin
Move(FArgPtr^, Arg, Size);
FArgPtr := Align(FArgPtr + Size, 4);
end;

procedure Dump(const types: string); cdecl;
var
ap: TArgPtr;
cp: PChar;
begin
cp := PChar(types);
ap := TArgPtr.Create(@types, SizeOf(string));
while True do
begin
case cp^ of
#0:
begin
Writeln;
Exit;
end;

'i': Write(ap.ReadInt32, ' ');
'd': Write(ap.ReadDouble, ' ');
'e': Write(ap.ReadExtended, ' ');
's': Write(ap.ReadPChar, ' ');
else
Writeln('Unknown format');
Exit;
end;
Inc(cp);
end;
end;

type
PDump = procedure(const types: string) cdecl varargs;
var
MyDump: PDump;

function AsDouble(e: Extended): Double;
begin
Result := e;
end;

function AsSingle(e: Extended): Single;
begin
Result := e;
end;

procedure Go;
begin
MyDump := @Dump;

MyDump('iii', 10, 20, 30);
MyDump('sss', 'foo', 'bar', 'baz');

// Looks like Delphi passes Extended in byte-aligned
// stack offset, very strange; thus this doesn't work.
MyDump('e', 2.0);
// These two are more reliable.
MyDump('d', AsDouble(2));
// Singles passed as 8-byte floats.
MyDump('d', AsSingle(2));
end;

begin
Go;
end.

关于delphi - 具有 'varargs' 的函数如何检索堆栈的内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/298373/

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