在out中编组结构会返回垃圾

时间:2012-11-16 18:46:05

标签: c# struct marshalling ref

我正在尝试将一些数据存储到非托管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));

我觉得很简单。我知道charset和packing是可以的,因为我只是将其他结构定义中的struct layout属性粘贴到与实际大多数代码相同的dll中。

当读取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);

用[In,MarshalAs(UnmanagedType.LPStruct)]代替ref做了诀窍,但为什么呢?

感谢您的回答,快乐的编码。

2 个答案:

答案 0 :(得分: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

使用类的优点是可以在构造函数中自动设置大小,而不是调用者必须使用struct version显式设置它。

答案 1 :(得分: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,charset,编组等都可以。就我而言,它是。

如果您只是在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(不要问...)但调用程序无法确保最终收件人实际读取了数据。

最终谁会照顾清理记忆?

我可能会与被调用者一起清理数据并且调用者检查数据是否被释放/清除如果没有在创建新块之前和退出时相同,除非我发现方案更简单一些。