C# 与 C++ 互操作深度解析:四种关键技术实践
C# 和 C++ 的协同开发是现代软件工程中的常见需求——通过互操作技术,我们既能保留 C++ 的高性能与硬件级控制能力,又能享受 C# 的开发效率和强大的生态支持。下面介绍四种核心互操作技术及其代码实现:
一、平台调用 (P/Invoke) – 调用 C++ 原生函数
适用场景:直接调用现有 DLL 中的 C++ 函数
// C# 调用方
using System;
using System.Runtime.InteropServices;
class InteropDemo
{
// 声明 C++ 函数(约定使用 cdecl 调用规范)
[DllImport("NativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int AddNumbers(int a, int b);
[DllImport("NativeLib.dll", CharSet = CharSet.Ansi)]
private static extern void PrintMessage(string text);
public static void Main()
{
int sum = AddNumbers(15, 27); // 调用 C++ 加法函数
Console.WriteLine($"Sum from C++: {sum}");
PrintMessage("Hello from C#!"); // 传递字符串到 C++
}
}
对应的 C++ 实现 (NativeLib.cpp):
#include <iostream>
extern "C" __declspec(dllexport)
int AddNumbers(int a, int b) {
return a + b;
}
extern "C" __declspec(dllexport)
void PrintMessage(const char* text) {
std::cout << "C++ Received: " << text << std::endl;
}
关键技术点:
二、C++/CLI 桥接 – 托管与非托管的双向交互
适用场景:需要在两种代码间进行复杂对象交互
// C++/CLI 包装层 (.NET 程序集)
#pragma once
#include "NativeCalculator.h" // 原生 C++ 类
public ref class ManagedCalculator
{
public:
ManagedCalculator() : nativePtr(new NativeCalculator()) {}
~ManagedCalculator() { delete nativePtr; }
double Calculate(double x)
{
// 托管代码调用非托管代码
return nativePtr->Compute(x);
}
void SetCallback(Action<double>^ callback)
{
// 非托管代码回调托管委托
nativePtr->SetCallback(
gcnew NativeCallback([callback](double result) {
callback(result);
})
);
}
private:
NativeCalculator* nativePtr;
// 非托管类到委托的桥接
delegate void NativeCallback(double result);
NativeCallback^ managedDelegate;
};
原生 C++ 类 (NativeCalculator.h):
// 原生 C++ 类
class NativeCalculator {
public:
using Callback = void(*)(double);
void SetCallback(Callback cb) { callback = cb; }
double Compute(double x) {
double result = x * 2.5;
if(callback) callback(result); // 执行回调
return result;
}
private:
Callback callback = nullptr;
};
C# 调用代码:
var calc = new ManagedCalculator();
calc.SetCallback(result =>
Console.WriteLine($"C++ callback: {result}"));
double output = calc.Calculate(4.2);
Console.WriteLine($"Result: {output}");
/* 输出:
C++ callback: 10.5
Result: 10.5
*/
核心技术优势:
三、COM 互操作 – 集成传统组件
适用场景:调用现有 COM 组件或暴露 .NET 类为 COM
调用 COM 组件:
// 引入 COM 类型库 (如 EXCEL.EXE)
Type excelType = Type.GetTypeFromProgID("Excel.Application");
dynamic excel = Activator.CreateInstance(excelType);
excel.Visible = true;
dynamic workbook = excel.Workbooks.Add();
dynamic sheet = workbook.ActiveSheet;
sheet.Cells[1, 1] = "Data from C#";
Marshal.ReleaseComObject(sheet);
Marshal.ReleaseComObject(workbook);
excel.Quit();
暴露 .NET 类给 COM:
[ComVisible(true)]
[Guid("3AB1DF8C-71C1-4993-8CEF-FFDBFBB2A89E")]
public interface ICalc
{
double ProcessData(double input);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class DataProcessor : ICalc
{
public double ProcessData(double input) =>
Math.Sqrt(input) * 0.87;
}
关键步骤:
四、内存映射文件 – 高性能数据交换
适用场景:进程间大数据交换(避免封送开销)
C++ 生产者:
#include <Windows.h>
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0, 1024, // 1KB内存区
L"Global\\\\SharedMemory");
double* data = (double*)MapViewOfFile(
hMapFile, FILE_MAP_WRITE, 0, 0, 1024);
// 写入数据
data[0] = 3.1415926;
data[1] = 2.71828;
C# 消费者:
using var shm = MemoryMappedFile.OpenExisting(
"SharedMemory", MemoryMappedFileRights.Read);
using var accessor = shm.CreateViewAccessor(0, 1024);
accessor.Read(0, out double pi);
accessor.Read(8, out double e);
Console.WriteLine($"PI={pi}, E={e}");
性能关键:
五、技术选型决策树
关键注意事项
类型系统陷阱:
- C++ bool ≠ C# bool (前者是4字节,后者是1字节)
- 使用[MarshalAs(UnmanagedType.I1)]明确指定布尔类型
内存管理边界:
// P/Invoke内存回收示例
[DllImport("NativeLib.dll")]
private static extern IntPtr CreateBuffer(int size);
[DllImport("NativeLib.dll")]
private static extern void FreeBuffer(IntPtr ptr);
线程协同:
- COM 需初始化单线程单元([STAThread])
- C++/CLI 托管线程需附加到非托管环境
异常处理:
// C++ 端捕获托管异常
try { /* 调用.NET方法 */ }
catch (System::Exception^ ex) {
std::cerr << MarshalString(ex->Message);
}
通过合理的架构设计和精准的技术选型,C# 与 C++ 的互操作将成为构建高性能混合系统的利剑,在保持开发效率的同时突破纯托管环境的性能限制。
评论前必须登录!
注册