C#将dll打包到程序中

原文:C#将dll打包到程序中

最近比较懒,加上内容也不多就懒得排版了,字放大了,看起来应该方便一点

直接进入主题

先来看一个栗子,假设现在有一个第三方dll

namespace TestLibrary1
{
    public class Test
    {
        public void Point()
        {
            Console.WriteLine("aaabbbccc");
        }
    }
}

TestLibrary1.dll

在项目中引用,然后调用其中的方法Test,将输出aaabbbccc

using System;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var test = new TestLibrary1.Test();
            test.Point();
            Console.ReadLine();
        }
    }
}

效果

但是很显然,当你把程序发给你的客户的时候必须要携带一个dll,否则就会这样

当程序在运行中,某个程序集加载失败的时候 会触发  AppDomain.CurrentDomain.AssemblyResolve 事件
//
// 摘要:
//     在对程序集的解析失败时发生。
public event ResolveEventHandler AssemblyResolve;

在这个事件中,可以重新为加载失败的程序集手动加载

如果你将dll作为资源文件打包的你的应用程序中(或者类库中)

就可以在硬盘加载失败的时候 从资源文件中加载对应的dll

就像这样:

class Program
{
    static Program()
    {//这个绑定事件必须要在引用到TestLibrary1这个程序集的方法之前,注意是方法之前,不是语句之间,就算语句是在方法最后一行,在进入方法的时候就会加载程序集,如果这个时候没有绑定事件,则直接抛出异常,或者程序终止了
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        //获取加载失败的程序集的全名
        var assName = new AssemblyName(args.Name).FullName;
        if (args.Name == "TestLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
        {
            //读取资源
            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ConsoleApplication5.TestLibrary1.dll"))
            {
                var bytes = new byte[stream.Length];
                stream.Read(bytes, 0, (int)stream.Length);
                return Assembly.Load(bytes);//加载资源文件中的dll,代替加载失败的程序集
            }
        }
        throw new DllNotFoundException(assName);
    }
    //程序进入方法之前会加载程序集,当程序集加载失败,则会进入CurrentDomain_AssemblyResolve事件
    static void Main(string[] args)
    {
        var test = new TestLibrary1.Test();
        test.Point();
        Console.ReadLine();
    }
}

这样就软件以一个exe单独运行了

以上都是我网上看来了...................



 

不过如果我有很多dll怎么办,总不至于每一个dll写一个分支吧?

所以我准备写一个通用的资源dll加载类

 

原理蛮简单的,主要是通过StackTrace类获取调用RegistDLL方法的对象,获取到对方的程序集

然后通过Assembly.GetManifestResourceNames()获取所有资源的名称

判断后缀名".dll"(这一步可以自由发挥),然后加载,以加载的程序集的名称为key保存到一个字典中

并绑定AppDomain.AssemblyResolve事件

在程序集加载失败时,从字典中查询同名程序集,如果有,直接从字典中加载

代码如下:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace blqw
{
    /// <summary> 载入资源中的动态链接库(dll)文件
    /// </summary>
    static class LoadResourceDll
    {
        static Dictionary<string, Assembly> Dlls = new Dictionary<string, Assembly>();
        static Dictionary<string, object> Assemblies = new Dictionary<string, object>();

        static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
        {
            //程序集
            Assembly ass;
            //获取加载失败的程序集的全名
            var assName = new AssemblyName(args.Name).FullName;
            //判断Dlls集合中是否有已加载的同名程序集
            if (Dlls.TryGetValue(assName, out ass) && ass != null)
            {
                Dlls[assName] = null;//如果有则置空并返回
                return ass;
            }
            else
            {
                throw new DllNotFoundException(assName);//否则抛出加载失败的异常
            }
        }

        /// <summary> 注册资源中的dll
        /// </summary>
        public static void RegistDLL()
        {
            //获取调用者的程序集
            var ass = new StackTrace(0).GetFrame(1).GetMethod().Module.Assembly;
            //判断程序集是否已经处理
            if (Assemblies.ContainsKey(ass.FullName))
            {
                return;
            }
            //程序集加入已处理集合
            Assemblies.Add(ass.FullName, null);
            //绑定程序集加载失败事件(这里我测试了,就算重复绑也是没关系的)
            AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
            //获取所有资源文件文件名
            var res = ass.GetManifestResourceNames();
            foreach (var r in res)
            {
                //如果是dll,则加载
                if (r.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                {
                    try
                    {
                        var s = ass.GetManifestResourceStream(r);
                        var bts = new byte[s.Length];
                        s.Read(bts, 0, (int)s.Length);
                        var da = Assembly.Load(bts);
                        //判断是否已经加载
                        if (Dlls.ContainsKey(da.FullName))
                        {
                            continue;
                        }
                        Dlls[da.FullName] = da;
                    }
                    catch
                    {
                        //加载失败就算了...
                    }
                }
            }
        }
    }
}

LoadResourceDll

代码下载

时间: 2024-10-27 03:31:34

C#将dll打包到程序中的相关文章

嵌入Dll到.net程序中的方法

我们经常会写一些小程序给自己或者他人用,而这些程序时长又会去引用一些第三方的Dll,比如开源的 ICSharpCode.SharpZipLib.dll等,为了让程序保持整洁,或者给对方的时候方便,就想把这些dll给嵌入到EXE中去,这样在不打包的情况下,只要丢一个文件给对方就能用了.最近研究了下可行性,目前有如下两种方法: 方法1:把相关的第三方dll作为程序资源嵌入到EXE中,在程序运行的时候,从资源文件中输出到程序执行目录即可 (图1:示例项目,ThirdPartydlldemo.dll作为

在打包程序中自动安装SQL Server数据库 .

原文:在打包程序中自动安装SQL Server数据库 . 1.创建安装项目"Setup1"安装项目 在"文件"菜单上指向"添加项目",然后选择"新建项目". 在"添加新项目"对话框中,选择"项目类型"窗格中的"安装和部署项目",然后选择"模板"窗格中的"安装项目".在"名称"框中键入 "setup1

eclipse-java程序中注册系统级热键之dll问题

问题描述 java程序中注册系统级热键之dll问题 我下载了jintellitype-1.3.1.jar后,把JIntellitype.dll复制到了C:WINDOWSsystem32下,然后在eclipse工程名在点击右键--属性--Java构建路径--库--添加外部jar,然后把jintellitype-1.3.1.jar添加到工程中,编译后还是发生错误: 信息: Loading JIntellitype DLL Exception in thread "main" com.mel

C#如何从宿主程序中卸载dll插件?

问题描述 C#如何从宿主程序中卸载dll插件? 有没有大神来教教我啊,通过反射加载的插件,现在要卸载,如何实现啊?求详细教程 解决方案 很遗憾,没法卸载.不过C#可以以FileStream的方式加载插件,这样插件的 dll 可以删除.你主程序不再调用就是了.或者重启程序.

java web-在javaWeb程序中通过jacob调用dll出现Can&amp;amp;#39;t co-create object

问题描述 在javaWeb程序中通过jacob调用dll出现Can't co-create object 公司的web程序中需要调用一个C++开发的DLL动态库文件,在使用jacob过程中在出现以下为题: 首先在开发前使用mian方法测试调用dll可以正常实现功能需求,但将代码放在web程序中出现Can't co-create object错误.求大神解救 解决方案 Account requires an assignment to a CO objectCan't create handler

gradle打包android程序时,如何修改java文件中的属性值

问题描述 gradle打包android程序时,如何修改java文件中的属性值 工程有个文件例如1.java文件:其中有个public final static String URL = "/www.baidu.com"; 现在的问题是如何在用gradle打包的时候可以更换1.java文件中的URL? ant打包就可以写不同的build.xml,在build.xml中更换URL的字符串,在利用ant构建的时候通过编译不同的build.xml来达到更换URL的目的. 现在不知道gradl

visual studio 2010-关于VS2010打包安装程序,安装时,选择安装文件夹界面中的磁盘开销的问题,求大神指导

问题描述 关于VS2010打包安装程序,安装时,选择安装文件夹界面中的磁盘开销的问题,求大神指导 用VS2010打包的安装程序,安装时的选择安装文件夹界面 我想把磁盘开销去掉,或者换成下图这样的"所需空间"和"可用空间",不知道打包的时候,在哪设置 求指导 解决方案 最简单的是用第三方的打包工具,比如installshield,它支持对话框自定义.想怎么做就怎么做. 解决方案二: 安装程序的安装界面为乱码的问题

jcom-利用Jcom在用java程序中调用windows Com组件,Jcom.dll是不是支持64位操作系统?

问题描述 利用Jcom在用java程序中调用windows Com组件,Jcom.dll是不是支持64位操作系统? 利用Jcom在用java程序中调用windows Com组件,Jcom.dll是不是支持64位操作系统?我发现在32位机器上是可以调用成功的,为什么切换到64为机器上就调用不成功,有谁了解这个Jcom的,谢谢给个解答.

难道JavaEye就没有个高手么?怎么把在Netbeans中创建的J2ME程序打包到手机中测试?

问题描述 怎么把在Netbeans中创建的J2ME程序打包到手机中测试? 解决方案 ...没有高手这是什么意思?呵呵你点一下生成工程,就是那个bulit按钮,之后你可以回到你的工程那个文件夹下,在dist文件夹里有生成的jar文件那就是你要的东西了