C#调用C++动态库

这篇具有很好参考价值的文章主要介绍了C#调用C++动态库。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、序言

在C#开发过程中,我们可能会遇到需要调用Windows API 或是第三方库的场景,然而有时候这些库是由C++编写的,并不能直接应用在C#的程序中,这为开发带来许多阻力。本文介绍两种使用C#调用C++动态库的方式,以及在这个过程中可能遇到的问题,看完之后会对你的困境有所帮助。

二、非托管与托管

很多时候在项目中需要通过C++调用C#的dll,或者反过来调用。首先明白一个前提:C#是托管型代码。C++是非托管型代码。简而言之:

  • 托管型代码的对象在托管堆上分配内存,创建的对象由虚拟机托管。(C# )
  • 非托管型代码对象有实际的内存地址,创建的对象必须自己来管理和释放。(C++)

所以C#调用C++的动态库可以归类成两种办法:

  1. 非托管C++创建的dll库,需要用静态方法调用;
  2. 直接使用CLR,生成托管C++dll库。

接下来详细介绍具体的实现方式。

三、方法一:静态调用

1.加载第三方动态库

在使用C#时,在解决一些问题时会用到Windows API,这时你很容易看到如下的调用:


[DllImport("user32.dll")]
public static extern bool ReleaseCapture();

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.Button == MouseButtons.Left)
            {
                ReleaseCapture();
                SendMessage(Handle, 0x00A1, 2, 0);
            }
        }

这就是通过DLLImport来静态调用动态库中的方法,比较便捷,适用于一些简单的调用,但是缺点也很明显,想要使用相应的方法就得提前进行静态声明,而且不利于参数传递,也就是说如果参数有在库中声明的结构体,不能直接被静态方式引用,你必须在库中声明一个公共类做为接口来暴露数据。而有时我们使用的DLL是第三方的工具,不方便自己开源修改。

2.自定义动态库

有时候我们需要自己编写动态库来调用,就可以参考如下形式:

例子:新建C++项目,创建动态链接库(DLL),然后添加头文件textdll.h

#pragma once
#ifdef A_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

extern "C" DLL_API void MessageBoxShow();   //通过extern “C” 使MessageBoxShow方法为一个导出方法,外部可见

这一步是要在头文件中声明接口,之后需要实现相应的函数:

#include "stdafx.h"
#include "textdll.h"
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

void MessageBoxShow()
{
    MessageBox(NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}

我们编译之后,在目录下会生成 一个DLL(.dll为后缀的文件),我们复制到C#的debug目录下,使用C#程序静态调用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;  //必须要添加该引用
using System.Text;
using System.Threading.Tasks;

namespace dlltest
{
    class Program
    {
        [DllImport("TESTDLL.dll")]  //最关键的,导入该dll
        public extern static void MessageBoxShow();

        static void Main(string[] args)
        {
            MessageBoxShow();
        }
    }
}

但是这样做在很多时候不太方便,如果想要知道库中具体有哪些函数,还需要去翻阅文档或者源码,调用前还需要逐个进行静态的声明,所以接下来引用第二种方法。

四、方法二:托管C++动态库

通过C++/CLI作为中间层,因为C++/CLI既可调用.NET的类库,又可调用C++原生库,

所以可以通过C# 调用 C++/CLI 再调用 C++ DLL这样的三层调用方式完成。

1.动态库

首先需要编写一个DLL,可以是自己编写的动态库也可以是第三方的动态库,但是很重要一点是,在函数中需要有如下声明才能被后续引用:


_declspec(dllexport) int sum(int a, int b);//给c++调用
 
extern "C" __declspec(dllexport) int sum(int a, int b);//给c#调用

如果是自己编译则在VS中选择创建DLL项目,如何编写动态库这里就不赘述:

c#调用c++动态库,C#,c#,c++,开发语言

2.新建CLR项目

1.创建

打开VS,创建一个CLR的空项目:

c#调用c++动态库,C#,c#,c++,开发语言
从描述中就可以看到CLR本身就是打通.NET与C++代码的桥梁。

2.配置

以实现HID相关的USB功能为例,需要使用到一个hid的动态库,在创建完项目后打开项目属性,先引用第三方的DLL:
c#调用c++动态库,C#,c#,c++,开发语言

这个库目录中需要包含同名的相关文件:
c#调用c++动态库,C#,c#,c++,开发语言

同时引用库:
c#调用c++动态库,C#,c#,c++,开发语言
3.方法
然后新建一个类文件来编写相关方法:
Cli.h

#pragma once
#include "hidapi.h"

public ref class DFUCli
{
public:
	hid_device* handle;

	DFUCli();

	int Openhid();

	void test();

};

Cli.cpp

#include "DFUCli.h"

DFUCli::DFUCli()
{
}

int DFUCli::Openhid()
{
	uint16_t pid = 0x0000;
	uint16_t vid = 0x0000;
	
	handle = hid_open(vid, pid, NULL);
	if (handle == NULL) {
		return -1;
	}
	return 0;
}

void DFUCli::test()
 {
}

编写完成之后进行编译,此处需要更改生成类型为动态库:
c#调用c++动态库,C#,c#,c++,开发语言
这样我们就得到一个CLR的动态库文件。

3.C#引用DLL间接调用方法

新建一个C#工程,引用刚刚生成的DLL:
c#调用c++动态库,C#,c#,c++,开发语言
这时我们就能像使用普通的动态库一样调用相关方法,实例化对象之后,VS还会自动提示相应的函数,无须其他多余操作:
c#调用c++动态库,C#,c#,c++,开发语言

五、问题点汇总

当然由于这种多级的调用,经常会由于错误的操作或者配置导致一些未知的错误,下面整理了一些调试过程中遇到的问题,可能对你有所帮助:

1.fatal error LNK1561: 必须定义入口点

出现这个错误是由于我们创建的DLL是并没有入口函数,也就是main函数,而编译器按常规流程处理引发错误,有两种解决方式。

(1)设置子系统,解决方案上,右键->属性->链接器->系统->子系统,下拉框选择:窗口 (/SUBSYSTEM:WINDOWS)

c#调用c++动态库,C#,c#,c++,开发语言

(2)设置入口点,解决方案上,右键->属性->链接器->高级->入口点,设置成:WinMainCRTStartup

c#调用c++动态库,C#,c#,c++,开发语言

2.未能加载文件或程序集“XXX.dll”或它的某个依赖项的解决方法

这个问题有时候比较隐蔽,正常情况下应用程序查找依赖的dll时,顺序为先查找程序exe的输出路径,如果没有找到,那么会去C:\Windows\System32或System64文件夹中查找。

这种情况下我们只需要添加相关的dll到输出文件夹下就能解决错误,但是当你添加了对应的依赖项之后仍然报相关错误:
c#调用c++动态库,C#,c#,c++,开发语言
这时候你可能觉得无法理解从而陷入误区,然而事实上在这种多级的引用中,有可能你当前直接引用了A.dll,然后A.dll是一个CLR动态库引用了B.dll。如果A.dll找到了,但是A.dll依赖的B.dll没有找到,也会报上述错误,但这时它有可能只会提示没有找到A.dll。所以你需要把相关的依赖项全部都放在一个输出文件夹中来避免这种情况,当然也有可能你不清楚你所使用的动态库是否引用了第三方DLL,此时我们可以用Visual Studio中的dumpbin.exe工具查找A.dll的依赖项。

3.所生成项目的处理器框架“MSIL”与引用“xxx”的处理器架构“AMD64”不匹配

这是由于你的C#程序与动态库的生成配置不同导致的,将两者进行统一即可解决:
c#调用c++动态库,C#,c#,c++,开发语言

4.System.EntryPointNotFoundException: '无法在 DLL“xxx.dll”中找到名为“xx”的入口点。

在系统内的函数名称不对,遇到这个问题可以用工具查看内部名称,发觉函数名称并不是 “xx” 而是类似“?xx@@YAHHH@Z”这种不合法的形式。

原因为dll导出的格式不对,给c#调用时需要增加extern “C”,标准C格式。


_declspec(dllexport) int sum(int a, int b);//给c++调用
 
extern "C" __declspec(dllexport) int sum(int a, int b);//给c#调用

这一点在前文生成托管动态库中提到过。

5.错误 LNK2019 无法解析的外部符号

这个如果是运行时报错,常见的原因是你引用了某个库的函数,然后也正确添加了它的头文件路径,vs在写代码阶段可以找到这个函数的定义,但是,由于你没有添加或者正确设置这个库的lib或者dll路径的话,那么vs就会在运行时候报错无法解析的外部符号。库目录(lib文件目录)是在项目->属性->配置属性→VC++目录→库目录里进行添加,这一点在前文配置CLR项目中有详细步骤。

第二种可能是类似第4点错误,你没有正确的设置导出格式,需要增加相应的定义。

同样的第3点错误也可能导致这个问题。

除了前几种可能,还有一点就是lib的覆盖问题,引用动态库,在项目生成中如果更改lib生成路径,而正好引用该lib的项目又同时包含了原来的和重新生成的两个lib路径,编译器就有可能链接的还是原来的lib,就会导致这个问题。

综上所述,出现这个问题除了低级的拼写错误外,无非就是没有正确的引用相关的依赖文件,或是程序和他所引用的依赖同时引用了相关依赖但是版本不一致。

6.CS0570 '现用语言不支持xxx

这个比较常见是dll的问题,解决方案中有一个类库项目,原来是引用的dll文件,后来又在本解决方案中建了一个一样的类库项目,有的项目直接引用的是这个类库项目,有的又是引用的dll文件。

后来统一引用类库项目就能编译成功了。

还有一种可能则是函数的参数不兼容,假设你引用了一个MFC项目需要输入一个CString类型的参数,而C#中的String类型不能替代或是强制转换也会出现问题。

六、总结

使用动态库让我们的程序有了更多可能,同时可以兼容不同的语言,但是在引用过程中容易出现各种问题,特别要遵循一定的规范,否则在复杂操作时会有意想不到的问题。但是使用得当会是不可多得的助力!文章来源地址https://www.toymoban.com/news/detail-559095.html

到了这里,关于C#调用C++动态库的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++调用C# dll成功示例

    一.准备C# dll类库 。生成CSLib.dll  二、写C++应用调用  1)需要把dll复制到运行目录下。(不然会提示找不到dll System.IO.FileNotFoundException:“未能加载文件或程序集“CSLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”或它的某一个依赖项。系统找不到指定的文件) 2)设置\\\"公共语言运

    2024年02月14日
    浏览(61)
  • C#调用C++类,托管C++方式实现(创建C++ CLR dll项目)

            由于C#编写的是托管代码,编译生成微软中间语言,而C++代码则编译生成本地机器码(这种C++也有叫做本地C++或者非托管C++,VC6.0就是用于开发非托管C++代码的平台),这两种语言进行混合编程就存在一定困难。比较常用的方法是使用DllImport的方法,这种方法在网

    2024年02月07日
    浏览(47)
  • Unity Native Plugin C#和C++互相调用

    官方链接 1.DLL的方式: C++代码:编译成DLL,导入Unity C#代码: 2.还有一种是C++源码作为插件,只支持il2cpp。 C++代码:直接放到Unity的Assets目录下 C#源文件:区别只在导入时不写具体的文件名,写:__Internal即可,因为用IL2CPP 后端的方式,会把C++源文件放到工程内部一块编译。

    2024年02月12日
    浏览(44)
  • C#调用OpenCV(C++原版)思路和实现方法(小白教程)

    原本想着用OpenCV的话,只需考虑在编程语言环境下调用对应的库(包)就行了,因为之前也是做了一个利用python(OpenCV-python接口)下编写了一个停车场空位识别项目,但是没接触过实际的我,天真地认为这些语言封装库接口已经可以实现原本OpenCV的所有功能,但是看了面试题

    2024年02月02日
    浏览(37)
  • C++和C#程序语言的区别

    一直学习C++和C#,两者之间的区别总结一下 目录 一、两种语言概述 C++语言 C#语言 二、两种语言对比 2.1

    2024年02月09日
    浏览(42)
  • C#编程语言的优势与C++对比

           C#语言是由 C/C++演变而来的,是微软推出的一种基于.NET框架的、面向对象的高级编程语言。以.NET框架类库作为基础,拥有类似Visual Basic的快速开发能力。简单易学,入门超快,减少了烦人的指针,有统一的操作符/修饰符/运算符,使用起来极其舒心。         对于

    2024年02月09日
    浏览(43)
  • EtherNet/IP开发:JAVA、C#和C++开发源代码

    示例使用VS2010开发EtherNet/IP 开发语言使用C++ C++开发出来的程序,提供C标准API后,将可以提供给Dlphi、Labview、VB、QT、C#、CC++、C#、Java等几乎所有编程语言使用。 ① 在底层我们将采用socket和多线程方式开发,在数据处理方面使用指针为主。 ② EtherNet/IP在设计之初就是为高效

    2024年01月21日
    浏览(61)
  • 0基础开发EtherNet/IP:协议格式,JAVA、C#、C++处理

    经过一阵倒腾,把CIP、Ethernet/ip协议搞到手 协议的概念和理论就不提及了,上网随便一搜索EtherNet/IP遍地都是。 直接将协议关键点列举出来吧。 更多协议资料 www.jngbus.com 通讯软件群 30806722 这里讲解的是TCP和UDP协议的格式,EtherNet/IP数据所处位置。 ① 具体内容需要大家自己进

    2024年01月21日
    浏览(41)
  • Java、Python、C++和C#的界面开发框架和工具的重新介绍

    好的,以下是Java、Python、C++和C#的界面开发框架和工具的重新介绍: Java界面开发: Swing: 是Java提供的一个基于组件的GUI工具包,可以创建跨平台的图形用户界面。它提供了丰富的组件和布局管理器,使得界面开发相对简单。Swing是Java AWT的继承者,支持更多的功能和外观。

    2024年02月02日
    浏览(39)
  • [软件工具]opencv-svm快速训练助手教程解决opencv C++ SVM模型训练与分类实现任务支持C# python调用

    opencv中已经提供了svm算法可以对图像实现多分类,使用svm算法对图像分类的任务多用于场景简单且对时间有要求的场景,因为opencv的svm训练一般只需要很短时间就可以完成训练任务。但是目前网上没有一个工具很好解决训练问题,大部分需要自己编程去实现训练任务,这个对

    2024年02月06日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包