CPP和C#交互
CPP和NET交互
关键词
- C++/CLI
- 托管调用C++
C++调用C#
C++要添加CLR支持(公共语言运行支持)
,然后添加引用即可
https://www.jianshu.com/p/6e956c44eace/
C#调用C++(导出语法)
后续章节都是描述这个
导出
extern"C" __declspec(dllexport)
方式
-
非托管的形式,即最原始的方式,
C++
的代码编译还是C++
,内存管理由C++
处理 -
托管的形式,
C++
的代码最终编译为NET
的方式,一般为C++原始动态库
+CLI中间胶水库
+NET程序
,CLI中间胶水库
由C++编写
public ref class 定义导出的类
原始方式
参考链接 https://www.cnblogs.com/lgyup/p/7116162.html
公共方法导出
C++中
extern "C" __declspec(dllexport) int __stdcall Add(int n1, int n2);
C#中
[DllImport("SampleCppWrapper.dll")]
private static extern int Add(int n1, int n2);
类导出(只能转换,实际还是函数导出)
// 原始的C++类导出
class __declspec(dllexport) SampleCppClass
{
public:
SampleCppClass(void);
~SampleCppClass(void);
int Add(int n1, int n2);
int Sub(int n1, int n2);
};
// 包装的类导出,SampleCppWrapper.h
#include "..\SampleCppClass\SampleCppClass.h"
namespace SampleCppWrapper
{
extern "C" __declspec(dllexport) int __stdcall Add(int n1, int n2);
extern "C" __declspec(dllexport) int __stdcall Sub(int n1, int n2);
}
//SampleCppWrapper.cpp
#include "SampleCppWrapper.h"
namespace SampleCppWrapper
{
SampleCppClass* g_pObj = new SampleCppClass();
int __stdcall Add(int n1, int n2)
{
return g_pObj->Add(n1, n2);
}
int __stdcall Sub(int n1, int n2)
{
return g_pObj->Sub(n1, n2);
}
}
// C# 使用
[DllImport("SampleCppWrapper.dll")]
private static extern int Add(int n1, int n2);
[DllImport("SampleCppWrapper.dll")]
private static extern int Sub(int n1, int n2);
回调函数
C#
// C# 准备被C++ 回调的
private static void LoopCallback(IntPtr pContext)
{
Context ctx = (Context)Marshal.PtrToStructure(pContext, typeof(Context));
ctx.Form.textBox1.Text += "callback" + Environment.NewLine;
}
// 定义回调的类型
private delegate void LoopCallbackHandler(IntPtr pContext);
// 具体的回调的函数变量为callback
private static LoopCallbackHandler callback = LoopCallback;
// 把c#的句柄通过C++的接口告诉C#
SetCallbackFunc(callback); // 这个是C++ 提供的设置回调的接口
// 设置回调的参数,参数最终传递给C++的是指针
private Context ctx = new Context();
ctx.Form = this;
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(ctx));
Marshal.StructureToPtr(ctx, ptr, false);
// 设置回调的参数
SetCallbackContext(ptr);
// C++的回调的形式,参数是void* 这个是cpp 代码
void LoopCallbackFunc(void* pContext)
{
if (g_callbackWrapper != NULL)
{
g_callbackWrapper(pContext);
}
}
托管方式
-
快速入门尝试 https://blog.csdn.net/Isaac320/article/details/117709136
-
github https://github.com/tomorrowGooddays/Invoke 我最终对比了源码工程
-
如何在托管C++代码中混合托管和非托管代码 https://blog.csdn.net/zhaijingkui/article/details/103760900
-
互操作性,参数转换
https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal?view=net-5.0
-
调用 https://docs.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke
- 新建通用库
- 增加公共语言支持
- 选择正确的平台
- 取出
dllmain
FAQ
-
Unhandled exception. System.BadImageFormatException: Could not load file or assembly 'DLLMYX86, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. 试图加载格式不正确的程序。 File name: 'DLLMYX86, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' ---> System.BadImageFormatException: 试图加载格式不正确的程序。 (0x8007000B) at MYNETX86.Program.Main(String[] args)
这个问题首先排除X86和X64的问题,排除net和cpp的问题,然后就是下面的问题,注释掉dllmain
-
警告 : 正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码
注释掉dllmain函数,或者 #pragma unmanaged 标记入口点函数 然后再恢复
正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码 解决方法 调用托管“entrypoint”: 托管代码可能未运行在加载程序锁下,包括 DLL 入口点和从 DLL 入口点访问到的调用 编译器找到编译为 MSIL 的(可能)DLL 入口点。 由于加载入口点已编译为 MSIL 的 DLL 时存在一些潜在问题,因此强烈建议不要将 DLL 入口点函数编译为 MSIL。 有关更多信息,请参见 混合程序集的初始化 和 链接器工具错误 LNK1306。 更正此错误 不要使用 /clr 编译该模块。 使用 #pragma unmanaged 标记入口点函数。 dllmain DllMain 函数是 DLL 的用户定义入口点。 除非用户另外指定,否则,每当进程或线程附加到包含 DLL 或从包含 DLL 中分离时,都调用 DllMain。 由于这种调用可以在加载程序锁被保留时发生,因此不应将用户提供的 DllMain 函数编译为 MSIL。 另外,以 DllMain 为根的调用树中的函数不能编译为 MSIL。 若要在此处解决问题,则应使用 #pragma unmanaged 来修改定义 DllMain 的代码块。 对于由 DllMain 调用的每个函数,应执行同样的操作。 如果这些函数必须调用的某个函数需要一个用于其他调用上下文的 MSIL 实现,可以使用一个会创建同一函数的 .NET 版本和本机版本的复制策略。 或者,如果不需要 DllMain,或者不需要在有加载程序锁时执行它,则可以删除用户提供的 DllMain 实现,这便可以消除此问题。 如果 DllMain 尝试直接执行 MSIL,则会导致 编译器警告(等级 1)C4747。 但是,编译器无法检测到这样的情况:即 DllMain 调用另一个模块中的一个函数,该函数再尝试执行 MSIL。 原文链接:https://blog.csdn.net/qq_39008744/article/details/102929349
语法交互
https://docs.microsoft.com/zh-cn/cpp/dotnet/how-to-use-tracking-references-in-cpp-cli?view=msvc-170
% 引用传递
^ 托管内存
public ref C++的类导出到C#
导出的头文件
#include <msclr/marshal_cppstd.h>
using namespace msclr::interop;
using namespace System;
C# 速记
使用list 可以扩展
使用 array 实际是重新建立一个内存块