CPP和C#交互

CPP和NET交互

关键词

  • C++/CLI
  • 托管调用C++

C++调用C#

C++要添加CLR支持(公共语言运行支持),然后添加引用即可

https://www.jianshu.com/p/6e956c44eace/

C#调用C++(导出语法)

后续章节都是描述这个

导出

extern"C" __declspec(dllexport)

方式

  1. 非托管的形式,即最原始的方式,C++的代码编译还是C++,内存管理由C++处理

  2. 托管的形式,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);
	}
}

托管方式

  1. 新建通用库
  2. 增加公共语言支持
  3. 选择正确的平台
  4. 取出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 实际是重新建立一个内存块
posted @ 2022-06-17 13:08  zongzi10010  阅读(221)  评论(0编辑  收藏  举报