您的位置:首页 > 文旅 > 旅游 > C# 集成 C++ 的方法和实践 - P/Invoke(平台调用)- 1

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

2025/4/18 12:56:59 来源:https://blog.csdn.net/yellow_hill/article/details/139421819  浏览:    关键词:C# 集成 C++ 的方法和实践 - P/Invoke(平台调用)- 1

环境:


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 onceextern "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.dllFile Type: DLLSection contains the following exports for AAMED_DLL_DEMO1.dll00000000 characteristicsFFFFFFFF time date stamp0.00 version1 ordinal base1 number of functions1 number of namesordinal hint RVA      name1    0 0001133E Add = @ILT+825(Add)Summary1000 .00cfg1000 .data2000 .idata1000 .msvcjmc3000 .pdata3000 .rdata1000 .reloc1000 .rsrc9000 .text10000 .textbss

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


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

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com