C#调用C++ DLL中返回接口类对象指针的函数
主要有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 <string>
// 注释掉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 <fstream>
#include <ctime>
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 << "[" << strInfo << "]" << buffer << " :" << infoMessage << std::endl;
}
of.close();
}
编译生成后,先用一个C++的控制台项目测试以下这个dll是否有问题:
// LibTestByCpp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "lib/CppLibDll.h"
#pragma comment(lib, "lib/CppLibDll.lib") // 隐式调用
int main()
{ // C++很简单,直接通过工厂方法生成接口对象,然后调用接口中定义的虚方法即可
IExport *p = ExportObjectFactory();
p->OnInit("D:/TestInfo.txt");
p->OnTest();
DestroyExportObject(p);
system("pause");
return 0;
}
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.cnblogs.com/djh5520/p/14340517.html