C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ .NET互相调用

C++与.NET之间的互相调用的几种常见方式

作者:code36

C++与.NET之间的互操作性是开发混合应用程序时的常见需求,包括C++/CLI,P/Invoke,CLRHosting,这三种方法适用于不同场景的集成需求,感兴趣的可以了解一下

C++ 与 .NET 之间的互操作性是开发混合应用程序时的常见需求。下面我将展示几种常见的互操作方式和示例:

1. 使用 C++/CLI 作为桥梁

C++/CLI 是微软提供的一种中间语言,专门用于连接原生 C++ 和 .NET 代码。这是实现互操作性最灵活的方式。

cpp-dotnet-interop C++/CLI 互操作代码示例

NativeLibrary.h - 原生 C++ 库

// NativeLibrary.h - 原生 C++ 库
#pragma once

extern "C" {
    __declspec(dllexport) int Add(int a, int b);
    __declspec(dllexport) void* CreatePerson(const char* name, int age);
    __declspec(dllexport) const char* GetPersonName(void* person);
    __declspec(dllexport) int GetPersonAge(void* person);
    __declspec(dllexport) void DestroyPerson(void* person);
}

NativeLibrary.cpp - 原生 C++ 库实现

// NativeLibrary.cpp - 原生 C++ 库实现
#include "NativeLibrary.h"
#include <string>
#include <memory>

// 简单的 Person 类
struct Person {
    std::string name;
    int age;
};

int Add(int a, int b) {
    return a + b;
}

void* CreatePerson(const char* name, int age) {
    auto person = new Person{name, age};
    return person;
}

const char* GetPersonName(void* person) {
    return reinterpret_cast<Person*>(person)->name.c_str();
}

int GetPersonAge(void* person) {
    return reinterpret_cast<Person*>(person)->age;
}

void DestroyPerson(void* person) {
    delete reinterpret_cast<Person*>(person);
}

BridgeLibrary.h - C++/CLI 桥接头文件

// BridgeLibrary.h - C++/CLI 桥接头文件
#pragma once

using namespace System;

namespace BridgeLibrary {
    public ref class Calculator {
    public:
        static int Add(int a, int b);
    };

    public ref class PersonWrapper {
    private:
        void* nativePerson;

    public:
        PersonWrapper(String^ name, int age);
        ~PersonWrapper();
        !PersonWrapper();

        String^ GetName();
        int GetAge();
    };
}

BridgeLibrary.cpp - C++/CLI 桥接实现

// BridgeLibrary.cpp - C++/CLI 桥接实现
#include "BridgeLibrary.h"
#include "../NativeLibrary/NativeLibrary.h"
#include <msclr/marshal_cppstd.h>

namespace BridgeLibrary {
    int Calculator::Add(int a, int b) {
        return ::Add(a, b);
    }

    PersonWrapper::PersonWrapper(String^ name, int age) {
        msclr::interop::marshal_context context;
        const char* nativeName = context.marshal_as<const char*>(name);
        nativePerson = ::CreatePerson(nativeName, age);
    }

    PersonWrapper::~PersonWrapper() {
        this->!PersonWrapper();
    }

    PersonWrapper::!PersonWrapper() {
        if (nativePerson != nullptr) {
            ::DestroyPerson(nativePerson);
            nativePerson = nullptr;
        }
    }

    String^ PersonWrapper::GetName() {
        const char* nativeName = ::GetPersonName(nativePerson);
        return gcnew String(nativeName);
    }

    int PersonWrapper::GetAge() {
        return ::GetPersonAge(nativePerson);
    }
}

DotNetClient.cs - .NET 客户端调用示例

// DotNetClient.cs - .NET 客户端调用示例
using System;
using BridgeLibrary;

class Program {
    static void Main() {
        // 调用 C++ 计算函数
        int result = Calculator.Add(5, 3);
        Console.WriteLine("5 + 3 = " + result);

        // 使用 C++ 中的 Person 类
        using (var person = new PersonWrapper("John Doe", 30)) {
            Console.WriteLine("Name: " + person.GetName());
            Console.WriteLine("Age: " + person.GetAge());
        }
    }
}

2. 使用 P/Invoke 从 .NET 调用原生 C++

如果不想使用 C++/CLI,也可以通过 P/Invoke 直接从 .NET 调用原生 C++ 函数。

cpp-dotnet-pinvoke p/Invoke调用代码示例

NativeLibrary.h - 原生 C++ 库

// NativeLibrary.h - 原生 C++ 库
#pragma once

extern "C" {
    __declspec(dllexport) int Add(int a, int b);
    __declspec(dllexport) void* CreatePerson(const char* name, int age);
    __declspec(dllexport) const char* GetPersonName(void* person);
    __declspec(dllexport) int GetPersonAge(void* person);
    __declspec(dllexport) void DestroyPerson(void* person);
}

NativeLibrary.cpp - 原生 C++ 库实现

// NativeLibrary.cpp - 原生 C++ 库实现
#include "NativeLibrary.h"
#include <string>
#include <memory>

// 简单的 Person 类
struct Person {
    std::string name;
    int age;
};

int Add(int a, int b) {
    return a + b;
}

void* CreatePerson(const char* name, int age) {
    auto person = new Person{name, age};
    return person;
}

const char* GetPersonName(void* person) {
    return reinterpret_cast<Person*>(person)->name.c_str();
}

int GetPersonAge(void* person) {
    return reinterpret_cast<Person*>(person)->age;
}

void DestroyPerson(void* person) {
    delete reinterpret_cast<Person*>(person);
}

DotNetClient.cs - .NET 客户端 P/Invoke 调用示例

// DotNetClient.cs - .NET 客户端 P/Invoke 调用示例
using System;
using System.Runtime.InteropServices;

class Program {
    // 导入原生 C++ 函数
    [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Add(int a, int b);

    [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr CreatePerson(string name, int age);

    [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.LPStr)]
    public static extern string GetPersonName(IntPtr person);

    [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetPersonAge(IntPtr person);

    [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void DestroyPerson(IntPtr person);

    static void Main() {
        // 调用 C++ 计算函数
        int result = Add(5, 3);
        Console.WriteLine("5 + 3 = " + result);

        // 使用 C++ 中的 Person 类
        IntPtr personPtr = CreatePerson("John Doe", 30);
        try {
            string name = GetPersonName(personPtr);
            int age = GetPersonAge(personPtr);
            Console.WriteLine("Name: " + name);
            Console.WriteLine("Age: " + age);
        }
        finally {
            // 释放资源
            DestroyPerson(personPtr);
        }
    }
}

3. 从 C++ 调用 .NET 代码

从原生 C++ 调用 .NET 代码需要使用 CLR Hosting API。下面是一个简单的示例:

CppHost.cpp - C++ 调用 .NET 代码示例

// CppHost.cpp - C++ 调用 .NET 代码示例
#include <windows.h>
#include <metahost.h>
#include <string>
#include <iostream>

#pragma comment(lib, "mscoree.lib")

int main() {
    // 初始化 CLR
    ICLRMetaHost* pMetaHost = nullptr;
    ICLRRuntimeInfo* pRuntimeInfo = nullptr;
    ICLRRuntimeHost* pClrRuntimeHost = nullptr;

    HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost);
    if (FAILED(hr)) {
        std::cerr << "Failed to create CLR instance" << std::endl;
        return 1;
    }

    hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&pRuntimeInfo);
    if (FAILED(hr)) {
        std::cerr << "Failed to get runtime info" << std::endl;
        pMetaHost->Release();
        return 1;
    }

    BOOL fLoadable;
    hr = pRuntimeInfo->IsLoadable(&fLoadable);
    if (FAILED(hr) || !fLoadable) {
        std::cerr << "Runtime is not loadable" << std::endl;
        pRuntimeInfo->Release();
        pMetaHost->Release();
        return 1;
    }

    hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&pClrRuntimeHost);
    if (FAILED(hr)) {
        std::cerr << "Failed to get runtime host" << std::endl;
        pRuntimeInfo->Release();
        pMetaHost->Release();
        return 1;
    }

    hr = pClrRuntimeHost->Start();
    if (FAILED(hr)) {
        std::cerr << "Failed to start CLR" << std::endl;
        pClrRuntimeHost->Release();
        pRuntimeInfo->Release();
        pMetaHost->Release();
        return 1;
    }

    // 调用 .NET 方法
    DWORD pReturnValue;
    hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(
        L"DotNetLibrary.dll",
        L"DotNetLibrary.Calculator",
        L"Add",
        L"5,3",
        &pReturnValue);

    if (FAILED(hr)) {
        std::cerr << "Failed to execute .NET method" << std::endl;
    } else {
        std::cout << "Result from .NET: " << pReturnValue << std::endl;
    }

    // 清理资源
    pClrRuntimeHost->Stop();
    pClrRuntimeHost->Release();
    pRuntimeInfo->Release();
    pMetaHost->Release();

    return 0;
}

DotNetLibrary.cs - .NET 库示例

// DotNetLibrary.cs - .NET 库示例
namespace DotNetLibrary {
    public class Calculator {
        public static int Add(string args) {
            string[] numbers = args.Split(',');
            int a = int.Parse(numbers[0]);
            int b = int.Parse(numbers[1]);
            return a + b;
        }
    }
}

互操作方法比较

  1. C++/CLI

    • 优点:完全支持 .NET 功能,可直接访问 .NET 类库
    • 缺点:需要安装 .NET Framework/.NET Core
    • 适用场景:需要深度集成 .NET 功能的 C++ 应用
  2. P/Invoke

    • 优点:简单直接,不需要中间层
    • 缺点:类型映射复杂,不支持面向对象特性
    • 适用场景:从 .NET 调用简单的 C/C++ 函数
  3. CLR Hosting

    • 优点:允许原生 C++ 代码调用 .NET 代码
    • 缺点:实现复杂,性能开销大
    • 适用场景:需要在 C++ 应用中嵌入 .NET 功能

 到此这篇关于C++与.NET之间的互相调用的几种常见方式的文章就介绍到这了,更多相关C++ .NET互相调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文