OK, tôi thấy làm rõ trong câu hỏi của bạn có nghĩa là bạn cần phải thực hiện nhập C trong Delphi. Trong trường hợp đó, bạn cần tự triển khai vararg.
Kiến thức cơ bản cần thiết là quy ước gọi C trên x86: ngăn xếp phát triển xuống dưới và C đẩy đối số từ phải sang trái. Vì vậy, một con trỏ đến đối số được khai báo cuối cùng, sau khi nó được tăng lên bởi kích thước của đối số được khai báo cuối cùng, sẽ trỏ đến danh sách đối số đuôi. Từ đó, nó chỉ đơn giản là vấn đề đọc đối số và tăng con trỏ bằng kích thước thích hợp để di chuyển sâu hơn vào ngăn xếp. Các x86 ngăn xếp trong chế độ 32-bit là 4-byte liên kết nói chung, và điều này cũng có nghĩa là byte và các từ được thông qua như số nguyên 32-bit.
Dù sao đi nữa, đây là bản ghi trợ giúp trong chương trình demo cho biết cách đọc dữ liệu. Lưu ý rằng Delphi dường như đang chuyển các kiểu mở rộng theo một cách rất kỳ lạ; Tuy nhiên, bạn có thể sẽ không phải lo lắng về điều đó, vì float 10-byte thường không được sử dụng rộng rãi trong C, và thậm chí không được triển khai trong MS C, IIRC mới nhất.
{$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.
Điều này có vẻ tuyệt vời! Tôi đã ngạc nhiên khi thấy có thực sự không cần phải sử dụng lắp ráp để có được nội dung đăng ký ESP. Cảm ơn vì điều này - ví dụ tuyệt vời quá! – PatrickvL
Lưu ý rằng mã cần thích ứng nếu nó hoạt động trên x64 - hàm Align đặc biệt cắt bớt các con trỏ đến các giá trị 32 bit. –