• 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

    代码下载

  • 相关阅读:
    PAT Advanced 1067 Sort with Swap(0, i) (25分)
    PAT Advanced 1048 Find Coins (25分)
    PAT Advanced 1060 Are They Equal (25分)
    PAT Advanced 1088 Rational Arithmetic (20分)
    PAT Advanced 1032 Sharing (25分)
    Linux的at命令
    Sublime Text3使用指南
    IntelliJ IDEA创建第一个Groovy工程
    Sublime Text3 安装ftp插件
    Sublime Text3配置Groovy运行环境
  • 原文地址:https://www.cnblogs.com/blqw/p/LoadResourceDll.html
Copyright © 2020-2023  润新知