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日

相关文章

  • c#异步读取数据库与异步更新ui的代码实现

    你好,想要实现c#异步读取数据库并异步更新UI,可以采用以下步骤: 步骤一:建立异步的数据库连接 在c#中,可以使用SqlClient.SqlConnection类来建立数据库连接,并使用await关键字进行异步操作。具体代码如下: public async Task<SqlConnection> ConnectToDBAsync() { str…

    C# 2023年5月31日
    00
  • C#影院售票系统毕业设计(4)

    C#影院售票系统毕业设计(4)详细攻略: 需求分析与实现设计 在需求分析中,我们确定了本系统的主要功能:用户注册登录、电影列表、选座购票、订单管理、影院信息查询等。 接着,我们需要对这些功能进行实现设计,可以采用 MVC (Model-View-Controller)架构。 其中,Model负责数据处理、View负责用户界面,Controller负责用户交互…

    C# 2023年6月7日
    00
  • sql字符串函数大全和使用方法示例

    SQL字符串函数是SQL语言中的一类函数,用于处理字符串类型的数据。这些函数可以用于字符串的拼接、截取、替换、转换等操作。本文将介绍SQL字符串函数的大全和使用方法示例。 SQL字符串函数大全 以下是SQL字符串函数的大全: CONCAT:用于将两个或多个字符串连接在一起。 SUBSTRING:用于从字符串中提取子字符串。 REPLACE:用于将字符串中的一…

    C# 2023年5月15日
    00
  • C#图片处理类分享

    C#图片处理类分享 在本文中,我们将分享一些如何使用C#图片处理类(Image class)的技巧和实用示例。这些技巧涵盖的范围包括图片压缩,大小和比例的更改,旋转和翻转图片等。 图片压缩 压缩图片可以减小图片的大小,从而减少图片在服务器上的存储空间和网络传输带宽占用。下面是一个简单的示例,演示如何使用C#的Image类来压缩图片: using System…

    C# 2023年5月31日
    00
  • .NET Core使用C#扫描并读取图片中的文字

    .NET Core使用C#扫描并读取图片中的文字 在.NET Core中,可以使用C#编写代码来扫描并读取图片中的文字。这可以通过OCR(Optical Character Recognition,光学字符识别)技术实现。本文将介绍如何使用C#和Tesseract OCR库来扫描并读取图片中的文字。 准备工作 在开始之前,需要完成以下准备工作: 安装.NET…

    C# 2023年5月17日
    00
  • ItemsControl 数据绑定的两种方式

    我来为你讲解“ItemsControl 数据绑定的两种方式”的完整攻略。 一、介绍 在 WPF 中,我们通常使用 ItemsControl 来呈现一组数据集合。ItemsControl 提供了两种数据绑定的方式:通过 ItemsSource 属性绑定数据集合,或者通过数据模板绑定单个对象。 二、数据绑定方式一:ItemsSource 属性绑定数据集合 在此数…

    C# 2023年6月6日
    00
  • 使用C#代码获取存储过程返回值

    下面是详细的“使用C#代码获取存储过程返回值”的攻略。 1. 获取存储过程返回值 在C#中调用存储过程时,我们经常需要获取存储过程的返回值。获取存储过程返回值的方法有以下两种: 1.1 使用output参数获取返回值 在存储过程中声明一个output参数,用于返回该存储过程的返回值。在C#中,使用和调用存储过程一样的方法传递一个output参数,然后读取输出…

    C# 2023年6月7日
    00
  • Asp.net core程序中使用微软的依赖注入框架

    Asp.net core程序中使用微软的依赖注入框架 在Asp.net core程序中,使用微软的依赖注入框架可以方便地管理应用程序中的依赖项。在本攻略中,我们将介绍如何在Asp.net core程序中使用微软的依赖注入框架,并提供两个示例说明。 步骤一:安装依赖注入框架 在项目中安装依赖注入框架 在项目中安装依赖注入框架。可以使用NuGet包管理器或者在项…

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