p/invoke碎片--对类的封送处理
主要是看默认封送处理行为
按类成员的类型是否为“可直接传递到非托管内存”的类型来分类;按照成员中是否有“可直接传递到非托管内存”的类型来讨论。
所有成员都是“可直接传递到非托管内存”的类型
托管代码和非托管代码:
//托管代码 ClassStruct cs = new ClassStruct(); cs.a = 90; cs.d = 23; DoClassStruct(cs); Console.Read(); } [StructLayout(LayoutKind.Sequential)] class ClassStruct { public int a; public double d; } 、、、、、、、、、、、////////////非托管代码 typedef struct
{
int a;
double d;
}CLASSSTRUCT;
extern "C" __declspec(dllexport) void DoClassStruct(CLASSSTRUCT *cs)
{
printf("%d\n",cs->a);
printf("%lf\n",cs->d);
cs->a=100;
}
下断点,在DoClassStruct(cs);处,发现cs的地址是0x01bcbaa0;进入 非托管函数中,下断点,发现cs的地址不变。如下:
0x01BCBAA0 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 37 40 Z.............7@ 其中的5a是指90,后面8位是指double值23.0 。
结论:如果类的字段都是“可直接传递到非托管代码中”的类型,那么传递是引用,或者说是指针。这是在非托管代码中的修改也会反映到托管代码中了。这个过程就是锁定。
类的成员不包含“非可直接传递到非托管代码中”的类型
看下面一个例子,满足条件的类型有bool和string类型等。
cs.a = 90; cs.d = 23; cs.str = "abcd"; DoClassStruct(cs); Console.WriteLine(cs.a); Console.Read(); } [StructLayout(LayoutKind.Sequential)] class ClassStruct { public int a; public double d; public string str; } /////////////////////////////非托管代码 typedef struct { int a; double d; char *pStr; }CLASSSTRUCT; extern "C" __declspec(dllexport) void DoClassStruct(CLASSSTRUCT *cs) { printf("%d\n",cs->a); printf("%lf\n",cs->d); cs->a=100; }
通过跟踪发现,依然使用普通结构体的过程:在非托管内存中开辟空间---把托管内存中数据复制一份到非托管内存M-----内存M的地址作为参数传递给函数---一切的操作都是围着内存M进行。 可想而知,最后的结果不会反映到托管内存中去。