我正在尝试将一些数据存储到非托管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做了诀窍,但为什么呢?
感谢您的回答,快乐的编码。
答案 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(不要问...)但调用程序无法确保最终收件人实际读取了数据。
最终谁会照顾清理记忆?
我可能会与被调用者一起清理数据并且调用者检查数据是否被释放/清除如果没有在创建新块之前和退出时相同,除非我发现方案更简单一些。