C# 使用Dll的几种方法举例
使用 DLL(动态链接库)是 C# 开发中常见的任务之一。DLL 文件包含可以在运行时加载的代码和数据,允许程序共享功能和资源,降低程序的内存占用并促进代码的复用。本篇文章将深入探讨 C# 中使用 DLL 的多种方法,并提供相关代码示例。
1. 什么是 DLL
动态链接库(DLL)是一种包含可供多个程序同时使用的代码和数据的文件。它是在程序运行期间按需被加载进内存的,这意味着它们可以被动态链接和动态调用。这种机制不仅节约了内存,还促进了代码的复用和版本控制。
2. 在 C# 中使用 DLL 的动机
使用 DLL 的动机主要包括以下几个方面:
- 代码复用:将通用功能封装成 DLL 供多个项目使用。
- 减少应用程序大小:通过引用共享的库,而不是将所有代码包含在每个应用程序中。
- 模块化开发:使复杂的软件系统更易于管理和维护。
- 跨语言调用:从非托管代码(如 C/C++)中调用函数。
3. 通过 Visual Studio 引用 DLL
在 Visual Studio 中引用 DLL 是使用托管程序集最简单的方法。
创建和引用 DLL
-
创建 DLL 项目:
-
打开 Visual Studio,创建一个新的 C# 类库项目。
-
编写你的功能代码,如以下简单的数学库:
namespace MathLibrary {public class Calculator{public int Add(int a, int b){return a + b;}public int Subtract(int a, int b){return a - b;}} }
-
-
编译并生成 DLL。在解决方案资源管理器中,右键单击项目并选择“生成”选项。
-
在其他项目中引用该 DLL:
- 在需要使用该 DLL 的项目中右键点击“引用”,选择“添加引用”。
- 在“浏览”选项卡下找到生成的 DLL 文件并添加。
-
使用 DLL 中的类:
using MathLibrary;class Program {static void Main(){Calculator calc = new Calculator();Console.WriteLine($"Add: {calc.Add(10, 5)}");Console.WriteLine($"Subtract: {calc.Subtract(10, 5)}");} }
4. 使用 P/Invoke 调用非托管代码
Platform Invocation Services (P/Invoke) 提供了一种从 C# 调用非托管代码(如 C/C++)的方式。这个功能对于使用操作系统提供的 API 或者遗留的 C/C++ 库特别有用。
示例:调用 Windows API
假设我们需要调用 Windows API 中的 MessageBox
函数。
-
声明函数:
using System; using System.Runtime.InteropServices;class Program {[DllImport("user32.dll", CharSet = CharSet.Unicode)]public static extern int MessageBox(IntPtr hWnd, string text, string caption, int options);static void Main(){MessageBox(IntPtr.Zero, "Hello, World!", "My Box", 0);} }
-
关键点解析:
- 使用
DllImport
属性指示这是一个从非托管 DLL 调用的函数。 CharSet
被设置为Unicode
以处理字符编码。
- 使用
5. 使用 COM 对象
在 C# 中使用 COM 对象,需要通过运行时可调用包装器(RCW)来实现。Visual Studio 可以自动生成 RCW。
示例:使用 Microsoft Excel COM 对象
-
添加引用:
- 在项目中选择“添加引用”,找到“COM”选项卡。
- 添加“Microsoft Excel 16.0 Object Library”。
-
使用 Excel COM 对象:
using Excel = Microsoft.Office.Interop.Excel;class Program {static void Main(){Excel.Application xlApp = new Excel.Application();xlApp.Visible = true;Excel.Workbook workbook = xlApp.Workbooks.Add();Excel.Worksheet worksheet = (Excel.Worksheet)workbook.Worksheets[1];worksheet.Cells[1, 1] = "Hello, Excel!";workbook.SaveAs("Sample.xlsx");workbook.Close();xlApp.Quit();} }
-
注意事项:
- 使用完 COM 对象后,要调用
Quit()
方法并释放对象。这可以通过Marshal.ReleaseComObject
来实现以避免内存泄露。
- 使用完 COM 对象后,要调用
6. 使用反射加载 DLL
反射提供了在运行时动态加载和使用程序集的能力。这对于需要在程序执行时创建对象或调用方法的场景特别有用。
示例:动态加载 DLL
-
动态加载和调用方法:
using System; using System.Reflection;class Program {static void Main(){// 加载 DLLAssembly assembly = Assembly.LoadFrom("MathLibrary.dll");// 获取 Calculator 类型Type calculatorType = assembly.GetType("MathLibrary.Calculator");// 创建 Calculator 实例object calculatorInstance = Activator.CreateInstance(calculatorType);// 获取 Add 方法MethodInfo addMethod = calculatorType.GetMethod("Add");// 调用 Add 方法object result = addMethod.Invoke(calculatorInstance, new object[] { 10, 5 });Console.WriteLine($"Result of Add: {result}");} }
-
反射的优缺点:
- 优点:灵活,可以在运行时决定加载和调用哪一段代码。
- 缺点:性能开销较大,且在代码结构发生变化时可能导致运行时错误。
7. 实践示例与代码解析
让我们通过一个实际的项目来整理使用不同方式加载 DLL 的步骤。假设我们要开发一个图像处理程序,其核心功能由一个复杂的 C++ 库实现,而我们希望在 C# 中调用这个库。
C++ DLL 创建
以下是一个简单的 C++ 动态链接库示例,提供了图像转灰度的功能:
// ImageLibrary.cpp
#include "ImageLibrary.h"extern "C" __declspec(dllexport) void ToGrayscale(unsigned char* image, int width, int height)
{for (int i = 0; i < width * height * 3; i += 3){unsigned char gray = (unsigned char)(0.299 * image[i] + 0.587 * image[i + 1] + 0.114 * image[i + 2]);image[i] = image[i + 1] = image[i + 2] = gray;}
}
C# 调用 P/Invoke
在 C# 程序中调用上面的 C++ 函数:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;class Program
{[DllImport("ImageLibrary.dll", CallingConvention = CallingConvention.Cdecl)]public static extern void ToGrayscale(byte[] image, int width, int height);static void Main(){string inputImagePath = "input.jpg";string outputImagePath = "output.jpg";Bitmap bitmap = new Bitmap(inputImagePath);Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);int bytes = Math.Abs(bmpData.Stride) * bitmap.Height;byte[] rgbValues = new byte[bytes];IntPtr ptr = bmpData.Scan0;Marshal.Copy(ptr, rgbValues, 0, bytes);ToGrayscale(rgbValues, bitmap.Width, bitmap.Height);Marshal.Copy(rgbValues, 0, ptr, bytes);bitmap.UnlockBits(bmpData);bitmap.Save(outputImagePath);Console.WriteLine("Image converted to grayscale and saved as " + outputImagePath);}
}
8. 常见问题与解决方案
-
无法加载 DLL:
- 确保 DLL 文件位于应用程序的运行目录中。
- 检查 DLL 的依赖项是否都已正确安装。
-
调用函数失败:
- 检查 P/Invoke 声明和实际 DLL 函数签名的一致性。
- 确保数据类型之间的转换是正确的,如
int
、string
到非托管类型的映射。
-
内存泄露:
- 确保所有非托管资源都已正确释放,特别是在处理 COM 对象时。
9. 性能优化与注意事项
- 减少不必要的调用:频繁的 DLL 调用可能会导致性能问题,应尽量批量处理数据。
- 尽量使用托管代码:对于简单功能,优先考虑使用 C# 实现,以避免不必要的复杂性和错误。
- 缓存方法信息:在使用反射时,缓存好需要调用的方法和属性信息,以降低性能开销。
10. 总结
C# 使用 DLL 提供了灵活的代码重用和功能扩展的途径。从直接引用托管程序集,到通过 P/Invoke 调用非托管代码,再到使用 COM 对象和反射加载 DLL,每种方式都有其独特的应用场景和挑战。在实际开发中,选择合适的技术需要综合考虑项目的特性、性能要求和维护成本。通过深入理解这些技术实现的方法和注意事项,可以更好地在 C# 项目中运用 DLL 来实现复杂功能。
print("拥抱新技术才是王道!")
关注我,不迷路,共学习,同进步
关注我,不迷路,共学习,同进步
相关文章:

C# 使用Dll的几种方法举例
使用 DLL(动态链接库)是 C# 开发中常见的任务之一。DLL 文件包含可以在运行时加载的代码和数据,允许程序共享功能和资源,降低程序的内存占用并促进代码的复用。本篇文章将深入探讨 C# 中使用 DLL 的多种方法,并提供相关…...

什么是不同类型的微服务测试?
大家好,我是锋哥。今天分享关于【什么是不同类型的微服务测试?】面试题?希望对大家有帮助; 什么是不同类型的微服务测试? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 微服务架构中的测试可以分为多种类…...

Docker 拉取镜像时配置可用镜像源(包含国内可用镜像源)
在/etc/docker/daemon.json中写入如下内容(如果文件不存在请新建该文件): { "registry-mirrors":["https://registry.docker-cn.com"] } 重新加载 json 配置文件: sudo systemctl daemon-reload重启 docker 服务: sud…...

International Symposium on Artificial Intelligence Innovations
计算机科学(Computer Science): 算法、自动化软件工程、生物信息学和科学计算、计算机辅助设计、计算机动画、计算机体系结构、计算机建模、计算机网络、计算机安全、计算机图形学与图像处理、数据库与数据挖掘、数据压缩、数据加密、数字信号…...

Golang笔记_day10
Go面试题(三) 1、什么是channel,为什么它可以做到线程安全 在Go语言中,channel是一种类型,它可以用来在协程之间传递数据通过共享内存来通信: 通过共享内存来通信是指多个线程或进程直接访问相同的内存区域…...

mlir learn
https://github.com/j2kun/mlir-tutorial 学习这个项目 https://www.jeremykun.com/2023/08/10/mlir-getting-started/ get start 用我的mac编译一下试试看 然后遇到架构不对的问题 因为他的提交默认是x86 https://github.com/j2kun/mlir-tutorial/pull/1/commits/5a267e269d57…...

Windows安装RabbitMQ 4.0.2(图文教程)
本章教程,主要记录在Windows 10上RabbitMQ 4.0.2的安装过程。 一、下载安装包 1、官方下载(速度不稳定) Erlang:https://github.com/erlang/otp/releases/download/OTP-26.0/otp_win64_26.0.exe RabbitMQ 4.0.2:https://github.com/rabbitmq/rabbitmq-server/releases/do…...

分布式系统中为什么需要使用消息队列
本文转载自 linkedkeeper.com 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。 当今市面上有很多主流的消息中间件,如老牌的ActiveMQ、RabbitMQ&#…...

Linux环境配置(学生适用)
1.挑选最便宜的云服务器 如腾讯云服务器,华为云服务器,百度云服务器等等…… 2.找到你的云服务器实例,然后找到你的公网IP。 3.云服务器实例 ---更多 --- 重置root密码 (一定要重置) 4. 下载并安装 xshell 或者其他登陆软件 xshel…...

麦禾软件:Mac用户找免费开源工具的最佳选择
抖知书老师推荐: 麦禾软件已经成为众多Mac用户的必备平台,尤其对于那些经常寻找免费、开源、正版软件的用户来说,绝对是一个福音。随着科技的不断进步和用户需求的提升,安全、便捷的软件下载体验成为用户选择平台的核心标准。而…...

OpenCV4.8 开发实战系列专栏之 08 - 通道分离与合并
大家好,欢迎大家学习OpenCV4.8 开发实战专栏,长期更新,不断分享源码。 专栏代码全部基于C 与Python双语演示,专栏答疑群 请联系微信 OpenCVXueTang_Asst 本文关键知识点: OpenCV中默认imread函数加载图像文件&#…...

iOS 18.1 RC 版本发布,修复iPhone16随机重启、浏览视频卡顿等bug
今日,苹果发布 iOS 18.1 RC 版本升级,内部版本号为 22B82。 iOS 18.1 RC 也就是 iOS 18.1 准正式版,如果没有大的 Bug,这将是 iOS 18.1 正式版发布前最后一次更新,正式版预计下周向消费者推送。 该 RC 版除了为海外用…...

安装buildkit,并使用buildkit构建containerd镜像
背景 因为K8s抛弃Docker了,所以就只装了个containerd,这样就需要一个单独的镜像构建工具了,就用了buildkit,这也是Docker公司扶持的,他们公司的人出来搞的开源工具,官网在 https://github.com/moby/buildkit 简介 服务端为buildkitd,负责和runc或containerd后端连接干活,目前…...

maven jar包二进制文件 invalid stream header: EFBFBDEF 的错误
背景: 将jasper模板文件导入jar包后,生成文件报错 org.springframework.core.io.Resource resource new ClassPathResource("/template/XXXX.jasper");jasperPrint JasperFillManager.fillReport(resource.getInputStream(), paramentMap, …...

Git绑定Gitee或Github以及Git面试常见题
1.绑定Git或Gitee Git绑定Gitee或GitHub的过程相对简单,以下是详细的步骤说明: 一、绑定Gitee仓库 在Gitee上创建仓库 登录Gitee官网,点击首页右上角的“”号,选择“新建仓库”。在新建仓库页面,输入仓库的名称、描述…...

值得细读的8个视觉大模型生成式预训练方法
写在前面 大语言模型的进展催生出了ChatGPT这样的应用,让大家对“第四次工业革命”和“AGI”的来临有了一些期待,也作为部分原因共同造就了美股2023年的繁荣。LLM和视觉的结合也越来越多:比如把LLM作为一种通用的接口,把视觉特征…...

go 包相关知识
在Go语言中,包的引用和搜索路径是由环境变量GOPATH和GO111MODULE共同决定的。 GOPATH环境变量:这个变量定义了默认的工作目录,Go命令行工具将会在这个目录下查找包文件。这个目录通常包含三个子目录:src、bin和pkg。 src目录包含…...

机器学习:opencv--人脸检测以及微笑检测
目录 前言 一、人脸检测的原理 1.特征提取 2.分类器 二、代码实现 1.图片预处理 2.加载分类器 3.进行人脸识别 4.标注人脸及显示 三、微笑检测 前言 人脸检测是计算机视觉中的一个重要任务,旨在自动识别图像或视频中的人脸。它可以用于多种应用࿰…...

linux系统挂载硬盘
参考链接:https://www.cnblogs.com/wenhainan/p/12292823.html (1)lsblk命令查看磁盘挂载情况 (2)使用fdisk分区新磁盘 如果要分多个分区需指定每个分区的大小 (3)格式化新分区 mkfs命令格…...

MySQL实现主从同步
一、首先我们准备3台mysql 分别为: 主服务器:test-mysql-master,端口3306 从服务器:test-mysql-slave1,端口3307 从服务器:test-mysql-slave2,端口3308 注意:如果防火墙是开着的记得把关掉,并且重启docker…...

人工智能--数学基础
目录 编辑2.1 线性代数基础 2.2 微积分及优化理论 2.3 概率论与统计学 2.4 信息论简介 2.1 线性代数基础 线性代数是处理向量空间(包括有限维或无限维)以及这些空间上的线性映射的一门数学分支。在线性代数中,最核心的概念包括向量、矩…...

2024人工智能技术的普及 如何看待AI技术的应用前景
AI 技术的应用前景十分广阔,但也面临着一些挑战,以下是对其应用前景的一些看法: 积极方面: 多行业深度融合与效率提升5: 医疗领域:AI 在医疗影像分析、辅助诊断、疾病预测等方面具有巨大潜力。例如…...

日常记录:springboot 2.2.5 + es 6.8.12
前言 最近有用到搜索引擎的功能,这不得把之前的es过程实践一遍,但发现过程坎坷,因为版本太低了。 一、安装es 6.8.12 安装过程跟之前写那章日常记录:elasticsearch 在linux安装_elasticsearch linux安装-CSDN博客一样࿰…...

MySQL数据库备份与恢复详解
文章目录 一、为什么需要备份数据库?二、MySQL数据库的备份方式1. 逻辑备份2. 物理备份3. 二进制日志备份 三、恢复数据库1. 使用mysqldump备份文件恢复2. 使用物理备份恢复3. 使用二进制日志恢复 四、备份与恢复的最佳实践五、结语 在日常的数据库运维中࿰…...

10.22 MySQL
存储过程 存储函数 存储函数是有返回值的存储过程,存储函数的参数只能是in类型的。具体语法如下: characteristic 特性 练习: 从1到n的累加 create function fun1(n int) returns int deterministic begindeclare total i…...

「AIGC」n8n AI Agent开源的工作流自动化工具
n8n AI Agent 是一个利用大型语言模型(LLMs)来设计和构建智能体(agents)的工具,这些智能体能够执行一系列复杂的任务,如理解指令、模仿类人推理,以及从用户命令中理解隐含意图。n8n AI Agent 的核心在于构建一系列提示(prompts),使 LLM 能够模拟自主行为。 传送门→ …...

Android 中获取和读取短信验证码
方法一:通过 SMS Retriever API SMS Retriever API 是 Google 提供的一种安全的方式,可以从系统中获取不需要权限的短信验证码。这种方式不需要请求 READ_SMS 权限,非常适合处理短信验证码的情况。 1. 在 build.gradle 中添加依赖 dependen…...

SQL语句高级查询(适用于新手)
SQL查询语句的下载脚本链接!!! 【免费】SQL练习资源-具体练习操作可以查看我发布的文章资源-CSDN文库https://download.csdn.net/download/Z0412_J0103/89908378 本文旨在为那些编程基础相对薄弱的朋友们提供一份详尽的指南,特别聚…...

main.ts中引入App.vue报错,提示“Cannot find module ‘./App.vue’ or its corresponding type
原因 代码编辑器:vscode ,使用vue3,所以安装了 Volar 插件,可以使 vue 代码高亮显示,不同颜色区分代码块,以及语法错误提示等 提示:如果使用的是vue2,则使用 Vetur 插件࿱…...

Android15音频进阶之组音量调试(九十)
简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+…...