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日

相关文章

  • 一步步打造简单的MVC电商网站BooksStore(3)

    一步步打造简单的MVC电商网站BooksStore(3) 在这一部分中,我们将继续完善我们的MVC电商网站开发计划,包括:数据库设计,模型开发和控制器开发。 数据库设计 在我们的电商网站开发中,我们将使用MySQL数据库来存储数据。我们需要设计一些数据表来存储用户信息、产品信息、订单信息等。 具体来说,我们需要至少设计三个表:一个用户信息表,一个产品信息表和…

    C# 2023年5月31日
    00
  • 深入多线程之:深入分析Interlocked

    深入多线程之:深入分析Interlocked 介绍 多线程编程中,线程间的数据共享是必不可少的。但是,由于线程间数据的竞争,可能会存在数据异常的情况。而Interlocked类提供了一些原子性的操作,避免了竞争,从而保证线程间数据的准确性。 Interlocked 类及其方法 Interlocked 类的定义为:用于在多个线程之间提供原子操作的方法。 Int…

    C# 2023年6月7日
    00
  • asp.net jscript 一句话木马

    首先需要说明的是,一句话木马是一种常用的网络攻击技巧,攻击者可以通过一行代码或一句话控制Web服务器或受害者的计算机。因此,开发人员和网站维护人员应谨慎对待这些类型的攻击。 “asp.net jscript一句话木马”是一种特定的一句话木马,其使用asp.net语法和jscript编写,以下是完整攻略: 获取asp.net jscript一句话木马 asp.…

    C# 2023年5月31日
    00
  • C#中事件的动态调用实现方法

    下面就为大家详细讲解C#中事件的动态调用实现方法的完整攻略。 简介 在C#中,事件是非常常用的机制。有时我们需要在运行时动态地添加和移除事件的监听器,这时候动态调用事件就显得非常重要了。本文将详细介绍C#中动态调用事件的实现方法。 使用委托实现动态调用事件 C#中事件使用委托实现,在C#中委托是一种特殊的类型,它被用来封装具有相同参数和返回类型的方法。事件本…

    C# 2023年6月6日
    00
  • html加css样式实现js美食项目首页示例代码

    让我来详细讲解一下如何利用HTML和CSS实现一个美食项目的首页。 环境准备 在开始之前,你需要做好以下准备: 一台电脑和代码编辑器 熟悉HTML和CSS的基本语法 熟悉基本的JavaScript语法 目标设计 我们这个美食项目的首页,需要实现以下功能: 导航栏 轮播图 宣传广告图 美食推荐列表 HTML部分 首先,我们需要在HTML中添加必要的元素,包括导…

    C# 2023年5月31日
    00
  • Unity实现轮盘方式的按钮滚动效果

    下面是实现“Unity实现轮盘方式的按钮滚动效果”的攻略。 步骤一:创建UI布局 首先,我们需要创建一个包含多个按钮的UI布局。具体操作步骤如下: 在Unity编辑器中,打开场景视图,并点击“Create”->“UI”->“Canvas”创建一个UI画布。 在画布中,创建一个Panel对象,用来存放按钮。 在Panel对象中添加一个Vertica…

    C# 2023年6月3日
    00
  • 怎么利用c#修改services的Startup type

    要利用C#修改Windows服务的启动类型(Startup type),可以使用.NET Framework下的ServiceController和ServiceType类。步骤如下: 步骤一:添加引用 在项目中添加System.ServiceProcess引用。 步骤二:获取服务 使用ServiceController类获取要修改的服务,可以用服务名称或服…

    C# 2023年6月6日
    00
  • c#多进程通讯的实现示例

    下面我将为大家详细介绍“c#多进程通讯的实现示例”的完整攻略。 1. 简介 在实际应用中,我们可能需要同时启动多个进程,并实现这些进程之间的通讯,以完成某些特定的任务。C#语言提供了一些类和方法,可以方便地实现多进程通讯。 2. 进程间通讯方式 在C#中,有多种进程间通讯方式,包括:管道通讯、共享内存、网络通讯等。这里我们将以管道通讯和共享内存为例,进行讲解…

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