C#将结构体和指针互转的方法

1. 功能及位置
    将数据从托管对象封送到非托管内存块,属于.NET Framework 类库
    命名空间:System.Runtime.InteropServices
    程序集:mscorlib(在 mscorlib.dll 中)

2. 语法
    C#:
        [ComVisibleAttribute(true)] public static void StructureToPtr (Object structure,IntPtr ptr,bool fDeleteOld);
    C++:
        [ComVisibleAttribute(true)]public: static void StructureToPtr (Object^ structure, IntPtr ptr, bool fDeleteOld);

3. 参数说明
    structure:托管对象,包含要封送的数据。该对象必须是格式化类的实例。
    ptr:指向非托管内存块的指针,必须在调用此方法之前分配该指针。
    fDeleteOld:设置为 true 可在执行Marshal.DestroyStructure方法前对 ptr 参数调用此方法。请注意,传递 false 可导致内存泄漏。

4. 异常
    异常类型:ArgumentException
    条件:structrue参数是泛型类型

5. 备注
    StructureToPtr将结构的内容复制到 ptr 参数指向的预分配内存块。如果 fDeleteOld 参数为 true,则使用嵌入指
    针上适当的删除 API 来删除最初由 ptr 指向的缓冲区,但该缓冲区必须包含有效数据。此方法为在镜像托管类中指
    定的每个引用字段执行清理工作。

    假设 ptr 指向非托管内存块。此内存块的布局由相应的托管类 structure 描述。StructureToPtr将字段值从结构封
    送到指针。假设 ptr 块包含引用字段,该字段指向当前包含“abc”的字符串缓冲区。假设托管端上相应的字段是包含“vwxyz”的字符串。如果不另行通知它,StructureToPtr将分配一个新的非托管缓冲区来保存“vwxyz”,并将它挂钩到 ptr 块。这将丢弃旧缓冲区“abc”使之漂移而不将其释放回非托管堆。最后,您将得到一个孤立的缓冲区,它表示在代码中存在内存泄漏。如果将 fDeleteOld 参数设置为真,则 StructureToPtr 在继续为“vwxyz”分配新缓冲区之前释放保存“abc”的缓冲区。

6. 举例
    定义PERSON结构,并将该结构的一个变量拷贝到非托管内存,再将该内存中的PERSON还原为PERSON对象,观察其内容的变化。  

源代码如下:

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace testStructureToPtr
{
    public static class define  //define some constant
    {
        public const int MAX_LENGTH_OF_IDENTICARDID = 20;   //maximum length of identicardid
        public const int MAX_LENGTH_OF_NAME = 50;           //maximum length of name
        public const int MAX_LENGTH_OF_COUNTRY = 50;        //maximum length of country
        public const int MAX_LENGTH_OF_NATION = 50;         //maximum length of nation
        public const int MAX_LENGTH_OF_BIRTHDAY = 8;        //maximum length of birthday
        public const int MAX_LENGTH_OF_ADDRESS = 200;       //maximum length of address
    }

    public struct PERSON    //person structure
    {
        //MarshalAs:指示如何在托管代码和非托管代码之间封送数据
        //UnmanagedType:指定如何将参数或字段封送到非托管内存块
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_IDENTICARDID)]
        public byte[] identicardid;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NAME)]
        public byte[] name;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_COUNTRY)]
        public byte[] country;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NATION)]
        public byte[] nation;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_BIRTHDAY)]
        public byte[] birthday;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_ADDRESS)]
        public byte[] address;
    }

    class testProgram
    {
        private static byte _fillChar = 0;      //the fill character

        //convert string to byte array in Ascii with length is len
        public static byte[] CodeBytes(string str, int len)
        {
            if (string.IsNullOrEmpty(str))
            {
                str = string.Empty;
            }

            byte[] result = new byte[len];
            byte[] strBytes = Encoding.Default.GetBytes(str);

            //copy the array converted into result, and fill the remaining bytes with 0
            for (int i = 0; i < len; i++)
                result[i] = ((i < strBytes.Length) ? strBytes[i] : _fillChar);

            return result;
        }

        //show the person information
        public static void ShowPerson(PERSON person)
        {
            Console.WriteLine("cardid   :" + Encoding.ASCII.GetString(person.identicardid));
            Console.WriteLine("name     :" + Encoding.ASCII.GetString(person.name));
            Console.WriteLine("country  :" + Encoding.ASCII.GetString(person.country));
            Console.WriteLine("nation   :" + Encoding.ASCII.GetString(person.nation));
            Console.WriteLine("birthday :" + Encoding.ASCII.GetString(person.birthday));
            Console.WriteLine("address  :" + Encoding.ASCII.GetString(person.address));
        }

        static void Main(string[] args)
        {
            PERSON person;
            person.identicardid = CodeBytes("123456198001011111", define.MAX_LENGTH_OF_IDENTICARDID);
            person.name = CodeBytes("jackson", define.MAX_LENGTH_OF_NAME);
            person.country = CodeBytes("China", define.MAX_LENGTH_OF_COUNTRY);
            person.nation = CodeBytes("HanZu", define.MAX_LENGTH_OF_NATION);
            person.birthday = CodeBytes("19800101", define.MAX_LENGTH_OF_BIRTHDAY);
            person.address = CodeBytes("Luoshan Road, Shanghai", define.MAX_LENGTH_OF_ADDRESS);

            int nSizeOfPerson = Marshal.SizeOf(person);
            IntPtr intPtr = Marshal.AllocHGlobal(nSizeOfPerson);

            Console.WriteLine("The person infomation is as follows:");
            ShowPerson(person);

            try
            {
                //将数据从托管对象封送到非托管内存块,该内存块开始地址为intPtr
                Marshal.StructureToPtr(person, intPtr, true);

                //将数据从非托管内存块封送到新分配的指定类型的托管对象anotherPerson
                PERSON anotherPerson = (PERSON)Marshal.PtrToStructure(intPtr, typeof(PERSON));

                Console.WriteLine("The person after copied is as follows:");
                ShowPerson(anotherPerson);
            }
            catch (ArgumentException)
            {
                throw;
            }
            finally
            {
                Marshal.FreeHGlobal(intPtr);    //free tha memory
            }
        }
    }
}

7. 分析及扩展
    结构体:
    public struct PERSON    //person structure
    {
        //MarshalAs:指示如何在托管代码和非托管代码之间封送数据
        //UnmanagedType:指定如何将参数或字段封送到非托管内存块
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_IDENTICARDID)]
        public byte[] identicardid;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NAME)]
        public byte[] name;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_COUNTRY)]
        public byte[] country;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NATION)]
        public byte[] nation;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_BIRTHDAY)]
        public byte[] birthday;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_ADDRESS)]
        public byte[] address;
    }

    A. 通过获取Person信息的函数返回的指针,再将该指针转换为结构体,并显示结构体中的内容
        a. 获取Person信息的函数
            //获取 Person信息
            [DllImport("person.dll", CallingConvention = CallingConvention.Cdecl)]
                public static extern IntPtr GetPerson(IntPtr pEngine);

        b. 使用指针定义一个变量,来获取一个返回值为该指针的函数
            IntPtr Person = GetPerson(pEngine);

        c. 将指针变量装换为结构体,操作如下:
           PERSON anotherPerson = (PERSON)Marshal.PtrToStructure(intPtr, typeof(PERSON)); 

        d. 通过ShowPerson(PERSON person)函数,用来打印输出信息
           Console.WriteLine("cardid   :" + Encoding.ASCII.GetString(anotherPerson.identicardid));

    B. 通过定义结构体,并赋值结构体中的数据,再将结构体转为指针数据,并将指针传入到SetPerson信息的函数中
        a. 设置Person信息的函数
            //获取 Person信息
            [DllImport("person.dll", CallingConvention = CallingConvention.Cdecl)]
                public static extern int SetPerson(IntPtr pEngine);

        b. 定义结构体,并赋值结构体中的数据
            PERSON person = new PERSON();
            person.identicardid = CodeBytes("123456198001011111", define.MAX_LENGTH_OF_IDENTICARDID);
            person.name = CodeBytes("jackson", define.MAX_LENGTH_OF_NAME);
            person.country = CodeBytes("China", define.MAX_LENGTH_OF_COUNTRY);
            person.nation = CodeBytes("HanZu", define.MAX_LENGTH_OF_NATION);
            person.birthday = CodeBytes("19800101", define.MAX_LENGTH_OF_BIRTHDAY);
            person.address = CodeBytes("Luoshan Road, Shanghai", define.MAX_LENGTH_OF_ADDRESS);

        c. 将结构体转为指针数据

            int nSizeOfPerson = Marshal.SizeOf(person);                 //定义指针长度
            IntPtr personX  = Marshal.AllocHGlobal(nSizeOfPerson);        //定义指针
            Marshal.StructureToPtr(person, personX, true);                //将结构体person转为personX指针

        d. 将指针传入到SetPerson函数中
            SetPerson(personX);    

原文地址:https://www.cnblogs.com/hbtmwangjin/p/9060482.html

时间: 2024-12-25 15:05:10

C#将结构体和指针互转的方法的相关文章

黑马程序员 C语言-枚举,结构体,指针

一.枚举 ※枚举变量的定义 前面只是定义了枚举类型,接下来就可以利用定义好的枚举类型定义变量. 跟结构体一样,有3种方式定义枚举变量 1.先定义枚举类型,再定义枚举变量 enum Season {spring, summer, autumn, winter}; enum Season s; 2.定义枚举类型的同时定义枚举变量 enum Season {spring, summer, autumn, winter} s; 3.省略枚举名称,直接定义枚举变量 enum {spring, summer

深入了解Windows句柄到底是什么(句柄是逻辑指针,或者是指向结构体的指针,图文并茂,非常清楚)good

总是有新入门的Windows程序员问我Windows的句柄到底是什么,我说你把它看做一种类似指针的标识就行了,但是显然这一答案不能让他们满意,然后我说去问问度娘吧,他们说不行网上的说法太多还难以理解.今天比较闲,我上网查了查,光是百度百科词条“句柄”中就有好几种说法,很多叙述还是错误的,天知道这些误人子弟的人是想干什么. 这里我列举词条中的关于句柄的叙述不当之处,至于如何不当先不管,继续往下看就会明白: 1.windows 之所以要设立句柄,根本上源于内存管理机制的问题—虚拟地址,简而言之数据的

指针 指针与数组 指针与字符串 指针与函数 结构体与指针 宏

指针 指针与数组 指针与字符串 指针与函数?? 指针与数组 1.数组名:数组元素首地址 eg: int array[3]={1,3,6}; 这里array 恒等于&array[0] 2.int *p = a; int *p = 0; int a[]={0}; 3.int *p = a; 均指向数组的首地址 *p是? *(p+1)是?(*p)+1是? *(p+1)决定向下移动几个字节是类型 4.指针可以当数组名使用 p[1] = 3; 恒等于a[1] ;恒等于*(p+1);恒等于*(a+1) 5.

指向结构体的指针

运用指向结构体数组的指针输出学生信息 说明:指针指向结构体数组,就得到了该结构体数组的起始地址.通过该地址可以访问结构体数组中的所有成员变量.其中,指向结构体的指针的算术运算与 指向数组的指针的用法相似. #include<stdio.h> #define N 10 //结构体类型及变量的定义,初始化 struct student { char *number; char *name; char sex; int age; float score; }stu[3]={{"06001&

代码案例(结构体,函数指针,指针函数,冒泡排序) 修改

#import <Foundation/Foundation.h> typedef struct {     char name[20];     int age;     float score; }Stu; //建立字符串和函数之间的一一对应关系. typedef BOOL (*PStu) (Stu stu1,Stu stu2) ; typedef struct nameFunctionPair{     char name[20]; //存储函数对应的字符串     PStu funct

【C语言】用结构体数组指针完成:有三个学生信息,存放在结构体数组中,要求输出全部信息

//用结构体数组指针完成:有三个学生信息,存放在结构体数组中,要求输出全部信息 #include <stdio.h> struct Stu { int num; char name[20]; char sex; int age; }; int main() { struct Stu student[3]={{317,"han",'m',20},{318,"hun",'w',22},{311,"dan",'w',18}}; struct

EDKII CR宏:根据成员指针获取父结构体变量指针

核心提示: 1. CR宏 (Containing Record):根据成员指针获取父结构体变量指针 2. 0 指针的妙用. 在EDKII 的代码中有不少关于CR宏的使用,如 时钟中断处理函数CoreTimerTick. VOID EFIAPI CoreTimerTick ( IN UINT64 Duration ) { IEVENT *Event; ... if (!IsListEmpty (&mEfiTimerList)) { Event = CR (mEfiTimerList.Forward

C Language Study - 结构体成员指针初始化

结构体成员指针初始化 不可以正确运行的初始化方式(1): #include <stdio.h> #include <string.h> #include <malloc.h> //#include "a.h" //char a[100]; struct stu { char* name; int num; }*pst,st; void init_pst() { pst = (struct stu *)malloc(sizeof(struct stu)

代码案例(结构体,函数指针,指针函数,冒泡排序)

typedef struct {     char name[20];     int age;     float score; }Stu; #import <Foundation/Foundation.h> //姓名升序 void sortByName(Stu *p , int count ) {     for (int i = 0 ; i < count -1; i ++) {         for (int j= 0 ; j < count -1-i; j ++) {