代码改变世界

.net 互操作之p/invoke- 数据封送(结构体中的字符串,其他字段,内存布局)(4)

2010-08-26 23:49  Clingingboy  阅读(1041)  评论(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;
     //用MarshalAsUnmanagedType来指定 
     [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);
        }