【问题标题】:How to get address of untyped const data?如何获取无类型常量数据的地址?
【发布时间】:2015-07-28 17:47:17
【问题描述】:

在一个常见的 Delphi 模式中,我将一个值作为 untyped 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 调用在一种特殊情况下失败(它返回错误的值)。 没有特别的原因,我碰巧仔细查看了正在传递的指针值。当我在调试器中检查every参数值时,我注意到了一些可怕的事情。我注意到:

@Something
Pointer(@Something)

返回不同的值。

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

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

编辑:人们去吃午饭的东西与问题无关。我已经编辑了这个问题,希望人们能专注于这个问题,而不是例子。

【问题讨论】:

  • 在 XE2 中,@Salt 和 Pointer(@Salt) 具有相同的值。如果您遇到问题,您可以作弊并使用absolute 获取地址。声明一个字节的局部变量或类似的东西,并将其绝对为salt。然后获取本地变量的地址。
  • 您的代码无法编译。 DoSomething 需要 2 个参数。
  • 最后,使用字符串保存二进制数据是在玩火。对于非 ASCII 数据,预计会出现与语言环境相关的失败。它是二进制的。不要假装其他。你不是用 PHP 编码,你有一个选择。
  • 人们去吃午饭,吃的东西与问题无关。我已经编辑了这个问题,希望人们能专注于这个问题,而不是这个例子。 几乎没有。 FWIW,我回答了这个问题,并对文本/二进制问题发表了评论。您的编辑让我们的答案现在看起来很奇怪。
  • 好的。正如@Sertac 的评论。这是一个有效的评论。 FWIW,我不喜欢传递salt[1]。如果盐是空的怎么办? Pointer(salt)^ 更好。将文本视为二进制的问题取模。无论如何,你永远不会避免人们对附带问题发表评论。这就是程序员的想法。

标签: delphi delphi-xe6


【解决方案1】:

我无法在 XE8 中重现您的情况。 调试器显示@SaltPointer(@Salt) 的地址相同。

同样,这个测试 sn-p 的输出是相同的。 我只能假设调试器告诉你的东西在某种程度上是不真实的。

更新:XE7 中的测试重现了调试器中的错误。不过 sn-p 的结果是相同的。)

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.

【讨论】:

  • 调试器缺陷似乎已在 XE8 中修复,但在早期版本中存在
  • @DavidHeffernan,我也可以在 XE7 中重现。关注列表中的 Addr(Salt) 也可以。
【解决方案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 一种 l 吨

【讨论】:

  • 呃。 另一个 Delphi 中的错误?我们已经拥有新版本的 Delphi 将近一年了,我们仍然试图用它发布一个应用程序。为什么,哦,为什么,我不只是升级到 Delphi 7。
  • D5 几乎没有错误,但你至少已经习惯了这些错误!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多