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…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

