C#通过PInvoke调用c++函数的备忘录的实例详解

C#通过PInvoke调用C++函数的备忘录

什么是PInvoke

PInvoke是Platform Invoke的缩写,是.NET Framework提供给C#程序员调用非托管DLL(Dynamic Link Library)在 Windows 平台上的接口技术。PInvoke 提供的主要技术便是 Marshal 类,Marshal 类可以完成 数据类型 转换、内存管理等工作,使得 C# 能够正确的调用非托管 DLL中的函数以及使用数据。

C++函数在C#中的使用

在C#中默认情况下只能使用.NET Framework中提供的类库及开发的类库。但有时候我们需要使用非托管的dll库(主要是C/C++的动态库)。在这种情况下,我们可以使用PInvoke技术来实现对非托管库的调用。

比如,下面例子我们要调用一个C++的函数:

extern "C"{
    __declspec(dllexport) int Add(int a, int b)
    {
        return a + b;
    }
}

这个函数是通过__declspec(dllexport)修饰导出,目的是在被其他程序调用的时候可以被找到。

编写一个C#的程序调用这个C++函数:

using System;
using System.Runtime.InteropServices;

namespace PInvokeDemo
{
    class Program
    {
        [DllImport("test.dll", EntryPoint = "Add", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
        static extern int Add(int a, int b);

        static void Main(string[] args)
        {
            int result = Add(1, 2);
            Console.WriteLine(result);
            Console.ReadKey();
        }
    }
}

重点介绍DllImport属性

  • DllImport属性的常用属性说明
  • EntryPoint:对应C++导出方法的方法名,如果与C++中方法名相同可以省略。注意:C#不支持C++的函数重载,查询导出方法名时需要区分名称。
  • SetLastError:表示方法调用后,调用GetLastError函数可以获取扩展的错误信息。
  • CharSet:字符集,指明字符集的类型,防止出现字符混乱的问题。C++字符集是MBCS类型。Ansi占用1个字节,Unicode占用2个字节。
  • ExactSpelling:字符匹配。
  • CallingConvention:使用的调用约定,默认为 STDCALL。其他的还有[Cdecl]和[Thiscall]等约定方式。

注意点

  1. 确保将生成的DLL文件路径与应用程序在同一个目录中,或者添加一个引用到该DLL文件。
  2. 确保你已经正确包含了System.Runtime.InteropServices命名空间,用于访问DllImportAttribute。

C++结构体在C#中的使用

在C++的dll中也有结构体,结构体中定义了很多值和方法。如何来将这个结构体导入到C#中来使用呢?

为了展示,我们新添一个C++的代码示例:

typedef struct tagBookInfo {
    char BookName[50];
    int BookID;
}BookInfo;

extern "C" {
    __declspec(dllexport) int ChangeBookInfo(BookInfo* pbookinfo, int bookid, const char* newname)
    {
        if (!pbookinfo)
            return 0;

        if (pbookinfo->BookID == bookid)
        {
            strncpy_s(pbookinfo->BookName,sizeof(pbookinfo->BookName), newname, strlen(newname));
            return 1;
        }
        else
        {
            return 0;
        }
    }
}

这个函数为一个改变图书信息的函数。通过传入一个BookInfo结构体指针(BookInfo*)来改变指定ID的图书的名称(newname)。

对应C#中的代码:

using System;
using System.Runtime.InteropServices;

namespace PInvokeDemo
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct BookInfo
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string BookName;
        public int BookID;
    }

    class Program
    {

        [DllImport("test.dll", EntryPoint = "ChangeBookInfo", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        static extern int ChangeBookInfo(ref BookInfo pbookinfo, int bookid, [MarshalAs(UnmanagedType.LPTStr)] String newname);

        static void Main(string[] args)
        {
            BookInfo mybook = new();
            mybook.BookID = 100;
            mybook.BookName = "book_name";
            Console.WriteLine("Before ChangeBookInfo, myBookName is " + mybook.BookName);
            if(ChangeBookInfo(ref mybook, 100, "new book name") == 1)
            {
                Console.WriteLine("After ChangeBookInfo, myBookName is " + mybook.BookName);
            }
            else
            {
                Console.WriteLine("Fail to change book info.");
            }
            Console.ReadKey();
        }
    }
}

重点介绍StructLayout属性

  • StructLayout属性的常用属性说明:
  • LayoutKind:指定如何布置类或结构,有三种枚举
    • Sequential:在内存中顺序排列每个字段,每个字段偏移量+当前长度 = 下一个字段偏移量。
    • Explicit:自定义布局。
    • Auto:让程序根据需要自动进行调整布局。
  • CharSet:字符集,指明字符集的类型,防止出现字符混乱的问题。C++字符集是MBCS类型。Ansi占用1个字节,Unicode占用2个字节。
  • FieldOffset:可以指定字段的偏移量,一个结构体一个一个字段地抽象出相对应的值。

输出结果:

Before ChangeBookInfo, myBookName is book_name
After ChangeBookInfo, myBookName is new book name

以上就是通过PInvoke技术调用C++的函数,以及C++结构体导入C#中的说明和示例。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#通过PInvoke调用c++函数的备忘录的实例详解 - Python技术站

(0)
上一篇 2023年6月7日
下一篇 2023年6月7日

相关文章

  • php通过淘宝API查询IP地址归属等信息

    下面是 “php通过淘宝API查询IP地址归属等信息”的完整攻略: 1. 获取淘宝API的AppKey 在使用淘宝API之前,我们需要先获得AppKey。具体步骤如下: 进入淘宝开放平台官网:https://open.taobao.com/ 点击“控制台”->“应用管理”->“创建应用”,按照提示进行填写并提交。 提交申请后,等待审核通过,审核通…

    C# 2023年6月1日
    00
  • 关于C#数强转会不会抛出异常详解

    关于”C#数强转会不会抛出异常详解”的攻略,我会分为以下几个部分进行讲解: 什么是强制类型转换? C#数值类型之间的强制类型转换规则 强制类型转换会不会抛出异常? 强制类型转换时如何避免异常的发生? 两个示例演示强制类型转换的使用和异常处理 1. 什么是强制类型转换? 强制类型转换是将一个数据类型的值转换为另一个数据类型的过程,通常也被称为“类型转换”或“类…

    C# 2023年5月15日
    00
  • ASP.NET中常用的三十三种代码第5/7页

    ASP.NET中常用的三十三种代码是一份比较全面的ASP.NET代码示例集合,包含了在ASP.NET开发过程中经常会用到的33种代码。下文将对其中第5/7页的代码进行详细介绍。 5. 使用Linq查询XML 该部分代码展示了如何在C#中使用Linq查询XML文件中的数据。以下是代码示例: using System.Linq; using System.Xml…

    C# 2023年5月31日
    00
  • Unity 从Resources中动态加载Sprite图片的操作

    下面是详细讲解“Unity 从Resources中动态加载Sprite图片的操作”的完整攻略。 一、前言 在Unity中,我们可以将一些资源文件放在一个名为“Resources”的文件夹中。这些资源文件可以通过Resources.Load方法进行动态加载,其中包括图片、音频、视频等资源。在本文中,我们将详细讲解如何在Unity中动态加载Sprite图片。 二…

    C# 2023年6月3日
    00
  • 在 asp.net core 的中间件中返回具体的页面的实现方法

    在 ASP.NET Core 中,中间件是处理 HTTP 请求和响应的组件,可以是任何处理这些请求和响应的代码。在中间件中返回具体的页面,通常需要借助于 ASP.NET Core MVC 中的视图引擎和控制器。下面是具体的实现方法。 步骤1:添加 MVC 中间件服务 首先,需要在 ASP.NET Core 应用程序中添加 MVC 中间件服务。在 Startu…

    C# 2023年5月31日
    00
  • C#实现Array,List,Dictionary相互转换

    下面详细讲解一下C#实现Array、List、Dictionary相互转换的完整攻略。 1. Array和List的相互转换 Array转List 使用ToList()方法可以将Array类型的数组转换为List泛型集合类型,具体代码如下所示: string[] array = { "apple", "banana",…

    C# 2023年6月7日
    00
  • C#数组应用分析第1/2页

    C#数组应用分析攻略 什么是C#数组 C#中的数组是一种数据结构,用于存储相同类型的固定大小的元素序列。数组在编程中非常常见,可以提高代码运行效率,也方便了数据的管理。 如何声明C#数组 在C#中声明数组需要指定元素的数据类型和数组的名称,如下所示: int[] numbers = new int[5]; 其中,int[]代表int类型的数组,numbers…

    C# 2023年6月7日
    00
  • 浅谈C#数组(一)

    来分享一下“浅谈C#数组(一)”的完整攻略。 简介 C#中的数组是一组具有相同数据类型的元素的集合。在C#中声明数组时需要指定数据类型、数组名称和数组元素数量。 声明和初始化数组 在C#中声明数组的语法如下: <数据类型>[] <数组名称> = new <数据类型>[<元素数量>]; 例如: int[] num…

    C# 2023年5月15日
    00
合作推广
合作推广
分享本页
返回顶部