11

古いAPIを使用していて、構造体のポインターを非同期で実行されるアンマネージコードに渡す必要があります。

つまり、構造体ポインターをアンマネージコードに渡した後、アンマネージコードはポインターをコピーして、すぐに戻ります。アンマネージコードは、別のスレッドでバックグラウンドでその構造体にアクセスできます。別のスレッドで実行されるアンマネージコードやスレッド自体を制御することはできません。

固定{}ステートメントは、非同期のアンマネージドピン留め用に設計されていないため、ピン留めには使用できません。

GCHandleは参照のみを固定できるため、GCHandleを使用するには構造体をボックス化する必要があります。私はそれを試しました、そしてそれは働きます。それに関する主な問題は、マネージコードから構造体を更新できないことです。構造体を更新するには、まずボックスを解除し、次に更新してから、もう一度ボックス化する必要がありますが、...おっと...ボックスをもう一度?!?これは、メモリ内の以前のポインタがまだ古い非最新の構造体を指していることを意味し、新しい構造体には別のポインタがあります。これは、新しいポインタをアンマネージコードに渡す必要があることを意味します...場合。

固定{}ステートメントなしで構造体をメモリに固定し、ポインタを変更せずにマネージコードから構造体を更新できるようにするにはどうすればよいですか?

ありがとう。

編集:

考えただけです...構造体を含む親オブジェクトを固定してから、コンテナオブジェクトではなく構造体のポインタを取得する方法はありますか?

4

6 に答える 6

7

構造体のメモリが長期間有効である必要があることを考えると、この場合に固定メモリを使用することはお勧めできません。GCHandle.Alloc() は、構造体をボックス化し、ヒープに格納します。固定されていると、道路の岩を回避する方法を常に見つける必要があるため、ガベージコレクターにとって長期的な負担になります。

簡単な解決策は、アンマネージ メモリ内の構造体にメモリを割り当てることです。Marshal.SizeOf() を使用して構造体のサイズを取得し、Marshal.AllocCoTaskMem() を使用してメモリを割り当てます。これにより、アンマネージ コードに渡す必要があるポインターが取得されます。Marshal.StructureToPtr() でメモリを初期化します。また、PtrToStructure() を使用して、アンマネージ コードによって書き込まれた構造体の更新を読み取ります。

これを頻繁に行うと、常に構造をコピーすることになります。構造のサイズによっては、費用がかかる場合があります。これを回避するには、アンセーフ ポインターを使用して、アンマネージ メモリに直接アクセスします。基本的な構文:

using System;
using System.Runtime.InteropServices;

class Program {
  unsafe static void Main(string[] args) {
    int len = Marshal.SizeOf(typeof(Test));
    IntPtr mem = Marshal.AllocCoTaskMem(len);
    Test* ptr = (Test*)mem;
    ptr->member1 = 42;
    // call method
    //..
    int value = ptr->member1;
    Marshal.FreeCoTaskMem(mem);
  }
  public struct Test {
    public int member1;
  }
}
于 2009-12-05T01:06:43.777 に答える
5

安全でないコードはオプションですか?

// allocate unmanaged memory
Foo* foo = (Foo*)Marshal.AllocHGlobal(sizeof(Foo));

// initialize struct
foo->bar = 0;

// invoke unmanaged function which remembers foo
UnsafeNativeMethods.Bar(foo);
Console.WriteLine(foo->bar);

// update struct
foo->bar = 10;

// invoke unmanaged function which uses remembered foo
UnsafeNativeMethods.Qux();
Console.WriteLine(foo->bar);

// free unmanaged memory
Marshal.FreeHGlobal((IntPtr)foo);

これはコンパイルされ、例外はスローされませんが、動作するかどうかをテストするためのアンマネージ関数が手元にありません。

MSDNから:

AllocHGlobal が LocalAlloc を呼び出すと、LMEM_FIXED フラグが渡されます。これにより、割り当てられたメモリが所定の位置にロックされます。また、割り当てられたメモリはゼロで埋められません。

于 2009-12-05T00:39:49.880 に答える
1

固定する代わりに、Marshal.StructureToPtrMarshal.PtrToStructureを使用して、ネイティブ コードで使用できるメモリに構造体をマーシャリングする必要があります。

于 2009-12-05T00:15:55.827 に答える
0

ActOnMe()構造体に次のようなインターフェイスとメソッドを含めるのはどうですか。

void ActByRef <T1、T2>(ref T1 p1、ref T2 p2);をデリゲートします。
インターフェイスIActOnMe<TT>{ActOnMe <T>(ActByRef <TT、T> proc、ref T param);}
struct SuperThing:IActOnMe <SuperThing>
{{
  int this;
  int that;
  ..。
  void ActOnMe <T>(ActByRef <SuperThing、T>、ref T param)
  {{
    proc(ref this、ref param);
  }
}

デリゲートは参照によってジェネリックパラメーターを受け取るため、ほとんどの場合、静的メソッドへのデリゲートと、そのメソッドとの間でデータを伝送するための構造体への参照を渡すことで、クロージャを作成するオーバーヘッドを回避できます。SuperThingさらに、 toのすでにボックス化されたインスタンスをキャストしてIActOnMe<SuperThing>呼び出すと、構造体への型キャストで発生するような別のコピーを作成するのではなく、そのボックス化されたインスタンスActOnMe<T>のフィールドが更新用に公開されます。

于 2012-03-06T19:05:46.650 に答える
0

構造例:

[StructLayout(LayoutKind.Sequential)]
public struct OVERLAPPED_STRUCT
{
   public IntPtr InternalLow;
   public IntPtr InternalHigh;
   public Int32 OffsetLow;
   public Int32 OffsetHigh;
   public IntPtr EventHandle;
}

それを構造体に固定して使用する方法:

OVERLAPPED_STRUCT over_lapped = new OVERLAPPED_STRUCT();
// edit struct in managed code
over_lapped.OffsetLow = 100;
IntPtr pinned_overlap_struct = Marshal.AllocHGlobal(Marshal.SizeOf(over_lapped));
Marshal.StructureToPtr(over_lapped, pinned_overlap_struct, true);

// Pass pinned_overlap_struct to your unmanaged code
// pinned_overlap_struct changes ...

// Get resulting new struct
OVERLAPPED_STRUCT nat_ov = (OVERLAPPED_STRUCT)Marshal.PtrToStructure(pinned_overlap_struct, typeof(OVERLAPPED_STRUCT));
// See what new value is
int offset_low = nat_ov.OffsetLow;
// Clean up
Marshal.FreeHGlobal(pinned_overlap_struct);
于 2009-12-05T00:26:44.823 に答える