Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2308831
  • 博文数量: 527
  • 博客积分: 10343
  • 博客等级: 上将
  • 技术积分: 5565
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-26 23:05
文章分类

全部博文(527)

文章存档

2014年(4)

2012年(13)

2011年(19)

2010年(91)

2009年(136)

2008年(142)

2007年(80)

2006年(29)

2005年(13)

我的朋友

分类: WINDOWS

2008-03-05 16:18:46

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);
阅读(1828) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~