p/invoke碎片--对数组的封送处理

  因为数组是引用类型,所以数组的处理根据数组元素的类型是否为“可直接传递到非托管代码”的类型而分为两种情况。主要目标是看内存是怎么变化的,是复制还是锁定。

数组中的元素是"可直接传递到非托管代码中"的类型

  这种类型很多,比如 int  double 等。

  完成的托管代码和非托管代码如下:

///////////////////////非托管代码
extern "C" __declspec(dllexport) void DoArray(int a[],int length)
{
    for(int i=0;i<length;i++)
    {
        printf("%d\n",a[i]);
        a[i]=99;
    }
}
////////////////////////////托管代码
        [DllImport(@"C:\Users\Administrator\Desktop\pInvoke\CPPDLL\Debug\CPPDLL.dll")]
        private static extern void DoArray(int []arr,int length);

            int[] arr = new int[] {1,2,3,4,5,6,7 };
            DoArray(arr,arr.Length);//断点        foreach(int a in arr)        {          Console.WriteLine(a);        }        

  跟踪过程:断点先下在托管代码调用函数的地方--DoArray处。这是看一下数组的内存情况如下,

0x01FABAA4  01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00  ................
0x01FABAB4  05 00 00 00 06 00 00 00 07 00 00 00 00 00 00 00  ................

可以看到7个数字情况。地址是0x01fabaa4.记下这个地址,继续。。。

  来到非托管代码中,发现a的地址如图:

可以看到,和托管代码中数组的地址是一样的。再执行,打印结果:

1 2 3 4 5 6 7 99  99  99 99 99 99 99.

  结论:如果数组中的元素是“可直接传递到非托管代码”中的类型时。默认的封送方式是采用了[In,Out]修饰的,传递是一个指针,先锁定内存再传递指针,并且非托管代码对数组的修改会反映到托管代码中。

数组的元素是“非可直接传递到非托管代码中”的类型

  托管内存中数组元素是char类型,是一种“非可直接传递到非托管代码中”的类型。看看内存的情况吧。

               private static extern void DoArray2(char [] arr, int length);
        static void Main(string[] args)
           char[] carry = new char[] { ‘a‘,‘b‘,‘c‘};
            DoArray2(carry, carry.Length);
            foreach (char c in carry)
            {
                Console.Write(c + "\t");
            }
////////////////////////非托管代码
extern "C" __declspec(dllexport) void DoArray2(char carry[],int length)
{
    for(int i=0;i<length;i++)
    {
        printf("%c\t",carry[i]);
        carry[i]=‘?‘;
    }
}

断点跟踪,断在DoArray2(carry,carry.Length)调用处,    carry在非托管代码中的分布如下:

0x01D4BAB8  61 00 62 00 63 00 00 00 00 00 00 00 00 00 00 00  a.b.c...........

接着断在非托管代码领域,数组carry地址显示为:

0x004ADC70  61 62 63 00 00 00 3d 00 d8 dd 4a 00 3d 00 22 00  abc...=.??J.=.".

显然,不是一块地址,结果可想而知,修改也不会反映到托管内存中了。

结果:a  b  c  a  b  c   .

  结论:当数组中的元素是“非可直接传递到非托管代码中”的类型时,这时的封送过程:先在非托管内存申请一块内存,把托管内存中字符串的数据传递复制过去,再把新内存的指针作为参数传递给函数,函数对数组的操作都是基于新内存,结果不会返回到托管代码中。另外值得一说的是,当调用完成后,非托管内存中的数据没有被释放掉。

使用IntPtr来封送数组

时间: 2024-12-06 10:37:16

p/invoke碎片--对数组的封送处理的相关文章

p/invoke碎片--对类的封送处理

主要是看默认封送处理行为 按类成员的类型是否为“可直接传递到非托管内存”的类型来分类;按照成员中是否有“可直接传递到非托管内存”的类型来讨论. 所有成员都是“可直接传递到非托管内存”的类型 托管代码和非托管代码: //托管代码 ClassStruct cs = new ClassStruct(); cs.a = 90; cs.d = 23; DoClassStruct(cs); Console.Read(); } [StructLayout(LayoutKind.Sequential)] cla

C#调用C/C++动态库 封送结构体,结构体数组

因为公司一直都是做C++开发的,因客户需要要提供C#版本接口,研究了一下C#,发现其强大简洁, 在跨语言调用方面封装的很彻底,提供了强大的API与之交互.这点比JNA方便多了. Java与C#都只能调用C格式导出动态库,因为C数据类型比较单一,容易映射. 两者都是在本地端提供一套与之映射的C#/java描述接口,通过底层处理这种映射关系达到调用的目的. 一. 结构体的传递 Cpp代码   #define JNAAPI extern "C" __declspec(dllexport) /

p/invoke 碎片-- 对字符串的处理

字符串在内存中的的几种风格 字符串作为参数和返回值 参考 字符串在内存中的几种风格 所谓的风格,也就是字符串在内存中的存在形式.如何存放的,占据内存的大小,还有存放顺序等.在不同的编程语言和不同的平台上字符串风格一般不相同. 1..net中字符串的风格 .net中的托管代码: 1 string strin = "in string"; 2 Console.WriteLine(strin);//断点下在这里 3 Console.Read(); 调试时查找字符串strin的地址发现内存中的

p/invoke碎片,对结构体的处理

结构体的一些相关知识 可直接转换类类型,比如int类型,在托管代码和非托管代码中占据内存大小 和意义都是一个样的. 结构体封送的关键是:在托管代码和非托管代码中定义的一致性.什么是定义的一致性?包括结构体的对齐粒度:结构体成员的顺序和每个结构体成员在托管代码和非托管代码中的占据内存的大小.本来想着是只是类型一样就行了,但是像布尔类型,在托管代码中占据1个字节,但是在非托管代码中是4个字节,也就是非可直接转换类型. 对齐粒度.这个东西感觉和内存布局有关,以前有一个错误的理念:在一个结构体中定义了一

[原]C#与非托管&mdash;&mdash;封送和自动封送

之前说到了如何从C函数声明通过简单的查找替换生成一份C#的静态引用声明(C#与非托管--初体验),因为只是简单说明,所以全部采用的是基础类型匹配和自动封送.自动封送虽然能省去我们不少编码时间,但如果不理解自动封送背后的实际行为,那就如同看魔术师的黑盒子,知其然不知其所以然.而且,自动封送也不是永远有效的万能药,因此这里记录一下封送相关的理解. 非基础类型在C#与Native代码交互时需要进行封送处理,一般的封送处理方式有内存拷贝.固定内存地址,总结如下: 针对string,可以使用Marshal

[转]C# 互操作性入门系列(三):平台调用中的数据封送处理

传送门 C#互操作系列文章: C#互操作性入门系列(一):C#中互操作性介绍 C#互操作性入门系列(二):使用平台调用调用Win32 函数 C#互操作性入门系列(三):平台调用中的数据封送处理 C#互操作性入门系列(四):在C# 中调用COM组件 本专题概要 数据封送介绍 封送Win32数据类型 封送字符串的处理 封送结构体的处理 封送类的处理 小结 一.数据封送介绍 看到这个专题时,大家的第一个疑问肯定是--什么是数据封送呢?(这系列专题中采用假设朋友的提问方式来解说概念,就是希望大家带着问题

C# 互操作性入门系列(三):平台调用中的数据封送处理

好文章搬用工模式启动ing ..... { 文章中已经包含了原文链接 就不再次粘贴了 言明 改文章是一个系列,但只收录了2篇,原因是 够用了 } --------------------------------------------------------------------------------------- C#互操作系列文章: C#互操作性入门系列(一):C#中互操作性介绍 C#互操作性入门系列(二):使用平台调用调用Win32 函数 C#互操作性入门系列(三):平台调用中的数据封

排错“未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配”

问题:在C#给C++传数组类型数据时报此错,相应英文信息为“Type could not be marshaled, because the length of an embed array doesn't not match the declared length in the layout” 原因1.声明的数组长度和实际的数组长度不一致,比如声明的数组长度为1000,实际数组里放了1000多.

C#调用C/C++动态库 封送结构体,结构体数组

因为实验室图像处理的算法都是在OpenCV下写的,还有就是导航的算法也是用C++写的,然后界面部分要求在C#下写,所以不管是Socket通信,还是调用OpenCV的DLL模块,都设计到了C#和C++数据类型的对应,还有结构体的封装使用.在夸语言调用方面,Java和C#都只能调用C格式导出的动态库,因为C数据类型比较单一,容易映射,两者都是在本地端提供一套与之映射的C#或者Java的描述接口,通过底层处理这种映射关系达到调用的目的. 5月19日学习内容: http://www.cnblogs.co