.net 互操作之p/invoke- 数据封送(结构体中的字符串,其他字段,内存布局)(4)
2010-08-26 23:49 Clingingboy 阅读(1044) 评论(0) 编辑 收藏 举报
一.字符串封送
与传值没多大区别,内存释放也分两种,自动和手动,不再介绍
1.托管结构与函数
//typedef struct _MSEMPLOYEE //{ // UINT employeeID; // short employedYear; // char* displayName; // char* alias; //} MSEMPLOYEE, *PMSEMPLOYEE; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] private struct MsEmployee { public uint EmployeeID; public short EmployedYear; public string DisplayName; public string Alias; } [StructLayout(LayoutKind.Sequential)] private struct MsEmployee_IntPtrString { public uint EmployeeID; public short EmployedYear; public IntPtr DisplayName; public IntPtr Alias; }
// void __cdecl GetEmployeeInfo(PMSEMPLOYEE pEmployee) [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static void GetEmployeeInfo(ref MsEmployee employee); [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "GetEmployeeInfo")] private extern static void GetEmployeeInfo_IntPtrString(ref MsEmployee_IntPtrString employee);
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"); } }
3.测试
private static void TestAllocString() { MsEmployee employee = new MsEmployee(); employee.EmployeeID = 10001; GetEmployeeInfo(ref 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); } private static void TestAllocString_IntPtrString() { MsEmployee_IntPtrString employee = new MsEmployee_IntPtrString(); employee.EmployeeID = 10001; GetEmployeeInfo_IntPtrString(ref employee); string displayName = Marshal.PtrToStringAnsi(employee.DisplayName); string alias = Marshal.PtrToStringAnsi(employee.Alias); Marshal.FreeCoTaskMem(employee.DisplayName); Marshal.FreeCoTaskMem(employee.Alias); Console.WriteLine("\n员?工?信?息?:"); Console.WriteLine("ID: {0}", employee.EmployeeID); Console.WriteLine("工?龄?:{0}", employee.EmployedYear); Console.WriteLine("Alias: {0}", alias); Console.WriteLine("姓?名?: {0}", displayName); }
4.封送字符串数组
//typedef struct _MSEMPLOYEE2 //{ // UINT employeeID; // short employedYear; // char displayName[255]; // char alias[255]; //} MSEMPLOYEE2, *PMSEMPLOYEE2; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] private struct MsEmployee2 { public uint EmployeeID; public short EmployedYear; //用MarshalAs 的UnmanagedType来指定 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)] public string DisplayName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)] public string Alias; }
定义函数
// void __cdecl GetEmployeeInfo2(PMSEMPLOYEE2 pEmployee) [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static void GetEmployeeInfo2(ref MsEmployee2 employee);
void __cdecl GetEmployeeInfo2(PMSEMPLOYEE2 pEmployee) { if(NULL != pEmployee) { //
pEmployee->employedYear = 2; strcpy_s(pEmployee->alias, 255, "jizhou"); strcpy_s(pEmployee->displayName, 255, "Jizhou Huang"); } }
测试
private static void TestInlineString() { MsEmployee2 employee = new MsEmployee2(); employee.EmployeeID = 10002; GetEmployeeInfo2(ref 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); }
其他字段封送
以MarshalAs标签来封送
如下封送了c++的bool类型和Win32的BOOL类型
//typedef struct _MSEMPLOYEE_EX //{ // UINT employeeID; // wchar_t* displayName; // char* alias; // bool isInRedmond; // BOOL isFamale; //} MSEMPLOYEE_EX, *PMSEMPLOYEE_EX; [StructLayout(LayoutKind.Sequential)] private struct MsEmployeeEx { public uint EmployeeID; [MarshalAs(UnmanagedType.LPWStr)] public string DisplayName; [MarshalAs(UnmanagedType.LPStr)] public string Alias; [MarshalAs(UnmanagedType.I1)] public bool IsInRedmond; public short EmployedYear; [MarshalAs(UnmanagedType.Bool)] public bool IsFamale; }
至于内存释放也是相同两种处理方式
定义函数
void __cdecl GetEmployeeInfoEx(PMSEMPLOYEE_EX pEmployee) { ShowNativeStructSize(sizeof(MSEMPLOYEE_EX)); if(NULL != pEmployee) { pEmployee->alias = (char*)CoTaskMemAlloc(255); pEmployee->displayName = (wchar_t*)CoTaskMemAlloc(255 * 2); strcpy_s(pEmployee->alias, 255, "xcui"); wcscpy_s(pEmployee->displayName, 255, L"崔晓源"); pEmployee->isInRedmond = false; pEmployee->employedYear = 2; pEmployee->isFamale = false; } }
测试
private static void TestGetEmployeeEx() { ShowMarshalSize(typeof(MsEmployeeEx)); MsEmployeeEx employee = new MsEmployeeEx(); employee.EmployeeID = 10001; GetEmployeeInfoEx(ref employee); Console.WriteLine("员?工?信?息?:"); Console.WriteLine("ID: {0}", employee.EmployeeID); Console.WriteLine("Alias: {0}", employee.Alias); Console.WriteLine("姓?名?: {0}", employee.DisplayName); Console.WriteLine("性?别?: {0}", employee.IsFamale ? "女?" : "男?"); Console.WriteLine("工?龄?: {0}", employee.EmployedYear); Console.WriteLine("是?否?在?总?部?: {0}", employee.IsInRedmond ? "是?" : "否?"); }
控制内存布局
当非托管结构体太多的时候,托管结构体只需要其中几个的时候,可以控制内存布局
指定StructLayout的Pack值
//#pragma pack(1) //typedef struct _MSEMPLOYEE_EX2 //{ // UINT EmployeeID; //4 bytes -> Offset 0 // USHORT EmployedYear; //2 bytes -> Offset 4 // BYTE CurrentLevel; //1 bytes -> Offset 6 // wchar_t* Alias; //4 bytes -> Offset 7 // wchar_t* DisplayName; //4 bytes -> Offset 11 // wchar_t* OfficeAddress; //4 bytes -> Offset 15 // wchar_t* OfficePhone; //4 bytes -> Offset 19 // wchar_t* Title; //4 bytes -> Offset 23 // USHORT RegionId; //2 bytes -> Offset 27 // int ZipCode; //4 bytes -> Offset 29 // double CurrentSalary; //8 bytes -> Offset 33 //} MSEMPLOYEE_EX2, *PMSEMPLOYEE_EX2; //#pragma pack() [StructLayout(LayoutKind.Explicit, Pack = 1)] private struct MsEmployeeEx2 { [FieldOffset(0)] public uint EmployeeID; [FieldOffset(4)] public ushort EmployedYear; [FieldOffset(6)] public byte CurrentLevel; [FieldOffset(27)] public ushort RegionId; [FieldOffset(29)] public int ZipCode; [FieldOffset(33)] public double CurrentSalary; }
注意非托管控制传入的结构体字段需要在托管代码中定义,不然会造成内存泄露.
void __cdecl GetEmployeeInfoEx2(PMSEMPLOYEE_EX2 pEmployee) { _wsetlocale(LC_ALL, L"chs"); ShowNativeStructSize(sizeof(MSEMPLOYEE_EX2)); if(NULL != pEmployee) { pEmployee->EmployedYear = 2; pEmployee->CurrentLevel = 60; pEmployee->RegionId = 1100; wprintf(L"原?邮?政?编?码?:?%d\n", pEmployee->ZipCode); pEmployee->ZipCode = 100080; pEmployee->CurrentSalary = 200000; } }
测试
private static void TestStructExactLayout() { ShowMarshalSize(typeof(MsEmployeeEx2)); // 创?建?一?个?对?象?,?并?赋?以?初?始?值? MsEmployeeEx2 employee = new MsEmployeeEx2(); employee.EmployeeID = 10001; employee.EmployedYear = 1; employee.CurrentLevel = 59; employee.RegionId = 1000; employee.ZipCode = 16; employee.CurrentSalary = 123456; GetEmployeeInfoEx2(ref employee); Console.WriteLine("员?工?信?息?:"); Console.WriteLine("ID: {0}", employee.EmployeeID); Console.WriteLine("工?龄?: {0}", employee.EmployedYear); Console.WriteLine("职?级?: {0}", employee.CurrentLevel); Console.WriteLine("区?域?代?码?: {0}", employee.RegionId); Console.WriteLine("邮?政?编?码?: {0}", employee.ZipCode); Console.WriteLine("工?资?: {0}", employee.CurrentSalary); }