Marshal对于作为参数的结构大小有内部限制, 但文档中却没有指明这一内部限制是多少. 下面的代码测试结构过大时, Marshal抛出异常
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.ComponentModel;
namespace CodeDomWrapper
{
public struct TestBool
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.I1, SizeConst = 65521)]
public byte[] bool_arr;
[DllImport("kernel32.dll", EntryPoint="RtlZeroMemory")]
static extern void ZeroMemory (
ref TestBool b,
int Length);
public static void Main()
{
TestBool tb = new TestBool();
try
{
ZeroMemory(ref tb, Marshal.SizeOf( tb) );
}
catch(Exception ex)
{
Console.WriteLine( ex.ToString() );
}
}
};
}
|
这么一段代码, 通过改变
SizeConst 的取值, 可以测试出Marshal的内部限制是 65520. 这个值是64K(65536 减去16), 16显然是一个合适的数据结构的头部的大小.
这一限制对.NET 1.1和2.0都是相同的.
这样的bug在程序在很难解决, 因为结构过大时它给出的异常信息是:
System.Runtime.InteropServices.MarshalDirectiveException: Cannot marshal 'parameter #1': Internal limitation: structure is too complex or too large.
两种可能, 结构太大, 或者太复杂.
怎么样算是复杂? 我的应用还没有碰到这个"复杂"的极限, 但结构大小的极限, 却是早就超过了的.
手工测试结构大小的内部限制是十分麻烦的, 下面的代码通过运行时动态构造出一个内存中的C#程序并编译运行可以自动测试出这一限制:
|
文件: | Marshal_struct_size_limit.zip |
大小: | 1KB |
下载: | 下载 |
|
当碰到结构太大时的补救办法:
(I[DllImport("my_native.dll")]
private static extern int Set_ BigStruct(IntPtr param);
internal static int BigStruct_Delegate(ref BigStruct param)
{
System.IntPtr ip = IntPtr.Zero;
try
{
int param_size = Marshal.SizeOf( param);
ip = Marshal.AllocHGlobal(param_size);
// Copy the struct to unmanaged memory.
Marshal.StructureToPtr(param, ip, false);
int ret = Set_ BigStruct (ip); param = (
BigStruct
)Marshal.PtrToStructure(ip, typeof (
BigStruct
));
return ret;
}
catch (Exception ex)
{
Debug.Assert(false, ex.ToString());
}
finally
{
//Free the unmanaged memory.
//System.Runtime.InteropServices.COMException
//Invalid memory access
try
{
Marshal.FreeHGlobal(ip);
}
catch (Exception ex)
{
Debug.Assert(false, ex.ToString());
}
}
return 0;
}
|
通过 Marshal.StructureToPtr , 把结构按Marshal要求复制到一段指定的内存地址上. 然后真正的native函数
Set_
BigStruct 的函数原型要改成 IntPtr, 这与ref BigStruct var 从参数传递的角度看是一样的, 即native代码参数类型均为 foo(
BigStruct * var);
阅读(1920) | 评论(0) | 转发(0) |