C#调用C类型dll入参为struct的问题详解

前言

C# 可以通过 DllImport 的方式引用 C 类型的 dll。但很多 dll 的参数不会是简单的基础类型,而是结构体 struct 。因此就需要在 C# 端定义同样的结构体类型,才能实现调用 C 类型 dll。这里例举几种不同的结构体情况,以及其对应的解决方案。

基础调用方式

对于一个结构体类型:

typedef struct DATA
{
  int nNumber;
  float fDecimal;
};

在 C# 端就需要定义为

[StructLayout(LayoutKind.Sequential)]
public struct DATA
{
  public int nNumber;
  public float fDecimal;
}

包含字符数组

对于一个包含字符数组的结构体类型:

typedef struct DATA
{
  int nNumber;
  float fDecimal;
  char szString[256];
};

在 C# 端就需要使用 Marshal 设置数据空间大小,同时最好定义一个初始化函数与 get 的定义

[StructLayout(LayoutKind.Sequential)]
public struct DATA
{
  void alloc() {
    szString = new char[256];
  }

  string sString {
    get {
      int nLength = 256;
      string sData = "";
      for (int i = 0; i < nLength; i++)
      {
        if (szData[i] == '\0') break;
        sData += szData[i];
      }
      return sData;
    }
  }

  public int nNumber;
  public float fDecimal;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
  char[] szString;
}

包含字符二维数组

对于一个包含字符二维数组的结构体类型:

typedef struct DATA
{
  int nNumber;
  float fDecimal;
  char szString[6][256];
};

在 C# 端同样需要使用 Marshal 设置数据空间大小,需要将两个 Size 相乘,并定义一个初始化函数。同时在做一个 get 的定义。

[StructLayout(LayoutKind.Sequential)]
public struct DATA
{
  void alloc() {
    szString = new char[256 * 6];
  }

  public string[] sStrings
  {
    get {
      int nSize = 6, nLength = 256;
      string[] sDatas = new string[nSize];
      for (int i = 0; i < nSize; i++)
      {
        for (int j = 0; j < nLength; j++)
        {
          if (szData[i * nLength + j] == '\0') break;
          sData += szData[i * nLength + i];
        }
        sDatas[i] = sData;
      }
      return sDatas;
    }
  }

  public int nNumber;
  public float fDecimal;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256 * 6)]
  char[] szStrings;
}

dll 入参为结构体数组

若有一个这样的 C dll 函数定义:

void FnCall(DATA* datas);

// 调用方式
DATA datas[10];
fnCall(datas);

那么,在 C# 中要实现等价调用:

// 首先 Import 函数
[DllImport("Module.dll")]
public static extern void FnCall(IntPtr pInfo); // 注意入参要定义为指针

// 再定义定义结构体数组
int nCount = 10;
DATA datas = new DATA[nCount];

// 再分配内存空间
int nSize = Marshal.SizeOf(typeof(DEVICE_INFO));
IntPtr Dataptr = Marshal.AllocHGlobal(nSize * nCount);

// 调用函数
FnCall(Dataptr);

// 复制数据到结构体中
for (int i = 0; i < nCount; i++)
{
  IntPtr ptr = (IntPtr)((UInt32)Dataptr + i * size);
  datas[i] = (DEVICE_INFO)Marshal.PtrToStructure(ptr, typeof(DEVICE_INFO));
}

// 释放内存空间
Marshal.FreeHGlobal(Dataptr);

另外,如果你要调用的 dll 是非 C 类型 dll,而是 C++ Class。那么我们就可以将其再包装一层,转换为 C 类型 dll。

例如:

class Example {
public:
 int MethodCall();
};

那么就可以编写 C 类型的 dll。

extern "C" {
  Example* Example_New() {
    return new Example();
  }
  int Example_MethodCall(Example* p) {
    return p->MethodCall();
  }
  void Example_Delete(Example* p) {
    delete p;
  }
}

C# 那边就这样导入

[DllImport("Module.dll")]
public static extern IntPtr Example_Create();

[DllImport("Module.dll")]
public static extern int Example_MethodCall(IntPtr value);

[DllImport("Module.dll")]
public static extern void Example_Delete(IntPtr value);

// 调用方式
IntPtr p = Example_Create();
Example_MethodCall(p);
Example_Delete(p);

至于 C 类型 dll 中其他类型变量在 C# 的对应,则可以参考 Microsoft 的 文档 。

总结

到此这篇关于C#调用C类型dll入参为struct问题的文章就介绍到这了,更多相关C#调用C类型dll入参内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • c#调用c语言dll需要注意的地方

    一.将C#工程和C的dll工程放在同一个解决方案下,这样就可以实现联动调试,直接从C#中进入C的dll函数里.注意:每次更改dll中的代码后都必须重新生成dll.另,C#与C中有几种变量类型不对应,注意声明时的区分. 语言 C# C 类型 long long long/__int64 byte/Byte unsigned char char wchar_t UInt32 size_t 二.dll工程中头文件加入以下代码: // 此代码为了方便头文件在dll工程和调用该dll的工程中重复利用 //

  • C#中托管DLL和非托管DLL的区别详解

    首先解释一下,托管DLL和非托管DLL的区别.狭义解释讲,托管DLL就在Dotnet环境生成的DLL文件.非托管DLL不是在Dotnet环境生成的DLL文件. 托管DLL文件,可以在Dotnet环境通过 "添加引用" 的方式,直接把托管DLL文件添加到项目中.然后通过 Using DLL命 名空间,来调用相应的DLL对象 .  非托管DLL文件,在Dotnet环境应用时,通过 DllImport 调用. C# 调用非托管DLL文件.DLL文件是用C语言编写的. 托管DLL就是能够在公共

  • C#调用C++dll方法步骤

    C#调用C++dll的方法和步骤 其他分享涉及到的概念和方法对于像我这样比较菜的选手看起来比较费劲并且很难抓住重点,这里我总结了一段时间的研究成果供初学者救济之用,简单明了. 1.新建项目->Visual C++->Win32项目 MyDLL 注意:C++编写的dll一般是不能直接拿来C#调用,需要先新建个C++的工程把dll里的方法重新封装成可被C#外部调用的函数. 2.MyDLL.cpp里的代码如下: extern "C" _declspec(dllexport)int

  • C#如何通过probing指定dll寻找文件夹详解

    前言 我们在很大的项目开发,会发现项目引用的 dll 会很多,我想要按照不同的功能,将不同的 dll 放在不同的文件夹 简单的方法是通过修改 App.config 文件指定文件夹,如将文件移动到 abc\12 的文件夹里面,可以在 App.config 添加代码 <?xml version="1.0" encoding="utf-8" ?> <configuration> <runtime> <assemblyBinding

  • Qt程序中调用C#编写的dll(推荐)

    1.打开Visual Studio,新建一个C#的Class Library项目(这里选择的是.Net Framework 4),项目名为CSharpDll. 2.由于默认没有引入Forms等UI库,先在reference中添加引用System.Windows.Forms以便可以在测试中使用MessageBox等. 3.最终C#编写的dll的源代码如下图所示,命名空间为CSharpDll,公共类为CSharpClass. using System; using System.Collection

  • C# 添加对System.Configuration.dll文件的引用操作

    却被编译器提示说: 警告 1 "System.Configuration.ConfigurationSettings.AppSettings" 已过时: "This method is obsolete, it has been replaced by System.Configuration!System.Configuration.ConfigurationManager.AppSettings" 于是转而想找到那个ConfigurationManager类来使

  • vs2019 实现C#调用c++的dll两种方法

    1.托管与非托管的区别 链接地址(仅供参考) 除了链接中的,在实用角度出发: 非托管需要一个个声明引用,就很繁琐 但是托管(虽然麻烦)不用声明,只需要调好配置即可,还是比较方便的 为什么写这个博客,也是因为不同版本vs2017和vs2019有所区别,托管就容易踩坑,希望大家能看看,解决问题(我也很菜,大佬轻喷) 2.非托管类的实现 第一步:创建C++空项目(命名Caculate)添加一个类AddOperate .h代码部分: #pragma once extern "C" _decls

  • C# 嵌入dll 的方法

    在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形式总归让人不爽,那么有办法让生成的软件中直接就包含这个dll文件吗,这样就可以不用dll跟着exe走了,避免单独不能运行的情况. 答案是有的! 在工程项目目录下找到Resources.resx文件并点击,然后按下面操作,添加资源,将你要加入的dll添加进来. 操作完成后,就会在下面的内容框里看到你添加进来的dll. 然后

  • VSCode调试C#程序及附缺失.dll文件的解决办法

    刚开始使用VScode,经过两天的折腾,初步解决了调试C#无法生成.dll文件的问题,现将我的过程叙述如下(使用到.Net Core+VScode+C#插件). 1.首先在D盘建立一个新的文件夹,我的文件名是C# file,如下: 2.然后打开VS code,通过file -> Add Folder to Workspace选项将刚才建立的新的文件夹加入到工作空间中,如下: 3.通过命令Ctrl+shift+y打开调试控制台(DEBUG CONSOLE),然后选择终端(TERMINAL),会看到

  • 关于C#调用C++dll传指针释放内存问题

    一.传入dll前,在C#中申请内存空间 c#里面的指针即 IntPtr 申请如下: IntPtr SrcImgData = Marshal.AllocHGlobal(length); 这种需要提前知道空间大小,否则无法确定空间大小,会导致dll内部处理时越界报错. c#里面申请空间了,那么c++里面一般就是在这些空间里面操作了,一般不会重新分配内存,那么就不需要加引用了. c++: uchar* SrcImg c#导入dll函数时申明: IntPtr SrcImg 那么内存释放自然也是由c#来进

  • C#使用反射(Reflect)获取dll文件中的类型并调用方法

    使用反射(Reflect)获取dll文件中的类型并调用方法,具体内容如下 需引用:System.Reflection; 1. 使用反射(Reflect)获取dll文件中的类型并调用方法(入门案例) static void Main(string[] args) { //dll文件路径 string path = @"D:\VS2015Project\001\Computer\bin\Debug\computer.dll"; //加载dll文件 Assembly asm = Assemb

  • C#调用易语言写的Dll文件方法

    本人是用易语言起步的,起初是为了兴趣,后来由于易语言被杀软误杀严重,连空白程序都杀,后来转到了学C#,随着学习的深入,接触越来越复杂的东西之后,发现有些功能没有用易语言来写方便,所以就想到用C#来调用易语言写的DLL,就和一般的Dll调用一样,没什么技术含量. 易语言中新建个Dll文件 C#中和一般的DLL一样调用 <p>using System; using System.Collections.Generic; using System.ComponentModel; using Syst

  • C#调用Win32的API函数--User32.dll

    Win32的API函数是微软自己的东西,可以直接在C#中直接调用,在做WinForm时还是很有帮助的.有时候我们之直接调用Win32 的API,可以很高效的实现想要的效果. 代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace WindowsAPI { class CSharp_

  • C# [ImportDll()] 知识小结

    这周在做公司的一个C#项目中,要写一个webservice提供一个下载方法,之前公司有过,但是要整改,于是这种鸟屎摊子又交给了我,其中一个密文流的下载中要应用我们小组另一伙人用C++写的四个dll, 这些DLL是由我们自己编写的,非.NET 托管,这是一个麻烦事: 如果用 IDE往工程里add reference时,它会提示这几个dll是没有注册的,不是.NET托管的.好吧,顺便补充一下.NET托管和非托管. 具体官方术语是: 托管DLL就是能够在公共语言运行库(Common Language

随机推荐