C#调用C++ DLL中返回接口类对象指针的函数

您所在的位置:网站首页 通过接口引用对象 C#调用C++ DLL中返回接口类对象指针的函数

C#调用C++ DLL中返回接口类对象指针的函数

2023-09-09 19:00| 来源: 网络整理| 查看: 265

主要有2种方法,非托管和托管方式,2种都需要具备一定C++及DLL的基础: 1.通过一个间接层DLL来封装接口对象的方法调用 先来创建一个dll项目,用来生成一个给C#调用的dll:

 

 

 

 

 

项目结构如下:(部分文件是自行添加的如模块定义文件def)

 

 

 各个文件的内容如下:

// CppLibDll.h是接口定义头文件 #pragma once // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 CPPLIBDLL_EXPORTS // 符号编译的。在使用此 DLL 的 // 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 // CPPLIBDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的 // 符号视为是被导出的。 #ifdef CPPLIBDLL_EXPORTS #define CPPLIBDLL_API __declspec(dllexport) #else #define CPPLIBDLL_API __declspec(dllimport) #endif #include // 注释掉VS自动生成的示例代码 #if 0 // 此类是从 CppLibDll.dll 导出的 class CPPLIBDLL_API CCppLibDll { public: CCppLibDll(void); // TODO: 在此添加您的方法。 }; extern CPPLIBDLL_API int nCppLibDll; CPPLIBDLL_API int fnCppLibDll(void); #endif // 导出的接口类 class IExport { public: // 返回值:成功:0,失败:非0,失败信息保存在D:/Log.txt virtual int OnInit(std::string strSaveFilePath) = 0; // 返回值:成功:0,失败:非0,失败信息保存在D:/Log.txt virtual int OnTest() = 0; virtual ~IExport() {} }; // 假设这是原来的DLL暴露的接口函数 // 这种返回接口类对象的指针的导出函数,对于C++来说没有什么问题,但是对于C#没办法直接用对象指针调用接口方法 extern "C" CPPLIBDLL_API IExport* __stdcall ExportObjectFactory(); extern "C" CPPLIBDLL_API void __stdcall DestroyExportObject(IExport* obj); // 通过建立一个接口层,帮C#完成间接调用接口方法 // 这2个方法可以单独做成一个间接层dll(此处只是为了方便,一般情况也只能自己另外写一个dll,因为你不能修改别人的dll源码)// 下面strSaveFilePath变量类型不要用string,C#中的string类型和C++的string不匹配 extern "C" CPPLIBDLL_API int __stdcall CallOnInit(IExport* obj, const char* strSaveFilePath); extern "C" CPPLIBDLL_API int __stdcall CallOnTest(IExport* obj); // CppLibDll.cpp是接口实现头文件 // CppLibDll.cpp : 定义 DLL 应用程序的导出函数。 // #include "stdafx.h" #include "CppLibDll.h" #include "ExportImpl.h" // 实现了接口类的具体子类 #if 0 // 这是导出变量的一个示例 CPPLIBDLL_API int nCppLibDll=0; // 这是导出函数的一个示例。 CPPLIBDLL_API int fnCppLibDll(void) { return 42; } // 这是已导出类的构造函数。 // 有关类定义的信息,请参阅 CppLibDll.h CCppLibDll::CCppLibDll() { return; } #endif extern "C" CPPLIBDLL_API IExport* __stdcall ExportObjectFactory() { return new ExportImpl(); } extern "C" CPPLIBDLL_API void __stdcall DestroyExportObject(IExport* obj) { if (obj) { delete obj; obj = nullptr; } } extern "C" CPPLIBDLL_API int __stdcall CallOnInit(IExport* obj, const char* strSaveFilePath) { if (obj) { return obj->OnInit(strSaveFilePath); } else { return -1; } } extern "C" CPPLIBDLL_API int __stdcall CallOnTest(IExport* obj) { if (obj) { return obj->OnTest(); } else { return -1; } }

Source.def是模块定义文件,用于导出dll接口函数名,并保证其不被重命名:

LIBRARY "CppLibDll" EXPORTS ExportObjectFactory @ 1 DestroyExportObject @ 2 CallOnInit @ 3 CallOnTest @ 4

以下2个文件是实现了接口的一个具体派生类:

// ExportImpl.h #pragma once #include "CppLibDll.h" // 实现接口 class ExportImpl : public IExport { public: ExportImpl(); ~ExportImpl(); virtual int OnInit(std::string strSaveFilePath) override; virtual int OnTest() override; enum InfoType { InitError, InitInfo, TestError, TestInfo }; private: std::string m_strFilePath; void Log(InfoType info, std::string infoMessage); }; // ExportImpl.cpp #include "stdafx.h" #include "ExportImpl.h" #include #include const std::string logpath = "D:/Log.txt"; ExportImpl::ExportImpl() { m_strFilePath = ""; } ExportImpl::~ExportImpl() { // 如有资源需要释放 } int ExportImpl::OnInit(std::string strSaveFilePath) { if (strSaveFilePath == "") { Log(InfoType::InitError, "The given save file path is empty!"); return -1; } m_strFilePath = strSaveFilePath; Log(InfoType::InitInfo, "Init Ok!"); return 0; } int ExportImpl::OnTest() { if (m_strFilePath == "") { Log(InfoType::TestError, "The save file path is empty!"); return -1; } std::ofstream outFile(m_strFilePath, std::ios::app); if (!outFile) { Log(InfoType::TestError, "Open save file failed!"); return -2; } Log(InfoType::TestInfo, "Start test!"); Log(InfoType::TestInfo, "Testing..."); Log(InfoType::TestInfo, "Testing Over!"); Log(InfoType::TestInfo, "Result: Pass"); return 0; } void ExportImpl::Log(InfoType info, std::string infoMessage) { // 获取当前时间 std::time_t rawtime; char buffer[64]; std::time(&rawtime); // 获取系统时间 std::tm *localTm = localtime(&rawtime); // 生成本地时间 std::strftime(buffer, 64, "%Y-%m-%d %H:%M:%S", localTm); std::string strFilePath = logpath; std::string strInfo = ""; switch (info) { case InfoType::InitError: strInfo = "Init Error"; break; case InfoType::InitInfo: strInfo = "Init Info"; strFilePath = m_strFilePath; break; case InfoType::TestError: strInfo = "Test Error"; break; case InfoType::TestInfo: strInfo = "Test Info"; strFilePath = m_strFilePath; break; default: strInfo = "Undefine"; break; } std::ofstream of(strFilePath, std::ios::app); if (of) { of OnInit("D:/TestInfo.txt"); p->OnTest(); DestroyExportObject(p); system("pause"); return 0; }

 

 

 下面是C#封装并调用这个dll的代码: using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Disposable { class Program { static void Main(string[] args) { // CUseCppInterfaceObject obj = new CUseCppInterfaceObject(); // obj.OnInit(@"D:\TestInfo.txt"); // obj.OnTest(); // obj.Dispose();        

        using (CUseCppInterfaceObject obj = new CUseCppInterfaceObject())         {           obj.OnInit(@"D:\TestInfo.txt");           obj.OnTest();           //obj.Dispose();         }

Console.ReadLine(); } } public class CUseCppInterfaceObject : IDisposable { #region PInvokes // DLL内部函数 [DllImport("CppLibDll.dll")] static private extern IntPtr ExportObjectFactory(); [DllImport("CppLibDll.dll")] static private extern void DestroyExportObject(IntPtr pObj); [DllImport("CppLibDll.dll")] static private extern int CallOnInit(IntPtr pObj, string strSaveFilePath); [DllImport("CppLibDll.dll")] static private extern int CallOnTest(IntPtr pObj); #endregion PInvokes #region Members private IntPtr m_pNativeObject; // 保存创建的C++接口对象的指针 #endregion Members public CUseCppInterfaceObject() { // 通过dll导出接口创建C++接口对象实例 this.m_pNativeObject = ExportObjectFactory(); } // Finalizer is called when Garbage collection occurs, but only if // the IDisposable.Dispose method wasn't already called. ~CUseCppInterfaceObject() { Dispose(false); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool bDisposing) { if (this.m_pNativeObject != IntPtr.Zero) { // 非空指针,调用dll的接口销毁创建的接口对象 DestroyExportObject(this.m_pNativeObject); this.m_pNativeObject = IntPtr.Zero; } if (bDisposing) { // 已经清理非托管内存,无需再调用终结器 GC.SuppressFinalize(this); } } #region Wrapper public int OnInit(string strSaveFilePath) { return CallOnInit(this.m_pNativeObject, strSaveFilePath); } public int OnTest() { return CallOnTest(m_pNativeObject); } #endregion Wrapper } } 编译运行这个控制台程序,最终结果如下,成功调用了dll:

 

 

 

参考:

https://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class

https://stackoverflow.com/questions/9211128/p-invoke-how-to-call-unmanaged-method-with-marshalling-from-c



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3