如何获取未类型化 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 不同的值。

哪种方法是正确获取非类型化数据地址的方法?

编辑:人们去吃午饭是为了与问题无关的事情。我已经编辑了问题,希望人们会关注问题,而不是示例。

我无法在 XE8 中重现您的情况。 调试器为 @SaltPointer(@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

即使调试器赋予 @SaltPointer(@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