5

C 中的CONTAINING_RECORD 宏根据结构中字段成员的地址返回结构/记录类型变量的基地址。在我只能将预定义的记录指针传递给触发回调的某些 Windows API 函数的情况下,它非常有用。

例如,如果我有一些类型,例如:

type
   tInnerRecord = record
      x, y : integer;
   end;
   pInnerRecord = ^tInnerRecord

tOuterRecord = record
   field1 : integer;
   inner : tInnerRecord;
   field2 : integer;
end;
pOuterRecord = ^tOuterRecord;

我希望能够做类似的事情:

procedure SomeCallback( pIn : pInnerRecord ); stdcall;
var
   Out : pOuterRecord;
begin
   Out := CONTAINING_RECORD(pIn, tOuterRecord, inner);
   Out.field1 := pIn.x + pIn.y;
end;

在我的具体情况下,我想将我的对象指针与 ReadFileEx(Windows 异步 I/O)的重叠数据指针一起传递,以便我可以在回调中访问该对象。

在 Delphi (2006) 中是否有提供类似功能的等效功能?

4

3 回答 3

3

您应该可以使用一些强制转换和指针算术来做到这一点,但不能使用打包好的宏(正如 David 已经提到的那样)。就像是:

procedure SomeCallback(var pIn: pInnerRecord); stdcall;
const
  p = pOuterRecord(nil);
var
  Offset: Integer;
  Out: pOuterRecord;
begin
  Offset := INT_PTR(@p^.inner) - INT_PTR(p);
  Out := Pointer(INT_PTR(@pIn) - Offset);
  Out.field1 := pIn.x + pIn.y;
end;

var
 outer: tOuterRecord;
 inner: tInnerRecord;
begin
  inner.x := 1;
  inner.y := 2;
  outer.inner := @inner;
  SomeCallback(outer.inner);
  Assert(outer.field1 = 3);
end;

作品。请注意,我必须varSomeCallback.

于 2013-02-08T16:12:55.033 回答
3

我刚刚没有编译器,但这应该可以解决问题:

Out := Pointer( Cardinal(pIn) - Cardinal(@TOuterRecord(nil^).inner));

David 解释了为什么在 Delphi 中没有直接的等效函数。所以这是一个最接近的函数:

function ContainingRecord( var aField; aFieldOffsetRef : Pointer) : Pointer;
{$IF Declared(NativeUInt) = False}
type
  NativeUInt = Cardinal;
{$IFEND}
begin
  Result := Pointer(NativeUInt(@aField) - NativeUInt(aFieldOffsetRef));
end;

调用示例:

Out := ContainingRecord(pIn^, @pOuterRecord(nil).inner);
于 2013-02-08T16:11:09.637 回答
1

Delphi 中没有直接的等价物。不可能有这样的事情,因为 Delphi 没有宏和预处理器。

显然,在 Delphi 中可以计算出特定记录中特定字段的偏移量。然后执行获得包含记录基地址所需的减法是微不足道的。但是你不能做的是一次表达该计算并将其重新用于任何通用字段/记录对,这可以使用 C 宏来完成。

您需要为以这种方式使用的每个字段/记录对编写一个函数。

于 2013-02-08T16:05:30.793 回答