存在的问题:

问题1:C++ 与 C# 同样定义的结构体在内存布局上有时并不一致;

问题2:C# 中引入了垃圾自动回收机制,其垃圾回收器可能会重新定位指针所指向的结构体变量。

解决方案:

问题1方案:强制指定 C++、C# 结构体的内存布局,使其一致(两者都固定为:结构体的成员按其声明时出现的顺序依次布局,结构体成员的内存对齐为1字节对齐);

为题2方案:C# 调用时将待传递的结构体转化为字节数组,并使用 fixed 语句将该字节数组固定住。

示例代码如下:

1、C++结构体定义:

  #pragma pack(1)
  struct Person
  {
      #define Count_FavoriteNumbers 6
  
      int id;
  
      float favoriteNumbers[Count_FavoriteNumbers];
  
  };
  #pragma pack()        // #pragma pack(1) end

  C++ 导出函数:

  #define DllExport extern "C" __declspec(dllexport)

  DllExport void __stdcall InitPersonInfo_DLL(Person* p_Person)
  {
      p_Person->id = 0;
  
      for (int i = 1; i <= Count_FavoriteNumbers; i++)
      {
          p_Person->favoriteNumbers[i - 1] = 1.11 * i;
      }    
  }

2、C# 结构体定义:

  [StructLayout(LayoutKind.Sequential, Pack = 1)]
      public class Person
      {
          public const int Count_FavoriteNumbers = 6;

  public int id;

  [MarshalAs(UnmanagedType.ByValArray, SizeConst = Count_FavoriteNumbers, ArraySubType = UnmanagedType.U4)]
          public float[] favoriteNumbers = new float[Count_FavoriteNumbers];  
      }

  C# 调用(WPF窗体程序):

  public partial class MainWindow : Window
  {

    [DllImport("MFCLibrary_ExportFunction.dll", EntryPoint = "InitPersonInfo_DLL")]
          static extern unsafe void InitPersonInfo_DLL(byte* p_Person);
          public unsafe void InitPersonInfo(ref Person person)
          {
              byte[] structBytes = StructToBytes(person);
              fixed (byte* p_Person = &structBytes[0])
              {
                  InitPersonInfo_DLL(p_Person);

  person = (Person)BytesToStruct(structBytes, person.GetType());
              }

    public MainWindow()
          {
      InitializeComponent();

      Person zhangSan = new Person();
      InitPersonInfo(ref zhangSan);
          }

    #region // 结构体与 byte[] 互转

    // Struct 转换为 byte[]
          public static byte[] StructToBytes(object structure)
          {
              int size = Marshal.SizeOf(structure);
              IntPtr buffer = Marshal.AllocHGlobal(size);

  try
              {
                  Marshal.StructureToPtr(structure, buffer, false);
                  byte[] bytes = new byte[size];
                  Marshal.Copy(buffer, bytes, 0, size);

  return bytes;
              }
              finally
              {
                  Marshal.FreeHGlobal(buffer);
              }
          }

  // byte[] 转换为 Struct
          public static object BytesToStruct(byte[] bytes, Type strcutType)
          {
              int size = Marshal.SizeOf(strcutType);
              IntPtr buffer = Marshal.AllocHGlobal(size);

  try
              {
                  Marshal.Copy(bytes, 0, buffer, size);

  return Marshal.PtrToStructure(buffer, strcutType);
              }
              finally
              {
                  Marshal.FreeHGlobal(buffer);
              }
          }
          #endregion

  }

DLL 函数中使用结构体指针作函数参数(C# 调用 C++ 的 DLL)的更多相关文章

  1. [编程] C语言结构体指针作为函数参数

    结构体指针作为函数参数:结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合,也就是所有成员,而不是像数组一样被编译器转换成一个指针.如果结构体成员较多,尤其是成员为数组时,传送的时间和空间 ...

  2. C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用

    类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...

  3. c语言中的结构体指针类型的cast

    1.我们在c语言中会经常碰到强制类型转换. 在这,我介绍一种结构pointer类型转换,但是有前提(有点类似于c++中的继承中的子父对象的cast). 简单的介绍一下: 首先我们要知道一个结构的指针, ...

  4. C语言中全局结构体指针隐含的错误

    前天在嵌入式系统上,调试一个数组的全局变量时,发现该变量一直会动态变化.深入分析, 才发现该全局结构体没有申请内存,而是用了一个指针.这种情况编译器是检查不出来的,在linux 上运行会挂掉,但是在裸 ...

  5. c语言中结构体指针

    1.指向结构体的指针变量: C 语言中->是一个总体,它是用于指向结构体,如果我们在程序中定义了一个结构体,然后声明一个指针变量指向这个结构体.那么我们要用指针取出结构体中的数据.就要用到指向运 ...

  6. C语言结构体指针与结构体变量作形参的区别

    区别 结构体变量 结构体变量作为形参,传递的是结构体变量本身,是一种值传递 形参结构体变量成员值的改变不影响对应的实参构体变量成员值的改变 结构体指针 结构体指针作为函数参数,传递的是指向结构体变量的 ...

  7. C/C++语言结构体指针的使用

    C/C++语言结构体指针的使用 主要内容 结构体的使用 - 定义,赋值,结构体指针 结构体作为函数参数的使用 指针的使用 代码内容重点 结构体的使用 - 定义,赋值,结构体指针 结构体作为函数参数的使 ...

  8. Leetcode 2. Add Two Numbers(指针和new的使用)结构体指针

    ---恢复内容开始--- You are given two non-empty linked lists representing two non-negative integers. The di ...

  9. ctypes 操作 python 与 c++ dll 互传结构体指针

    CMakeLists.txt # project(工程名) project(blog-3123958139-1) # add_library(链接库名称 SHARED 链接库代码) add_libra ...

随机推荐

  1. 在经过身份验证的服务中不支持跨域 javascript 回调

    在 asp.net web forms 站点中做了一个 wcf restful service 接口,开启了webforms 身份认证. 当 webforms 站点用户登录之后,访问 restful ...

  2. Eclipse 中快捷键

    Ctrl + Shift + T   查看原生类定义

  3. pymongo中的连接操作:Connection()与MongoClient()

    class MongoClient(pymongo.common.BaseObject) Connection to MongoDB. Method resolution order: MongoCl ...

  4. zabbix监控到异常后自动执行对应命令

    zabbix可不仅仅只有监控功能,还支持远程执行命令,实现简单自动化运维 以下以监控mysql的3306端口为例,如该端口异常关闭,自动执行命令重启mysql 创建一个监听3306的监控项,键值按照这 ...

  5. db2 varchar字段类型太大问题

    [DB2]SQL1585N 由于没有具有兼容页面大小的可用系统临时表空间,因此无法创建临时表.SQLSTATE=54048 自己写了一段SQL,SQL中包含ORDER BY 字句,但是在执行的时候报错 ...

  6. 在linxu机器ansible上运行启动django项目命令

    source py3env/bin/activate  进入虚拟环境 cd /xiangmulujing     进入项目路径 然后就可以执行运行命令了 python manage.py runser ...

  7. Java技术之如何保证同一资源被多个线程并发访问时的完整性?

    常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问.Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持. 在Java中一共有四种方法支持同步,其中前三个是同步 ...

  8. nginx ngx_http_image_filter_module 简单试用

    nginx包含了一个ngx_http_image_filter_module 模块,我们可以方便的进行图片的缩略图,平时一些简单的功能 已经够用了 环境准备 为了简单使用docker-compose ...

  9. Django 00-socket、wsgi及初始django学习心得

    HTTP基本原理1.http简述:http协议永远都是客户端发起请求,服务端回送请求.客户端和服务端本质上是一个socket客户端和服务端,http协议可以说是基于socket的再上层封装2.http ...

  10. 漫画赏析:Linux 内核到底长啥样(转)

    知乎链接:https://zhuanlan.zhihu.com/p/51679405 来自 http://TurnOff.us 的漫画 “InSide The Linux Kernel” 本文转载自: ...