.net 互操作之p/invoke- 数据封送(嵌套结构体,类,数组)(5)
2010-08-26 23:50 Clingingboy 阅读(2380) 评论(0) 编辑 收藏 举报嵌套结构体
复杂的数据类型往往结构体中还有结构体,即嵌套结构体,也可以说是复杂类型.如下定义
//typedef struct _PERSONNAME //{ // char* first; // char* last; // char* displayName; //} PERSONNAME, *PPERSONNAME; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct PersonName { public string first; public string last; public string displayName; } //typedef struct _PERSON //{ // PPERSONNAME pName; // int age; //} PERSON, *PPERSON; [StructLayout(LayoutKind.Sequential)] public struct Person { public IntPtr name; public int age; }
Person中的name属性被定义成IntPtr,
测试代码
1.使用IntPtr 定义结构体
Console.WriteLine("\n结构体作为引用类型成员"); // 创?建?名?字? PersonName name = new PersonName(); name.last = "Cui"; name.first = "Xiaoyuan"; // 创?建?人? Person person = new Person(); person.age = 27; IntPtr nameBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(name)); Marshal.StructureToPtr(name, nameBuffer, false); person.name = nameBuffer; Console.WriteLine("调?用?前?显?示?姓?名?为?:?{0}", name.displayName); TestStructInStructByRef(ref person); PersonName newValue = (PersonName)Marshal.PtrToStructure(person.name, typeof(PersonName)); // 释?放?在?非?托?管?代?码?中?分?配?的?PersonName实?例?内?存? Marshal.DestroyStructure(nameBuffer, typeof(PersonName)); Console.WriteLine("调?用?后?显?示?姓?名?为?:?{0}", newValue.displayName);
分析如下代码片段
//Marshal.SizeOf(name)计算此对象非托管内存所需大小,用AllocCoTaskMem申请内存
IntPtr nameBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(name));
//将对象封送到非托管内存中 Marshal.StructureToPtr(name, nameBuffer, false);
//将非托管对象封送到托管对象中
PersonName newValue = (PersonName)Marshal.PtrToStructure(person.name, typeof(PersonName)); // 释放在?非托管代码中分配的PersonName实例内存 Marshal.DestroyStructure(nameBuffer, typeof(PersonName));
2.使用结构体实例
这样内存即自动管理
Console.WriteLine("\n结?构?体?作?为?值?类?型?成?员?"); Person2 person = new Person2(); person.name.last = "Huang"; person.name.first = "Jizhou"; person.name.displayName = string.Empty; person.age = 26; TestStructInStructByVal(ref person);
3.对象单一化
若非托管对象字段是嵌套结构体,托管代码可以声明为单一对象
[StructLayout(LayoutKind.Sequential)] public struct Person2_Flattened { public string first; public string last; public string displayName; public int age; }
3.1测试
Console.WriteLine("\n结?构?体?作?为?值?类?型?成?员?(?flattened)?"); Person2_Flattened person = new Person2_Flattened(); person.last = "Huang"; person.first = "Jizhou"; person.displayName = string.Empty; person.age = 26; TestStructInStructByVal(ref person);
类引用传递
1.NonBlittlable引用类型
因为其为引用类型,所以无须使用ref修饰符
也是以StructLayout标记
//typedef struct _MSEMPLOYEE //{ // UINT employeeID; // short employedYear; // char* displayName; // char* alias; //} MSEMPLOYEE, *PMSEMPLOYEE; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] private class MsEmployee { private uint _employeeID; private short _employedYear; private string _displayName; private string _alias; #region Properties public uint EmployeeID { get { return _employeeID; } set { _employeeID = value; } } public short EmployedYear { get { return _employedYear; } set { _employedYear = value; } } public string DisplayName { get { return _displayName; } set { _displayName = value; } } public string Alias { get { return _alias; } set { _alias = value; } } #endregion }
2.非托管代码
注意引用类型需要申请内存
void __cdecl GetEmployeeInfo(PMSEMPLOYEE pEmployee) { if(NULL != pEmployee) { // 我?们?应?该?根?据?pEmployee->employeeID查?找?写?信?息?回?来? pEmployee->employedYear = 2; pEmployee->alias = (char*)CoTaskMemAlloc(255); pEmployee->displayName = (char*)CoTaskMemAlloc(255); strcpy_s(pEmployee->alias, 255, "xcui"); strcpy_s(pEmployee->displayName, 255, "Xiaoyuan Cui"); } }
因为是引用类型,所以注意这里指定了In,Out
[DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static void GetEmployeeInfo([In, Out] MsEmployee employee);
测试代码
MsEmployee employee = new MsEmployee(); employee.EmployeeID = 10001; GetEmployeeInfo(employee); Console.WriteLine("\n员?工?信?息?:"); Console.WriteLine("ID: {0}", employee.EmployeeID); Console.WriteLine("工?龄?:{0}", employee.EmployedYear); Console.WriteLine("Alias: {0}", employee.Alias); Console.WriteLine("姓?名?: {0}", employee.DisplayName);
3.Blittable引用类型
类内部全为值类型
//typedef struct _SIMPLESTRUCT //{ // int intValue; // short shortValue; // float floatValue; // double doubleValue; //} SIMPLESTRUCT, *PSIMPLESTRUCT; [StructLayout(LayoutKind.Sequential)] private class ManagedClassBlittable { private int _intValue; private short _shortValue; private float _floatValue; private double _doubleValue; #region Properties public int IntValue { get { return _intValue; } set { _intValue = value; } } public short ShortValue { get { return _shortValue; } set { _shortValue = value; } } public float FloatValue { get { return _floatValue; } set { _floatValue = value; } } public double DoubleValue { get { return _doubleValue; } set { _doubleValue = value; } } #endregion }
[DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)] private extern static void TestStructArgumentByRef(ManagedClassBlittable argClass);
非托管代码可直接修改,定义的方法也无需指定ref修饰符
void __cdecl TestStructArgumentByRef(PSIMPLESTRUCT pStruct) { ShowNativeStructSize(sizeof(SIMPLESTRUCT)); if( NULL != pStruct) { // 打?印?初?始?数?据? wprintf(L"\n结?构?体?原?数?据?: int = %d, short = %d, float = %f, double = %f\n", pStruct->intValue, pStruct->shortValue, pStruct->floatValue, pStruct->doubleValue); // 修?改?数?据? pStruct->intValue++; pStruct->shortValue++; pStruct->floatValue += 1; pStruct->doubleValue += 1; } }
测试
ManagedClassBlittable blittableObject = new ManagedClassBlittable(); blittableObject.IntValue = 1; blittableObject.ShortValue = 2; blittableObject.FloatValue = 3; blittableObject.DoubleValue = 4.5; TestStructArgumentByRef(blittableObject); Console.WriteLine("\n结?构?体?新?数?据?:?int = {0}, short = {1}, float = {2:f6}, double = {3:f6}", blittableObject.IntValue, blittableObject.ShortValue, blittableObject.FloatValue, blittableObject.DoubleValue);
4.封送为指针
非托管代码
void __cdecl TestReturnStructFromArg(PSIMPLESTRUCT* ppStruct) { if( NULL != ppStruct) { *ppStruct = (PSIMPLESTRUCT)CoTaskMemAlloc( sizeof(SIMPLESTRUCT)); (*ppStruct)->intValue = 5; (*ppStruct)->shortValue = 4; (*ppStruct)->floatValue = 3.0; (*ppStruct)->doubleValue = 2.1; } return; }
注意这里参数用out修饰符,表示安引用传递参数(ref修饰符对应 [In,Out]属性,out修饰符对应[Out]属性)
[DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)] private extern static void TestReturnStructFromArg(out ManagedClassBlittable outObject);
测试
ManagedClassBlittable outObject; TestReturnStructFromArg(out outObject); Console.WriteLine("\n结?构?体?新?数?据?:?int = {0}, short = {1}, float = {2:f6}, double = {3:f6}", outObject.IntValue, outObject.ShortValue, outObject.FloatValue, outObject.DoubleValue);
封送数组
1.当数组类型为blittable类型时,直接定义即可
定义函数
// int __cdecl TestArrayOfInt(int intArray[], int arraySize); [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)] private extern static int TestArrayOfInt(int[] intArray, int arraySize);
// 返回整数数组中,所有元素之和。并将每个元素的值加10
int __cdecl TestArrayOfInt(int intArray[], int arraySize) { int result = 0; for(int i = 0; i < arraySize; i++) { result += intArray[i]; intArray[i] += 10; } return result; }
测试
int[] intArray = new int[] { 1, 2, 3, 4, 5, 6, 7 }; Console.WriteLine("\n\n调?用?前?整?数?数?组?元?素?为?:?"); foreach (int i in intArray) { Console.Write("{0} ", i); } int sum = TestArrayOfInt(intArray, intArray.Length); Console.WriteLine("\n调?用?前?整?数?数?组?中?所?有?元?素?之?和?为?:?{0}", sum); Console.WriteLine("\n调?用?后?整?数?数?组?元?素?为?:?"); foreach (int i in intArray) { Console.Write("{0} ", i); } Console.WriteLine();
2.若数组类型为非blittable类型时,则需要显示添加[In,Out]属性
定义函数
// UINT __cdecl TestArrayOfChar(char charArray[], int arraySize); [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static uint TestArrayOfChar([In, Out] char[] charArray, int arraySize);
// 返回字符数组中,数字字符的总数
UINT __cdecl TestArrayOfChar(char charArray[], int arraySize) { int result = 0; for(int i = 0; i < arraySize; i++) { if (isdigit(charArray[i])) { result++; charArray[i] = '@'; } } return result; }
测试代码
char[] charArray = new char[] { 'a', '1', 'b', '2', 'c', '3', '4' }; Console.WriteLine("\n调?用?前?字?符?数?组?元?素?为?:?"); foreach (char c in charArray) { Console.Write("{0} ", c); } uint digitCount = TestArrayOfChar(charArray, charArray.Length); Console.WriteLine("\n调?用?前?字?符?数?组?中?数?字?个?数?为?:?{0}", digitCount); Console.WriteLine("\n调?用?后?字?符?数?组?元?素?为?:?"); foreach (char c in charArray) { Console.Write("{0} ", c); }
3.字符串数组(指针数组)
void __cdecl TestArrayOfString(char* ppStrArray[], int size) { for(int i = 0; i < size; i++) { // 翻?转?字?符?串? ReverseAnsiStringInPlace(ppStrArray[i]); } } void ReverseAnsiStringInPlace(char* rawString) { int strLength = (int)strlen(rawString); char tempChar; for(int i = 0; i < strLength/2; i++) { tempChar = rawString[i]; rawString[i] = rawString[strLength - 1 - i]; rawString[strLength - 1 - i] = tempChar; } }
// void __cdecl TestArrayOfString(char* ppStrArray[], int size) [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static void TestArrayOfString([In, Out] string[] charArray, int arraySize);
测试
private static void TestStringArray() { string[] strings = new string[] { "This is the first string.", "Those are brown horse.", "The quick brown fox jumps over a lazy dog." }; Console.WriteLine("\n原?始?字?符?串?数?组?中?的?元?素?为?:?"); foreach (string originalString in strings) { Console.WriteLine(originalString); } TestArrayOfString(strings, strings.Length); Console.WriteLine("修?改?后?字?符?串?数?组?中?的?元?素?为?:?"); foreach (string reversedString in strings) { Console.WriteLine(reversedString); } }
若将数组参数指定为IntPtr,则需要注意内存管理
// int __cdecl TestRefArrayOfString(void** strArray, int* size) [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] private extern static int TestRefArrayOfString(out IntPtr charArray, out int arraySize);
测试
IntPtr arrayPtr; int arraySize; int returnCount = TestRefArrayOfString(out arrayPtr, out arraySize); IntPtr[] arrayPtrs = new IntPtr[returnCount]; Marshal.Copy(arrayPtr, arrayPtrs, 0, returnCount); string[] strings = new string[returnCount]; for (int i = 0; i < returnCount; i++) { strings[i] = Marshal.PtrToStringUni(arrayPtrs[i]); Marshal.FreeCoTaskMem(arrayPtrs[i]); Console.WriteLine("#{0}: {1}", i, strings[i]); } Marshal.FreeCoTaskMem(arrayPtr);