在一个常见的Delphi模式中,我将一个值作为无类型const 传递给函数:
procedure DoSomething(const Something; SomethingLength: Integer);
begin
//...
end;
在这个例子中,我碰巧传递了Windows FORMATETC
结构:
procedure Test;
var
omgp: TFormatEtc;
begin
omgp := Default(TFormatEtc);
omgp.cfFormat := RegisterClipboardFormat('CF_PNG');
omgp.ptd := nil;
omgp.dwAspect := DVASPECT_CONTENT or DVASPECT_THUMBNAIL;
omgp.lindex := -1; //all pages
omgp.tymed := TYMED_HGLOBAL;
DoSomething(omgp, SizeOf(omgp));
end;
我需要获取此数据的地址,因此我可以将其传递给基础Windows函数需要指向数据的指针。
为了做到这一点,我一直使用Pointer(@data)
:
procedure DoSomething(const Something; SomethingLength: Integer);
begin
SomethingThatNeedsAPointer(Pointer(@Something));
end;
直到这一个API调用,在一个特定的情况下,失败(它返回错误的值)。没有特别的原因,我碰巧仔细查看传递的指针值。当我在调试器中检查每个参数值时,我发现了一些可怕的东西。我注意到了:
@Something
Pointer(@Something)
返回不同的值。
@Something
应该已经是指针Pointer(@Something)
应该是一个多余的演员正确以哪种方式获取无类型数据的地址?
编辑:人们就与问题无关的事情去吃午餐。我编辑了这个问题,希望人们能够专注于这个问题,而不是示例。
答案 0 :(得分:2)
我无法在XE8中重现您的情况。
调试器显示@Salt
和Pointer(@Salt)
的相同地址。
同样,此测试代码段的输出也是相同的。 我只能假设调试器告诉你的是不真实的。
(更新:XE7中的测试会在调试器中重现错误。但代码段的结果是相同的。)
program Test;
{$APPTYPE CONSOLE}
procedure Test1(p: Pointer);
begin
WriteLn(Cardinal(p));
end;
procedure Test(const Salt);
begin
Test1(@Salt);
Test1(Pointer(@Salt));
Test1(Addr(Salt));
end;
var
s:AnsiString;
begin
s := 'Test';
Test( s[1]);
ReadLn;
end.
答案 1 :(得分:2)
这是一个调试器错误,调试器错误地报告了Pointer(@Salt)
的值。我可以在XE5,XE6和XE7中重现故障,但不能在XE4和XE8中重现故障。因此,这似乎是XE5中引入的缺陷,并在XE8中被删除。
每当您看到这样的问题时,总是有可能发生调试器故障。在这种情况下,我们可以通过此程序证明故障在于调试器:
{$APPTYPE CONSOLE}
uses
System.SysUtils;
procedure DoSomething(const Salt; SaltLength: Integer);
begin
Writeln(IntToHex(NativeUInt(Pointer(@Salt)), 8));
Writeln(IntToHex(NativeUInt(@Salt), 8));
end;
procedure Test;
var
salt: AnsiString;
begin
salt := 'salt';
DoSomething(salt[1], Length(salt));
end;
begin
Test;
end.
该程序输出:
007C9BD4 007C9BD4
尽管调试器将@Salt
和Pointer(@Salt)
赋予不同的值。
请注意此程序
{$APPTYPE CONSOLE}
procedure DoSomething(const Salt; SaltLength: Integer);
var
i: Integer;
P: PAnsiChar;
begin
P := @Salt;
for i := 0 to SaltLength-1 do
begin
Writeln(P^);
inc(P);
end;
end;
procedure Test;
var
salt: AnsiString;
begin
salt := 'salt';
DoSomething(salt[1], Length(salt));
end;
begin
Test;
end.
输出:
s a l t