您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

如何在C#中调用C++编写的动态库?

时间:2023-06-08 12:36:13  来源:今日头条  作者:小乖兽技术

 

场景和优点

在以下场景下,可能会使用C#调用C++编写的dll:

  1. C++库已经存在并且经过了充分测试和验证,需要被C#项目重复使用时;
  2. C++编写的库中包含高性能计算、海量数据处理等需要使用底层语言实现的操作时,可以考虑将这些操作封装为动态链接库供C#调用;
  3. 在跨平台开发时,C++可在多个平台上运行,通过封装为dll,可以让C#项目也能够在多个平台上运行;
  4. 需要将不同的功能模块拆分成独立的组件,C++编写的dll可以作为一个独立的组件,供C#项目或其他语言的项目调用。

此外,使用C#调用C++编写的dll还有以下优点:

  1. C#具有较高的开发效率和易用性,通过调用C++编写的dll可以兼顾高性能和高开发效率。
  2. C#可以使用.NET Framework提供的强大工具和库,如LINQ、异步编程等等,这些工具和库可以提高开发效率,同时也可以利用C++的性能优势。
  3. C#可以与其他语言,如JAVAPython/ target=_blank class=infotextkey>Python等配合使用,借助各种技术,如SOAP、WCF、gRPC等实现多语言之间的互操作。
  4. C++作为一种系统级编程语言,可以访问系统底层资源,如内存、磁盘、网络等,C#调用C++编写的dll可以实现访问这些底层资源的功能,从而提供更多的功能。

C#调用C++编写的动态库的方式

在C#中调用C++编写的动态库有以下几种方式:

1. 使用DllImport特性

使用DllImport特性可以直接引入动态链接库中的C++函数,并在C#中进行调用。

下面是一个简单的示例:

首先,我们在C++中编写一个简单的dll,里面包含一个计算两数之和的函数addition:

c++Copy Code// file: mylib.cpp
#include "pch.h"
#include "mylib.h"

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

然后,我们在C++中将其封装为一个dll,并导出addition函数:

// file: mylib.h

#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif

extern "C" MYLIB_API int addition(int a, int b);  // export the function

接着,在C#项目中使用DllImport特性导入这个dll,并调用其中的函数:

using System.Runtime.InteropServices;

class Program
{
    [DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int addition(int a, int b);

    static void MAIn(string[] args)
    {
        int result = addition(1, 2);
        Console.WriteLine("The sum is: " + result);
    }
}

在上述示例中,我们使用DllImport特性声明了一个addition方法,将其与C++中的addition函数进行绑定。在Main函数中,我们调用了这个方法,并输出计算结果。

需要注意的是,在使用DllImport特性时,需要指定正确的dll名称和函数调用规约,否则可能会出现运行时错误。

2. 使用C++/CLI

 

另一种实现方式是使用C++/CLI(C++/Common Language Infrastructure)。

C++/CLI是一种结合了C++和CLR(Common Language Runtime)的语言,它可以编写针对.NET Framework/CLR的代码,同时也可以访问C++的底层资源。因此,我们可以使用C++/CLI来封装C++库,并将其作为dll供C#调用。

下面是一个简单的示例:

首先,在C++/CLI中编写一个类LibraryWrApper,里面包含一个使用C++库计算两数之和的方法Addition:

// file: LibraryWrapper.h
#pragma once

namespace MyLibrary {

    public ref class LibraryWrapper
    {
    private:
        Library* lib;  // the C++ object we want to wrap
    public:
        LibraryWrapper();  // constructor
        ~LibraryWrapper();  // destructor
        int Addition(int a, int b);  // method used to add two numbers
    };
}

其中,Library是我们需要封装的C++库中的一个类。

然后,在实现文件LibraryWrapper.cpp中实现类的构造函数、析构函数和Addition方法:

// file: LibraryWrapper.cpp
#include "pch.h"
#include "LibraryWrapper.h"
#include "Library.h"

using namespace MyLibrary;

LibraryWrapper::LibraryWrapper()
{
    lib = new Library();  // create a new Library object
}

LibraryWrapper::~LibraryWrapper() {
    delete lib;  // release the memory
}

int LibraryWrapper::Addition(int a, int b)
{
    return lib->addition(a, b);  // call the addition method in C++ library
}

这里我们实例化了一个C++库中的对象,然后在Addition方法中调用了它的addition方法。

最后,在C++/CLI项目中发布dll,并在C#项目中引用。在C#项目中,我们可以创建一个LibraryWrapper对象,并调用其中的Addition方法:

using System;
using System.Runtime.InteropServices;

namespace CppCLILibraryTest
{
    class Program
    {
        static void Main(string[] args)
        {
            MyLibrary.LibraryWrapper wrapper = new MyLibrary.LibraryWrapper();
            int result = wrapper.Addition(1, 2);
            Console.WriteLine("The sum is: " + result);
        }
    }
}

需要注意的是,当使用C++/CLI封装C++库时,我们需要确保两者所使用的Runtime是相同的。比如,如果C++库是使用静态连接的方式与CRT(C Runtime)链接的,那么我们需要在C++/CLI项目的属性中设置“/MT”选项,以保证代码使用相同的CRT版本。

3. 使用COM组件

 

另一种实现方式是使用COM组件。COM是微软推出的一种二进制接口标准,它可以让不同的应用程序之间以二进制码互相通信。

下面是一个简单的示例:

首先,在C++中编写一个简单的dll,里面包含一个计算两数之和的函数addition:

// file: MyLibrary.h
#pragma once

#ifdef MYLIBRARY_EXPORTS
#define MYLIBRARY_API __declspec(dllexport)
#else
#define MYLIBRARY_API __declspec(dllimport)
#endif

namespace MyLibrary {
    class MyMath {
    public:
        static int Addition(int a, int b);
    };
}

然后,我们将这个dll封装为一个COM组件。我们需要创建一个类,其中包含COM接口和类工厂:

// file: MathCOM.h
#pragma once

#include "MyLibrary.h"

class MathCOM : public IUnknown {
private:
    ULONG m_cRef;
public:
    MathCOM();
    ~MathCOM();

    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    // COM interface method
    STDMETHODIMP Addition(int a, int b, int* result);
};

class MathClassFactory : public IClassFactory {
private:
    ULONG m_cRef;
public:
    MathClassFactory();
    ~MathClassFactory();

    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    // IClassFactory methods
    STDMETHODIMP CreateInstance(IUnknown* pUnknownOuter, REFIID riid, void** ppvObject);
    STDMETHODIMP LockServer(BOOL fLock);
};

在实现文件MathCOM.cpp中,我们需要为这些接口方法提供具体的实现:

// file: MathCOM.cpp
#include "stdafx.h"
#include "MathCOM.h"

MathCOM::MathCOM() {
    m_cRef = 1;
}

MathCOM::~MathCOM() {}

STDMETHODIMP MathCOM::QueryInterface(REFIID riid, void** ppv) {
    *ppv = NULL;

    if (riid == IID_IUnknown || riid == IID_IDispatch)
        *ppv = this;

    if (*ppv != NULL) {
        ((LPUNKNOWN)*ppv)->AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) MathCOM::AddRef() {
    return InterlockedIncrement((LONG*)&m_cRef);
}

STDMETHODIMP_(ULONG) MathCOM::Release() {
    ULONG cRef = InterlockedDecrement((LONG*)&m_cRef);
    if (cRef == 0) delete this;
    return cRef;
}

STDMETHODIMP MathCOM::Addition(int a, int b, int* result) {
    *result = MyLibrary::MyMath::Addition(a, b);
    return S_OK;
}

MathClassFactory::MathClassFactory() {
    m_cRef = 1;
}

MathClassFactory::~MathClassFactory() {}

STDMETHODIMP MathClassFactory::QueryInterface(REFIID riid, void** ppv) {
    *ppv = NULL;

    if (riid == IID_IUnknown || riid == IID_IClassFactory)
        *ppv = this;

    if (*ppv != NULL) {
        ((LPUNKNOWN)*ppv)->AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) MathClassFactory::AddRef() {
    return InterlockedIncrement((LONG*)&m_cRef);
}

STDMETHODIMP_(ULONG) MathClassFactory::Release() {
    ULONG cRef = InterlockedDecrement((LONG*)&m_cRef);
    if (cRef == 0) delete this;
    return cRef;
}

STDMETHODIMP MathClassFactory::CreateInstance(IUnknown* pUnknownOuter, REFIID riid, void** ppvObject) {
    if (pUnknownOuter) return CLASS_E_NOAGGREGATION;

    MathCOM* pMathCOM = new MathCOM();
    if (!pMathCOM) return E_OUTOFMEMORY;

    HRESULT hResult = pMathCOM->QueryInterface(riid, ppvObject);
    pMathCOM->Release();
    return hResult;
}

STDMETHODIMP MathClassFactory::LockServer(BOOL fLock) {
    return S_OK;
}

在项目中使用C++编译器生成COM组件dll之后,在C#项目中使用COM互操作性来调用这个COM组件,代码如下:

using System.Runtime.InteropServices;

namespace COMTest
{
    [ComImport, Guid("B9D43B8A-61F3-4668-AB30-C2BE194AD0AA")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IMathCOM {
        [PreserveSig]
        int Addition(int a, int b, out int result);
    }

    [ComImport, Guid("8CFD0B22-24A3-4490-9127-9DB3FD53E15F")]
    class MathCOM { }

    class Program
    {
        static void Main(string[] args)
        {
            IMathCOM mathCOM = (IMathCOM)new MathCOM();
            int result = 0;
            mathCOM.Addition(1, 2, out result);
            Console.WriteLine("The sum is: " + result);
        }
    }
}

在这个示例中,我们声明了一个用来调用COM组件的接口IMathCOM,然后实例化MathCOM类并把它转换为IMathCOM类型,就可以调用其中的Addition方法了。

以上三种方式都可用于调用C++编写的动态库,选择使用哪种方式应该根据具体的场景和需求来决定。



Tags:C#   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
C# 中15个值得收藏的开源项目推荐
在开源的世界里,C# 编程语言也占有一席之地。这些开源项目涵盖了多个领域,从框架、库到工具,它们为C#开发者提供了丰富的资源和工具,帮助他们更高效地开发、测试和部署应用程序...【详细内容】
2024-03-20  Search: C#  点击:(30)  评论:(0)  加入收藏
C#异步编程:Task.Run vs. async-await,掌握基础与高级用法
概述:C#中的异步编程有两主要方式:Task.Run用于在后台线程执行同步操作,而async-await更适用于清晰表达异步流程。基础用法展示了它们的简单应用,高级用法则演示了它们的结合使...【详细内容】
2024-03-09  Search: C#  点击:(23)  评论:(0)  加入收藏
C# 线程本地存储为什么线程间值不一样
为什么用 ThreadStatic 标记的字段,只有第一个线程拿到了初始值,其他线程都是默认值,让我能不能帮他解答一下,尼玛,我也不是神仙什么都懂,既然问了,那我试着帮他解答一下,也给后面类...【详细内容】
2024-01-26  Search: C#  点击:(67)  评论:(0)  加入收藏
C# 登顶!超越Java或非空想
整理丨诺亚出品 | 51CTO技术栈(微信号:blog51cto)近日,TIOBE编程社区公布年度编程语言,此次摘得这一桂冠的是C#。这也是C#在TIOBE二十多年评选历史中首次赢得这一年度大奖。C#虽...【详细内容】
2024-01-15  Search: C#  点击:(114)  评论:(0)  加入收藏
C#进程间消息传递
C#是一种流行的编程语言,它可以用于开发Windows应用程序。在开发Windows应用程序时,有时需要进行进程间通信,以实现不同进程之间的数据传递和交互。C#提供了多种方式来进行进程...【详细内容】
2024-01-01  Search: C#  点击:(104)  评论:(0)  加入收藏
搞懂C#文件压缩:SharpZipLib vs. DotNetZip,实用代码一网打尽!
在C#中,有两个热门的文件压缩解析类库分别是SharpZipLib和DotNetZip。以下是它们的简要介绍以及使用实例代码。1. SharpZipLib功能: 支持ZIP和GZip格式的压缩和解压缩。 提供...【详细内容】
2023-12-31  Search: C#  点击:(11)  评论:(0)  加入收藏
探秘C#中的秘密通道:五种引人注目的方法调用内部或私有方法
在 C# 中,可以使用不同的方法调用内部或私有方法。下面分别介绍通过反射、MethodInfo.CreateDelegate、表达式(树)、动态方法(call)、动态方法(calli)这五种方法。1. 通过反射方法...【详细内容】
2023-11-24  Search: C#  点击:(20)  评论:(0)  加入收藏
C#参数传递
前几天一个学员在学习C#与参数传递交互时,也不知道参数传递可以用来做什么 。下面我们就详细讲讲C# 和参数传递交互的相关知识。C#是一种面向对象的编程语言,支持多种参数传...【详细内容】
2023-11-11  Search: C#  点击:(214)  评论:(0)  加入收藏
C#与高级控件
前几天一个学员在学习C#与高级控件交互时,也不知道高级控件可以用来做什么 。下面我们就详细讲讲C# 和高级控件交互的相关知识。C#是一种功能丰富的面向对象编程语言,它包含...【详细内容】
2023-11-10  Search: C#  点击:(256)  评论:(0)  加入收藏
如何在C#客户端程序中无缝集成Python算法
背景介绍在软件开发领域,C#是一种广泛应用的面向对象编程语言,具有强大的类型系统和丰富的库支持。它常被用于开发Windows桌面应用程序、Web应用程序和服务端应用程序等。然而...【详细内容】
2023-11-03  Search: C#  点击:(297)  评论:(0)  加入收藏
▌简易百科推荐
C++常见避坑指南
C++ 从入门到放弃?本文主要总结了在C++开发或review过程中常见易出错点做了归纳总结,希望借此能增进大家对C++的了解,减少编程出错,提升工作效率,也可以作为C++开发的避坑攻略。...【详细内容】
2024-04-03  腾讯技术工程    Tags:C++   点击:(4)  评论:(0)  加入收藏
C++ 之父反驳白宫警告:自诞生第一天起,C++ 的目标就一直是提高安全性
整理 | 郑丽媛上个月,美国白宫国家网络主任办公室(ONCD)在一份主题为《回到基础构件:通往安全软件之路》的 19 页 PDF 报告中,呼吁开发人员停止使用容易出现内存安全漏洞的编程语...【详细内容】
2024-03-25    CSDN  Tags:C++   点击:(4)  评论:(0)  加入收藏
八个 C++ 开源项目,帮助初学者进阶成长
通过参与或阅读开源项目的源代码,你可以获得丰富的实践机会。实际的项目代码比简单的教程更具挑战性,可以帮助你深入理解 C++ 的各种概念和技术。1.ThreadPool一个简单的 C++1...【详细内容】
2024-03-22  AI让生活更美好  微信公众号  Tags:C++   点击:(21)  评论:(0)  加入收藏
C# 中15个值得收藏的开源项目推荐
在开源的世界里,C# 编程语言也占有一席之地。这些开源项目涵盖了多个领域,从框架、库到工具,它们为C#开发者提供了丰富的资源和工具,帮助他们更高效地开发、测试和部署应用程序...【详细内容】
2024-03-20  程序员编程日记  微信公众号  Tags:C#   点击:(30)  评论:(0)  加入收藏
C#异步编程:Task.Run vs. async-await,掌握基础与高级用法
概述:C#中的异步编程有两主要方式:Task.Run用于在后台线程执行同步操作,而async-await更适用于清晰表达异步流程。基础用法展示了它们的简单应用,高级用法则演示了它们的结合使...【详细内容】
2024-03-09  架构师老卢  今日头条  Tags:C#   点击:(23)  评论:(0)  加入收藏
C++多线程编程:解锁性能与并发的奥秘
今天我们将深入探讨C++中的多线程编程,揭示多线程如何解锁性能潜力,提高程序的并发性能。什么是多线程?在计算机科学中,多线程是指一个进程(程序的执行实例)中的多个线程同时执行...【详细内容】
2024-02-03     AI让生活更美好  Tags:C++   点击:(69)  评论:(0)  加入收藏
C++代码优化攻略
今天我们将深入探讨C++性能优化的世界。在当今软件开发的浪潮中,高性能的代码是必不可少的。无论是开发桌面应用、移动应用,还是嵌入式系统,性能都是关键。1. 选择合适的数据结...【详细内容】
2024-01-26  AI让生活更美好  微信公众号  Tags:C++   点击:(114)  评论:(0)  加入收藏
C# 线程本地存储为什么线程间值不一样
为什么用 ThreadStatic 标记的字段,只有第一个线程拿到了初始值,其他线程都是默认值,让我能不能帮他解答一下,尼玛,我也不是神仙什么都懂,既然问了,那我试着帮他解答一下,也给后面类...【详细内容】
2024-01-26  一线码农聊技术  微信公众号  Tags:C#   点击:(67)  评论:(0)  加入收藏
C++质数检测器的设计与实现​
质数,作为数学中的一个基本概念,一直以其独特的性质吸引着众多研究者和爱好者。质数是指大于1的自然数中,除了1和它本身以外不再有其他因数的数。在实际应用中,质数检测也扮演着...【详细内容】
2024-01-15  鲨鱼编程  微信公众号  Tags:C++   点击:(111)  评论:(0)  加入收藏
C# 登顶!超越Java或非空想
整理丨诺亚出品 | 51CTO技术栈(微信号:blog51cto)近日,TIOBE编程社区公布年度编程语言,此次摘得这一桂冠的是C#。这也是C#在TIOBE二十多年评选历史中首次赢得这一年度大奖。C#虽...【详细内容】
2024-01-15    51CTO  Tags:C#   点击:(114)  评论:(0)  加入收藏
站内最新
站内热门
站内头条