C# 集成 C++ 的方法和实践 - P/Invoke(平台调用)- 1

news2024/11/23 17:20:51

环境:


P/Invoke(平台调用):

C#可以通过P/Invoke调用C++编写的DLL中的函数。

1.1 适用范围:

P/Invoke 是一种在 C# 程序中调用非托管代码(如 C++ 动态链接库)的方式。这种方法适用于函数调用相对简单的情况。

1.2 步骤:

  • 在 C++ 中编写算法并编译生成 DLL。
  • 在 C# 项目中使用 DllImport 属性引入 DLL。

 1.3 案例程序:

1.3.1 新建一个ExportedFunctions.cpp【函数调用法】

// MathFunctions.cpp
#include "MathFunctions.h"

int Add(int a, int b) {
    return a + b;
}

特点

  • 使用了extern "C",这告诉编译器按照C语言的方式处理函数名,避免了名称修饰。
  • 使用了__declspec(dllexport),这指示编译器导出该函数,使其在DLL中可见。
  • 这是一个自由函数(不属于任何类),可以直接被P/Invoke调用。

1.3.2 新建一个 ExportedFunctions.h

// MathFunctions.h
#pragma once

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

 1.3.3 在C#中调用动态库的函数:

using System;
using System.Runtime.InteropServices;

class Program
{
    // 使用DllImport属性声明DLL中的函数
    [DllImport("MathFunctions.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Add(int a, int b);

    static void Main(string[] args)
    {
        // 调用DLL中的Add函数
        int result = Add(5, 10);
        Console.WriteLine("The result of adding 5 and 10 is: " + result);
    }
}

2 VS STUDIO 生成DLL的DllMain模板给出的意义:

2.1 dllmain.cpp文件

包含了DllMain函数的框架。DllMain是DLL的入口点,它是一个特殊的函数,由Windows操作系统在DLL的生命周期中的关键时刻自动调用。这个函数的原型由Windows API定义,其作用是处理DLL的各种加载和卸载事件。

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

DllMain函数的参数如下:

  • HMODULE hModule:被加载DLL的模块句柄。
  • DWORD ul_reason_for_call:调用原因代码,指示为什么调用DllMain
  • LPVOID lpReserved:保留参数,用于特定调用原因的附加信息。

ul_reason_for_call参数可以有以下几种值:

  • DLL_PROCESS_ATTACH:DLL被加载到地址空间中时调用。这是初始化DLL设置的好地方,例如全局变量的初始化。
  • DLL_THREAD_ATTACH:一个新线程被创建到包含DLL的进程中时调用。这允许DLL为每个线程设置特定的数据。
  • DLL_THREAD_DETACH:一个线程结束时调用,不再需要DLL的服务。这可以用来清理线程特定的数据。
  • DLL_PROCESS_DETACH:DLL从进程的地址空间卸载时调用。这是执行清理工作,如释放资源和注销全局变量的好地方。

DllMain函数中,每个case对应一个调用原因。在默认情况下,Visual Studio生成的模板代码中,这些case下没有任何操作,只是简单的break语句。这意味着DLL在加载和卸载时不会执行任何特别的操作。如果你需要在DLL加载或卸载时执行特定的初始化或清理代码,你可以在相应的case下添加你的代码。例如:

switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
    // 初始化代码,如分配资源
    break;
case DLL_THREAD_ATTACH:
    // 线程特定初始化代码
    break;
case DLL_THREAD_DETACH:
    // 线程退出时的清理代码
    break;
case DLL_PROCESS_DETACH:
    // 进程退出时的清理代码,如释放资源
    break;
}

3 在VS 2019中,融合DllMain模板和P/Invoke(平台调用)

 3.1 在C#中进行集成:

将编译好的DLL文件放在你的C#项目可以访问到的位置。

我现在项目目录下,构建了一个lib目录,

然后,在C#中引入dll,

添加,编译好的C++库


问题:

1 尝试引入DLL

解决:

当您使用P/Invoke调用非托管代码(如C++ DLL)时,不需要注册DLL或将其作为COM组件。 

确保dll文件位于C#项目的输出目录中(如bin/Debugbin/Release),或者将DLL文件路径添加到系统PATH环境变量中。

 2 无法找到入口:

Error: 无法在 DLL“AAMED_DLL_DEMO1.dll”中找到名为“Add”的入口点。
 

 笔者的地址在下面:

D:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.20.27508\bin\HostX64\x64 

 执行命令如下:

 .\dumpbin.exe /EXPORTS K:\Prj_MotosCatch\Prj_Src\AAMED_LIB_Demo\AAMED_DLL_DEMO1\x64\Debug\AAMED_DLL_DEMO1.dll

结果:

PS D:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.20.27508\bin\HostX64\x64> .\dumpbin.exe /EXPORTS K:\Prj_MotosCatch\Prj_Src\AAMED_LIB_Demo\AAMED_DLL_DEMO1\x64\Debug\AAMED_DLL_DEMO1.dll
Microsoft (R) COFF/PE Dumper Version 14.20.27525.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file K:\Prj_MotosCatch\Prj_Src\AAMED_LIB_Demo\AAMED_DLL_DEMO1\x64\Debug\AAMED_DLL_DEMO1.dll

File Type: DLL

  Summary

        1000 .00cfg
        1000 .data
        2000 .idata
        1000 .msvcjmc
        3000 .pdata
        3000 .rdata
        1000 .reloc
        1000 .rsrc
        9000 .text
       10000 .textbss

 确实没有看到add的函数入口

修正代码,之前想按CLASS来写不行:

然后,重新执行:

PS D:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.20.27508\bin\HostX64\x64> .\dumpbin.exe /EXPORTS K:\Prj_MotosCatch\Prj_Src\AAMED_LIB_Demo\AAMED_DLL_DEMO1\x64\Debug\AAMED_DLL_DEMO1.dll
Microsoft (R) COFF/PE Dumper Version 14.20.27525.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file K:\Prj_MotosCatch\Prj_Src\AAMED_LIB_Demo\AAMED_DLL_DEMO1\x64\Debug\AAMED_DLL_DEMO1.dll

File Type: DLL

  Section contains the following exports for AAMED_DLL_DEMO1.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 0001133E Add = @ILT+825(Add)

  Summary

        1000 .00cfg
        1000 .data
        2000 .idata
        1000 .msvcjmc
        3000 .pdata
        3000 .rdata
        1000 .reloc
        1000 .rsrc
        9000 .text
       10000 .textbss

这时候,已经能看到Add的函数定义了,运行后结果正常,可以运行DLL的函数内容。


本文的案例代码:需要知识付费:

https://download.csdn.net/download/yellow_hill/89396682

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1797969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文了解AI绘画两大鼻祖 Midjourney 和 Stable Diffusion的区别,超详细讲解小白入门必看教程!

大家好,我是画画的小强 要说AI绘画软件哪家强?有人说Midjoureny (MJ), 有人说Stable Diffuion(SD),那他们到底有什么区别?应该选择哪款软件学习?今天带大家全面了解一下!文末可白嫖AI资料哦~ 一…

PVE安装CENTOS9提示“Fatal glibc error: CPU does not support x86-64-v2”

问题描述:PVE安装CENTOS9提示“Fatal glibc error: CPU does not support x86-64-v2” RHEL 9要求x86_64的CPU支持x86-64-v2,x86-64-v2需要处理器支持 CMPXCHG16B、LAHF-SAHF、POPCNT、SSE3、SSE4.1、SSE4.2、SSSE3 等现代指令集 解决方法:…

API接口测试工具:jmeter的安装、汉化、Jmeter桌面快捷图标和基本使用

文章目录 测试工具:JmeterJmeter安装和配置Jmeter汉化设置中文语言:永久方式设置中文语言:临时方式 设置Jmeter桌面快捷图标jmeter基本用法Jmeter无法保存测试问题解决 测试工具:Jmeter Jmeter依赖于JDK,所以必须确保…

SwiftUI获取用户的位置信息(CLLocationManager,CLLocationManagerDelegate)

本篇文章将会介绍一下在SwiftUI中如何通过CorLocation框架获取用户的位置信息,因为获取位置信息属于用户的隐私信息,所以需要在Info.plist文件里面加上访问位置权限的说明。 关于位置信息,可以请求两种级别的许可:always和when i…

项目经理进入职场都会经历的三个阶段

对于项目经理而言,进入职场是一个不断学习和成长的过程。在这个过程中,项目经理通常会经历三个主要阶段,每个阶段都有其独特的特点和挑战。 一、基础建设与学习阶段 对于新入行的项目经理来说,最初的阶段主要是基础技能的积累和…

AI绘画中的色彩空间转换技术

在数字艺术的广阔天地中,AI绘画作为一种新兴的创作方式,正以其独特的魅力吸引着越来越多的关注。它不仅仅是一种技术,更是一种全新的艺术表现形式。而在AI绘画的背后,色彩空间转换技术起着至关重要的作用。今天,我们就…

政安晨【零基础玩转各类开源AI项目】:解析开源项目:Champ 利用三维参数指导制作可控且一致的人体图像动画

目录 论文题目 Champ: 利用三维参数指导制作可控且一致的人体图像动画 安装 创建 conda 环境: 使用 pip 安装软件包 推理 1. 下载预训练模型 2. 准备准备引导动作数据 运行推理 训练模型 准备数据集 运行训练脚本 数据集 政安晨的个人主页:…

Web LLM 攻击技术

概述 在ChatGPT问世以来,我也尝试挖掘过ChatGPT的漏洞,不过仅仅发现过一些小问题:无法显示xml的bug和错误信息泄露,虽然也挖到过一些开源LLM的漏洞,比如前段时间发现的Jan的漏洞,但是不得不说传统漏洞越来…

抖音外卖区域代理需要多少钱?入局成本如何计算?

随着抖音外卖的日益火爆和抖音外卖平台全国代理的退场,想要申请抖音外卖平台区域代理的人数不断上涨。但是,创业毕竟不是儿戏,每一个决定都需要经过权衡利弊。而就做抖音外卖区域代理这一项目而言,抖音外卖平台区域代理需要多少钱…

洛杉矶裸机云多IP服务器网线路测评

在当今日益数字化的世界中,服务器的网络线路质量对于企业的运营效率和用户体验具有至关重要的作用。特别是对于那些寻求在洛杉矶部署裸机云多IP服务器的企业来说,了解服务器的网络线路质量显得尤为重要。本文将对洛杉矶裸机云多IP服务器的网络线路进行测…

Idea解决堆栈溢出

废话不说了,这问题搞了我两天,最近在用内网办公,没用公网,所以博客暂时没更新

QA | 关于智能座舱SusPIS-ATx系统常见问题答疑

前沿 在上一期《基于SusPIS-ATx的座舱仿真系统搭建与评估方法创意研讨会》中,我们围绕汽车智能座舱仿真测试相关评价规范和法规(如C-ICAP),引入了智能座舱测试行业难点及次生问题,介绍了基于SusPIS-ATx的智能座舱全域…

原花青素优化定向壳聚糖微通道的简单免疫调控结构设计

引用信息 文 章:A facile Immunoregulatory Constructional Design by Proanthocyanidin Optimizing Directional Chitosan Microchannel 期 刊:Small(影响因子:13.3) 发表时间:29/02/2024 作 …

多项目管理,如何平衡资源分配和优先级?

在多项目管理中,平衡资源分配和优先级是一项至关重要的任务,这不仅关乎项目的顺利进行,还直接影响到组织的整体效率和战略目标的实现。如果不能合理进行资源分配,容易引起资源的浪费和关键项目进度延期等问题,这不利于…

target resources requests storage size is smaller than the source

在进行dv克隆时,通过如下方式: kind: DataVolume metadata:annotations:cdi.kubevirt.io/storage.deleteAfterCompletion: "false"name: 7713bb8fdecd462fa0ca726e21cd9fa3-1namespace: default spec:pvc:accessModes:- ReadWriteManyresourc…

源代码加密:构筑软件安全的核心防线

在数字化时代,源代码作为软件的灵魂,承载着企业的核心竞争力和创新成果。源代码加密,作为一种精密的安全措施,旨在构建一道坚不可摧的防线,保护这些珍贵的数字资产免受未授权访问和泄露的威胁。 源代码加密的核心价值在…

霸王餐系统:解决新店销量,老店单量增长的秘诀

前言 在如今的外卖市场中,新店如何快速突破基础销量,老店又怎样持续增加单量,一直是商家们关注的焦点。霸王餐系统,便是一个行之有效的解决方案,它不仅以独特的业务模式吸引了消费者,更助力商家实现了销量…

工业信息化SCI期刊,中科院1区TOP,IF=12.3,收稿范围广泛

一、期刊名称 IEEE Transactions on Industrial Informatics 二、期刊简介概况 期刊类型:SCI 学科领域:工程工业 影响因子:12.3 中科院分区:1区TOP 三、期刊征稿范围 IEEE工业信息学汇刊是一本多学科期刊,发表技…

分布式版本控制工具软件——Git概述

目录 一、Git概述1.为什么要学习Git?(1)SCM概念(2)SCM实现 2.什么是版本控制?(1)版本控制软件的基础功能(2)集中式版本控制(3)分布式版…

【LeetCode热题100总结】239. 滑动窗口最大值

题目描述 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1: 输入:nums [1,3,-1,-3,5,3,6,7]…