本篇介绍如何使用C++开发DLL给WPF的C#脚本调用。本文虽然以C#的WPF窗体应用为例子,但不限于此,.net平台都可以使用,包括Unity的C#脚本。
项目准备
首先VS2019相对于VS2017最明显的变化就是创建新建工程的界面,创建C++ DLL 工程和C# WPF如下图所示:
C++项目的配置就参考之前的文章https://blog.youkuaiyun.com/luoyu510183/article/details/83999548,下面就不详细说明了。C#的UI代码我也不说明,主要是讲C#调用C++ DLL的部分,其他部分可以下载我的工程看源码理解。
C++项目的提醒事项:
C++的导出设置
先看看C++项目的文件结构:
NativeInterface
先看下NativeIterface的代码,这个文件是把这个DLL下所有的导出接口都以一个类的形式进行导出。代码如下:
/////////////////////////////////////////////
////////////NativeInterface.h////////////////
/////////////////////////////////////////////
#pragma once
extern "C" {
//本类把本DLL下的所有接口都统一在这里,使用单例的方式创建指针给C#使用
//以下的成员函数皆为函数指针的形式,因为,类的函数实际上还有一个隐藏this的指针参数,
//不便于C#解析。ITest1Manager这个类也举了怎么导出类的成员函数的例子。
//注意下面的函数指针和函数都是_stdcall,这是微软的示例里面使用的默认调用方式,即
//从右到左参数入栈,并且被调用方清理堆栈。这个是windows独有的,包括_thiscall,_clrcall,__cdecl等。
//关注于Windows平台的需要好好理解,其他默认使用_stdcall就行
class NativeManager
{
public:
//字符串传递测试
const char* (_stdcall *GetModuleName)();
//设置回调函数测试
void (_stdcall* SetLogHandler)(LogHandler handler);
//导出其他类测试
class CTestManager* TestManager;
//测试成员变量,错误
int Count;
//正确的获取总数
int (_stdcall *GetCount)();
//非函数指针,虚函数导出测试
class ITest1Manager* Test1Manager;
NativeManager();
~NativeManager();
};
//导出不能直接导出变量,所以用这个函数导出类的指针,用单例的方式创建
_declspec(dllexport) NativeManager* _stdcall CreateNativeManager();
//本函数用于释放导出类
_declspec(dllexport) void _stdcall ReleaseNativeManager();
//测试设置C#的回调函数
_declspec(dllexport) void (_stdcall ExSetLogHandler)(LogHandler handler);
}
//测试不使用 extern "C"的导出函数名
_declspec(dllexport) void (_stdcall ReleaseNativeManager1)(int num);
/////////////////////////////////////////////
////////////NativeInterface.cpp//////////////
/////////////////////////////////////////////
#include "pch.h"
#include "NativeInterface.h"
#include "CTestManager.h"
static const char* name = "Name is Native Interface";
static NativeManager* Instance = nullptr;
static int NameCount = 0;
static const char* _stdcall SGetModuleName()
{
//SafeLog 作为C++项目的日志打印,编译定义在pch.cpp中,整个工程全局可用。
//调用的是C#的Log回调函数,打印字符串会通过C#回调函数显示在界面上
Instance->Count = NameCount++;
SafeLog("%s Count:%d", name,NameCount);
return name;
}
static int _stdcall SGetCount()
{
return NameCount;
}
//LogHandler 定义在pch.h中:typedef void (_stdcall * LogHandler)(const char*);
//__stdcall是Windows的回调函数的统一调用约定,被调用方清理堆栈
//
void (_stdcall ExSetLogHandler)(LogHandler handler)
{
//Log定义在pch.cpp中,作为全局的Log函数指针
Log =(handler);
}
static void (_stdcall SSetLogHandler)(LogHandler handler)
{
char temp[256];
sprintf_s(temp, "Log Handler %p", handler);
Log = handler;
SafeLog(temp);
}
NativeManager::NativeManager()
{
SafeLog("NativeManager()");
GetModuleName = SGetModuleName;
SetLogHandler = SSetLogHandler;
TestManager = new CTest