0

アンマネージ dll との間でデータを保存および取得しようとしています。構造体を可能な限り単純化することで問題を絞り込もうとしましたが、ここに私が行き着いているものがあります:

構造定義

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public class MyStruct
{
  private UInt32 size;    
  public UInt16 SomeData;

  public MyStruct()
  {
    size = (UInt32)Marshal.SizeOf(this);
    this.SomeData = 66; //just put any non 0 value for test
  }
}

DLL のインポート:

[DllImport(MY_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
[return:MarshalAs(UnmanagedType.U1)]
public static extern bool SetData(ref MyStruct ms);
[DllImport(MY_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr GetData();

関数呼び出し:

MyStruct ms_in = new MyStruct();
bool b = Wrapper.SetData(ref ms_in);
IntPtr ptr = Wrapper.GetData();
MyStruct ms_out = (MyStruct)Marshal.PtrToStructure(ptr, typeof(MyStruct));

十分に単純だと思います。ほとんどのコードで実際に行ったのと同じ dll の別の構造体定義から構造体レイアウト属性を貼り付けただけなので、charset とパッキングが問題ないことはわかっています。

ms_out の内容を読み取ると、ゴミ (ランダムな大きな数字) でいっぱいになります。

試行錯誤してようやく私の質問に対する答えを見つけましたが、あまり理解できません。作業バージョンは次のとおりです。

[DllImport(MY_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool SetData( [In, MarshalAs(UnmanagedType.LPStruct)] MyStruct ms);

ref を [In, MarshalAs(UnmanagedType.LPStruct)] に置き換えるとうまくいきましたが、なぜですか?

回答ありがとうございます。コーディングをお楽しみください。

4

2 に答える 2

0

これをさらに調査すると、C# 構造体またはクラスの両方を宛先としてマーシャリングを使用できることがわかりました。

構造体の使用:

[StructLayout...
struct MyStruct
{
  //some properties

  //can't have parameterless constructor
  public void MakeStruct()
  {
    size = ...;
    //initialize properties as needed
  }
}   
...
public static extern bool SetData(ref MyStruct ms); //ref is ok for struct, equivalent to c &struct

クラスの使用:

[StructLayout...
class MyClass
{
  //some properties

  //parameterless constructor
  public void MyClass()
  {
    size = ...;
    //initialize properties as needed
  }
}

...
public static extern bool SetData([In, MarshalAs(UnmanagedType.LPStruct)]  MyClass ms); //no ref for class

クラスを使用すると、呼び出し元が構造体バージョンで明示的にサイズを設定する代わりに、コンストラクターでサイズを自動的に設定できるという利点があります。

于 2012-11-19T11:03:26.990 に答える
0

管理されていない dll から戻ったときに構造体がガベージでいっぱいになるのはなぜですか?

これは、アンマネージド dll にデータを設定してからそれを取得したときに発生しますが、ローカルでアンマネージド ポインターを作成し、データを設定して読み戻した場合には発生しません。

MyClass x = new MyClass(); //create class
IntPtr ptr2 = Marshal.AllocHGlobal(Marshal.SizeOf(x)); //allocate unmanaged memory
Marshal.StructureToPtr(x, ptr2, false); //marshall to unmanaged memory
MyClass xOut = (MyClass)Marshal.PtrToStructure(ptr2, typeof(MyClass)); //marshall from unmanaged memory
Marshal.FreeHGlobal(ptr2); //free unmanaged memory

データが上記のテストで「生き残った」場合、StructLayout、文字セット、マーシャリングなどはすべて問題ありません。私の場合はそうです。

C# でデータを作成し、それへのポインターをアンマネージ コードに渡すだけの場合、ポインター アドレスが有効であることだけは確実です。C# 変数がスコープ外になるとすぐに、ポインターが指すデータは無効になります。

次のメソッドは、文字列を含む、テストしたあらゆる種類のデータで機能します。

[return:MarshalAs(UnmanagedType.U1)]
public static extern bool SetData( IntPtr data); //no marshalling in, no ref, no problem...

MyClass x = new MyClass(); //create class
//...store some data in x....
IntPtr ptrIn = Marshal.AllocHGlobal(Marshal.SizeOf(x)); //allocate unmanaged memory
Marshal.StructureToPtr(x, ptrIn, false);    //marshall to unmanaged memory

bool b = Wrapper.SetData(ref ms_in); //store data in unmanaged dll
IntPtr ptrOut = Wrapper.GetData(); //get data back from unmanaged dll

MyClass xOut = (MyClass)Marshal.PtrToStructure(ptrOut, typeof(MyClass)); //marshall from unmanaged memory

Marshal.FreeHGlobal(ptrIn); //free unmanaged memory

ここでの問題は、呼び出し元が割り当てられたメモリを解放しなければならないことです。私のシナリオでは、これはさらに別の管理されていない dll にデータを渡すために使用されます (質問しないでください...) が、呼び出し元のプログラムには、最終的な受信者が実際にデータを読み取ったことを確認する方法がありません。

最後に記憶を整理するのは誰ですか?

私はおそらく、呼び出し先がデータをクリーンアップし、呼び出し元がデータが解放/クリーニングされていることを確認してから新しいブロックを作成し、シナリオが少し単純であることが判明しない限り、終了時に同じことを行うでしょう。

于 2012-11-19T11:40:46.743 に答える