.net 動態加載Dll


在程序正在使用的過程中,常常需要升級DLL。這時,如果dll已經被主程序引用,則無法修改,這樣的需求應該很常見。換個角度,可以理解成程序的升級或者修改Bug的功能。

以下通過動態的加載Dll來解決這個問題。

整個思路的前提是,動態調用的東西和前台需要的功能通過代理IBaseInterface連接起來,也就是說動態dll里面的類和Proxy都需要實現這個接口。

namespace BaseInterface
{
    public interface IBaseInterface
    {
        string GetString();
    }
}

核心代碼:Proxy.dll

namespace Proxy
{
    internal class AppDomainCore
    {
        public AppDomain DefaultAppDomain { get; private set; }
        public string DefaultAppDomainName { get; private set; }

        public AppDomainCore(string appDomainName)
        {
            DefaultAppDomainName = appDomainName;
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            setup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

            Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
            DefaultAppDomain = AppDomain.CreateDomain(appDomainName, evidence, setup);
        }

        public bool ClearAppDomain()
        {
            try
            {
                AppDomain.Unload(DefaultAppDomain);
                DefaultAppDomain = null;
                return true;
            }
            catch { return false; }
        }

        ~AppDomainCore()
        {
            ClearAppDomain();
        }
    }
}
namespace Proxy
{
    internal class AssemblyCore
    {
        public string ActivedAssemblyName { get; private set; }
        public string CurrentType { get; private set; }
        public FileInfo DefaultAssemblyFile { get; private set; }

        public AssemblyCore(string assemblyName, string type)
        {
            ActivedAssemblyName = assemblyName;
            CurrentType = type;

            try
            {
                DefaultAssemblyFile = new FileInfo(assemblyName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}
namespace Proxy
{
    public class Proxy : BaseInterface.IBaseInterface
    {
        AssemblyCore _assemblyCore;
        AppDomainCore _appDomainCore;

        public string DefaultAssemblyName { get { return _assemblyCore.ActivedAssemblyName; } }
        public string DefaultAppDomainName { get { return _appDomainCore.DefaultAppDomainName; } }

        public Proxy(string assemblyName, string typeName, string appDomainName)
        {
            _assemblyCore = new AssemblyCore(assemblyName, typeName);
            _appDomainCore = new AppDomainCore(appDomainName);
        }

        public void UnLoad()
        {
            _appDomainCore.ClearAppDomain();
        }

        BaseInterface.IBaseInterface _proxy;
        public string GetString()
        {
            if (_proxy == null)
                _proxy = _appDomainCore.DefaultAppDomain.CreateInstanceFromAndUnwrap(
                    _assemblyCore.ActivedAssemblyName, _assemblyCore.CurrentType) 
                    as BaseInterface.IBaseInterface;
            return _proxy.GetString();
        }
    }
}

以上代碼在項目Proxy里面寫入,是動態加載Dll的核心代碼。

在Proxy里面,其中AppDomainCore根據傳遞的參數生成新的AppDomain, AssemblyCore則根據傳遞的參數生成新的FileInfo. 在Proxy類里面,同時定義一個AssemblyCore和AppDomainCore,根據方法:_appDomainCore.DefaultAppDomain.CreateInstanceFromAndUnwrap(_assemblyCore.ActivedAssemblyName, _assemblyCore.CurrentType), 動態的生成實例,最后用as 轉換成接口IBaseInterface,通過調用接口的GetString方法來調用dll里面的方法。

一下為模擬的兩個Dll類,

namespace Assembly1
{
    [Serializable]
    public class ClassLibrary : MarshalByRefObject, BaseInterface.IBaseInterface
    {
        public string GetString()
        {
            return "Assembly1.ClassLibrary";
        }
    }
}
namespace Assembly2
{
    [Serializable]
    public class ClassLibrary : MarshalByRefObject, BaseInterface.IBaseInterface
    {
        public string GetString()
        {
            return "Assembly2.ClassLibrary";
        }
    }
}

分別生成到Assembly v1.0.dll和Assembly v2.0.dll中。

最終的界面代碼:

namespace DynamicDll
{
    public partial class Form1 : Form
    {
        Proxy.Proxy _proxy;
        public Form1()
        {
            InitializeComponent();
            LoadDll("Assembly v1.0.dll", "Assembly1.ClassLibrary", "Domain1");
            radV1.Click += (a, b) => LoadDll("Assembly v1.0.dll", "Assembly1.ClassLibrary", "Domain1");
            radV2.Click += (a, b) => LoadDll("Assembly v2.0.dll", "Assembly2.ClassLibrary", "Domain2");
        }

        void LoadDll(string assName, string type, string appName)
        {
            if (_proxy != null) _proxy.UnLoad();
            _proxy = new Proxy.Proxy(assName, type, appName);
            lblCurrentAppDomain.Text = _proxy.DefaultAppDomainName;
            lblCurrentAssembly.Text = _proxy.DefaultAssemblyName;
            lblMainAppDomain.Text = AppDomain.CurrentDomain.FriendlyName;
        }

        private void btnRes_Click(object sender, EventArgs e)
        {
            txtRes.Text = _proxy.GetString();
        }
    }
}

 

查看項目Dynamic的依賴項,可以很清楚的看到:它只依賴BaseInterface和Proxy兩個項目,與Assembly v1.0和v2.0都無關。

也就是說,程序運行的時候,可以修改Assembly v1.0和v2.0里面的代碼,重新生成,同時替換運行目錄下的dll。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM