5

CのCONTAINING_RECORDマクロは、構造内のフィールドメンバーのアドレスに基づいて、構造/レコード型変数のベースアドレスを返します。コールバックをトリガーするWindowsAPI関数に事前定義されたレコードポインタのみを渡すことができる場合に非常に役立ちます。

たとえば、次のようなタイプがある場合:

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 Async I / O)のオーバーラップしたデータポインターと一緒にオブジェクトポインターを渡したいと思います。

Delphi(2006)で同様の機能を提供する同等の機能はありますか?

4

3 に答える 3

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 に答える
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;

動作します。varのパラメータリストにを追加する必要があることに注意してくださいSomeCallback

于 2013-02-08T16:12:55.033 に答える
1

Delphiには直接同等のものはありません。Delphiにはマクロとプリプロセッサがないため、そのようなことはあり得ません。

明らかに、Delphiでは、特定のレコードの特定のフィールドのオフセットを計算することができます。そして、含まれているレコードのベースアドレスを取得するために必要な減算を実行するのは簡単です。ただし、Cマクロで実行できるように、その計算を一度表現して、一般的なフィールド/レコードのペアに再利用することはできません。

この方法で作業するフィールド/レコードのペアごとに1つの関数を作成する必要があります。

于 2013-02-08T16:05:30.793 に答える