如何获取无类型const数据的地址?

时间:2015-05-17 02:00:27

标签: delphi delphi-xe6

在一个常见的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)

返回不同的值。

Imgur

  • @Something 应该已经是指针
  • Pointer(@Something) 应该是一个多余的演员

正确以哪种方式获取无类型数据的地址?

编辑:人们就与问题无关的事情去吃午餐。我编辑了这个问题,希望人们能够专注于这个问题,而不是示例。

2 个答案:

答案 0 :(得分:2)

我无法在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.

答案 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

尽管调试器将@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