如何获取未类型化 const 数据的地址?
How to get address of untyped const data?
在常见的 Delphi 模式中,我将一个值作为 未类型化的常量 传递给函数:
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 调用,在一个特定的情况下,失败(它是 returning 错误的值)。 没有特别的原因,我碰巧仔细观察了传递的指针值。当我检查 每个 调试器中的参数值时,我发现了一些可怕的事情。我注意到:
@Something
Pointer(@Something)
return 不同的值。
@Something
应该已经是指针了
Pointer(@Something)
应该是多余的转换
哪种方法是正确获取非类型化数据地址的方法?
编辑:人们去吃午饭是为了与问题无关的事情。我已经编辑了问题,希望人们会关注问题,而不是示例。
我无法在 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.
这是一个调试器错误,调试器错误地报告了 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
在常见的 Delphi 模式中,我将一个值作为 未类型化的常量 传递给函数:
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 调用,在一个特定的情况下,失败(它是 returning 错误的值)。 没有特别的原因,我碰巧仔细观察了传递的指针值。当我检查 每个 调试器中的参数值时,我发现了一些可怕的事情。我注意到:
@Something
Pointer(@Something)
return 不同的值。
@Something
应该已经是指针了Pointer(@Something)
应该是多余的转换
哪种方法是正确获取非类型化数据地址的方法?
编辑:人们去吃午饭是为了与问题无关的事情。我已经编辑了问题,希望人们会关注问题,而不是示例。
我无法在 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.
这是一个调试器错误,调试器错误地报告了 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