C++如何通过C++/CLI与.NET交互?(代码示例)

C++/CLI是微软提供的混合编程语言扩展,支持原生C++与.NET双向调用,编译生成托管IL和原生机器码,由CLR统一管理;需启用/clr选项,仅支持.NET Framework或.NET 6+桌面场景。

使用C++/CLI可以在原生C++代码中直接调用.NET类库,也能让.NET代码调用C++逻辑。它不是单纯的“桥接工具”,而是微软提供的混合编程语言扩展,编译后生成托管IL代码(对.NET部分)和原生机器码(对native部分),由CLR统一管理。

启用C++/CLI支持

在Visual Studio中创建项目时需选择“CLR空项目”或手动修改项目属性:

  • 项目属性 → 配置属性 → 常规 → “公共语言运行时支持”设为“/clr”
  • 确保目标平台与引用的.NET程序集一致(如x64或x86,.NET Core/.NET 5+暂不支持C++/CLI,仅支持.NET Framework或.NET 6+的“带运行时”的桌面场景)

从C++/CLI调用.NET类(托管代码)

以下示例在C++/CLI中创建一个.NET StringBuilder,拼接字符串并返回结果:

#include "stdafx.h"
#include 
using namespace System;
using namespace System::Text;

// 托管函数:返回托管字符串 String^ BuildHelloWorld() { StringBuilder^ sb = gcnew StringBuilder(); sb->Append("Hello"); sb->Append(" "); sb->Append("World"); return sb->ToString(); }

// 原生C++可调用的包装函数(返回C风格字符串) const char GetHelloWorldNative() { String^ managedStr = BuildHelloWorld(); // 将托管字符串转为UTF8原生字符串 array^ bytes = System::Text::Encoding::UTF8->GetBytes(managedStr); pin_ptr pinned = &bytes[0]; static std::string cachedResult; cachedResult.assign(reinterpret_cast>(pinned), bytes->Length); return cachedResult.c_str(); }

注意:gcnew分配托管对象,pin_ptr防止GC移动内存,避免指针失效;static std::string用于缓存转换结果(因返回const char*需保证生命周期)。

从.NET调用原生C++函数

先写一个纯原生C++函数(放在 .cpp 文件中,不带 /clr 编译):

// native_math.cpp(不启用/clr)
extern "C" __declspec(dllexport) double Add(double a, double b) {
    return a + b;
}

再在C++/CLI中封装为托管接口供C#调用:

// wrapper.h
#pragma once
#include "native_math.h"
using namespace System;

public ref class MathWrapper { public: static double Add(double a, double b) { return ::Add(a, b); // 直接调用原生函数 } };

C#中即可这样使用:

// C# 代码
var result = MathWrapper.Add(3.5, 4.2); // 输出 7.7

传递复杂数据(如List

C++/CLI可双向转换原生容器与.NET集合:

#include 
using namespace System::Collections::Generic;

// 原生vector → .NET List List^ ToDotNetList(const std::vector& v) { auto list = gcnew List(v.size()); for (size_t i = 0; i < v.size(); ++i) { list->Add(v[i]); } return list; }

// .NET List → 原生vector std::vector ToStdVector(List^ list) { std::vector v(list->Count); for (int i = 0; i < list->Count; ++i) { v[i] = list[i]; } return v; }

注意:避免在循环中频繁访问 list[i](.NET索引器有开销),可用 list->ToArray() + pin_ptr 批量复制提升性能。

不复杂但容易忽略:C++/CLI项目必须引用对应.NET Framework版本(如v4.7.2),且所有依赖的DLL需在运行时路径中;调试时混合堆栈可见,但托管异常需用 try/catch (System::Exception^) 捕获,不能用原生 try/catch。