这是非托管代码封装的结构体: |
typedef struct structural_MEDIA_MOTION_INFO { DWORD dwEnable; DWORD dwRangeCount; DWORD dwRange[3][4]; DWORD dwSensitive[3]; } MEDIA_MOTION_INFO; |
这是非托管代码中的函数原型: |
void KGetMotionInfo(HANDLE h, MEDIA_MOTION_INFO* MotionInfo) |
做法一:
把非托管结构体封装成托管结构体 |
[StructLayout(LayoutKind.Sequential, Pack=1)] public struct MediaMotionInfo { public Int32 dwEnable; public Int32 dwRangeCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] public int[] dwRange; [MarshalAs(UnmanagedType.ByValArray,SizeConst = 3)] public int[] dwSensitive; } |
因为非托管代码要求地址传递(MEDIA_MOTION_INFO*),所以本处用(ref MediaMotionInfo) |
/// <summary> /// Get the Server’s Motion setting value. /// </summary> /// <param name="h">[in] The handle returned by KOpenInterface()</param> /// <param name="MotionInfo">[out] the Motion information on the video server.</param> [DllImport(DLLFILE, EntryPoint = "KGetMotionInfo")] public static extern void GetMotionInfo(IntPtr h,ref MediaMotionInfo MotionInfo); |
调用结果正确 mmi属性是[out],所以可以得到正确的结果结构体。 |
private void getMotionButton_Click(object sender, EventArgs e) { mmi = new Acti10000Sdk.MediaMotionInfo(); Acti10000Sdk.GetMotionInfo(h, ref mmi); } } |
解决方法二:
我把非托管的结构体封装成一个类,结构如下: |
[StructLayout(LayoutKind.Sequential, Pack = 1)] public class MediaMotionInfo { public Int32 dwEnable; public Int32 dwRangeCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] public int[] dwRange=new int[12]; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public int[] dwSensitive=new int[3]; } |
由于我封装的是个类,所以直接写MediaMotionInfo就是引用类型,我认为此处和非托管代码也是匹配的。 |
/// <summary> /// Get the Server’s Motion setting value. /// </summary> /// <param name="h">[in] The handle returned by KOpenInterface()</param> /// <param name="MotionInfo">[out] the Motion information on the video server.</param> [DllImport(DLLFILE, EntryPoint = "KGetMotionInfo")] public static extern void GetMotionInfo(IntPtr h, MediaMotionInfo MotionInfo); |
此处参数为一个类的引用,指向堆的上的对象。问题是这样做为什么得不到返回的对象呢? |
private void getMotionButton_Click(object sender, EventArgs e) { mmi = new Acti10000Sdk.MediaMotionInfo(); Acti10000Sdk.GetMotionInfo(h, mmi); } } |
问题:第二种方法哪里错了。
看不全,而且字太小
似乎第二种应该写为:
[DllImport(DLLFILE, EntryPoint = "KGetMotionInfo")]
public static extern void GetMotionInfo(IntPtr h, [Out]MediaMotionInfo MotionInfo);
我认为主要问题在这里:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public int[] dwRange=new int[12];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public int[] dwSensitive=new int[3];
上面两个数组,在struct里面,这两个数组被分配到了dwRangeCount之后的连续地址中,也就是说在struct 所对应的堆栈中地址分配是这样的:
dwEnable 4bytes
dwRangeCount 4bytes
dwRange 12*4 bytes
dwSensitive 3*4 bytes
而在class 里面,两个数组是作为引用类型存在的,也就是说class的内存中dwRangeCount 和 dwSensitive 的位置并不是值,而是引用。这是本质区别。
在class所对应的管理堆中地址分配是这样的:
dwEnable 4bytes
dwRangeCount 4bytes
dwRange 4 bytes ->指向dwRange 数组在管理堆中所在的实际位置
dwSensitive 4 bytes ->指向dwSensitive 数组在管理堆中所在的实际位置