gpt4 book ai didi

Delphi 堆栈错位 + com 编码 = 错误编码

转载 作者:行者123 更新时间:2023-12-03 14:52:50 26 4
gpt4 key购买 nike

这并不完全是一个直截了当的问题,因为我刚刚解决了它,而更像是“我做对了吗”类型的问题,并提醒那些可能陷入困境的人。

事实证明,Delphi 不会在堆栈上对齐变量,并且没有指令/选项来控制此行为。我的 XP SP3 上的默认 COM 编码器在编码记录时似乎需要 4 字节对齐。更糟糕的是,当它遇到未对齐的指针时,它不会返回错误,哦不:它会将指针向下舍入到最近的 4 字节边界并继续这样。

因此,如果您通过引用将在堆栈上分配的记录传递到 COM 编码函数中,那么您就完蛋了,甚至不知道。

这个问题可以通过使用 New/Dispose 来分配记录来解决,因为内存管理器倾向于将所有内容对齐到 8 字节或更好,但是上帝,这很烦人,无论是未对齐部分还是“trim-down-pointers”部分。

这真的是原因吗,还是我哪里错了?

更新:如何重现(Delphi 2007 for Win32)。

uses SysUtils;

type
TRec = packed record
a, b, c, d, e: int64;
end;

TDummy = class
protected
procedure Proc(param1: integer);
end;

procedure TDummy.Proc(param1: integer);
var a, b, c: byte;
rec: TRec;
begin
a := 5;
b := 9;
c := 100;

rec.a := param1;
rec.b := a;
rec.c := b;
rec.d := c;
writeln(IntToHex(integer(@rec), 8));
readln;
end;

var Obj: TDummy;
begin
obj := TDummy.Create;
try
obj.Proc(0);
finally
FreeAndNil(obj);
end;
end.

这给出了奇怪的结果地址,显然没有与任何东西对齐。如果没有,请尝试向“a, b, c: byte”添加更多字节变量(并且不要忘记在函数末尾模拟它们的一些工作)。

COM 部分更容易重现,但解释起来更长。创建一个名为 Sample Server 的新 VCL 应用程序,添加一个实现 ISampleObject 的 COM 对象 SampleObject,具有类型库、自由线程、单实例(确保检查 ISampleObject 在类型库中标记为 Ole Automation)。打开类型库,声明一个具有五个 __int64 字段的新 SampleRecord。将带有单个 SampleRecord* 输出参数的 SampleFunction 添加到 ISampleObject。通过返回固定值在 TSampleObject 中实现 SampleFunction:

function TSampleObject.SampleFunction(out rec: SampleRecord): HResult;
begin
rec.a := 1291;
rec.b := 742310;
//...
Result := S_OK;
end;

注意 Delphi 如何在自动生成的类型库头代码中将 SampleRecord 声明为“打包记录”:

SampleRecord = packed record
a: Int64;
b: Int64;
//...
end;

我已经检查过,至少在 Delphi 2010 中已修复此问题。自动生成的记录不会打包在那里。

注册 COM 服务器。运行它。

现在修改上面的源代码(示例 1)以调用此服务器,而不仅仅是执行 writeln:

uses SysUtils, Windows, ActiveX, SampleServer_TLB;

procedure TDummy.Proc(param1: integer);
var a, b, c: byte;
rec: SampleRecord;
Server: ISampleObject;
begin
a := 5;
b := 9;
c := 100;

rec.a := param1;
rec.b := a;
rec.c := b;
rec.d := c;
Server := CoSampleObject.Create;
hr := Server.SampleFunction(rec);
writeln('@: 'IntToHex(integer(@rec), 8)+', rec.a='+IntToStr(rec.a));
readln;
end;

var Obj: TDummy;
begin
CoInitializeEx(nil, COINIT_MULTITHREADED);
obj := TDummy.Create;
try
obj.Proc(0);
finally
FreeAndNil(obj);
CoUninitialize();
end;
end.

观察到,当rec的地址未对齐时,rec字段的值是错误的(具体来说,位移到8、16或24位,有时会换行到下一个值)。

最佳答案

您有堆栈未对齐的示例吗? Delphi 应该将所有内容都对齐到 4 字节边界。我能想到的唯一会导致错位的情况是,如果调用链上的某个地方有一些汇编代码明确地对堆栈做了一些操作以使其错位。

关于Delphi 堆栈错位 + com 编码 = 错误编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3111689/

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