分享在winform下實現模塊化插件編程-優化版

上一篇《分享在winform下實現模塊化插件編程》已經實現了模塊化編程,但我認爲不夠完美,存在如下幾個問題:html

1.IAppContext中的CreatePlugInForm方法只能依據完整的窗體類型名稱formTypeName來動態建立窗體對象,調用不夠方便,且該方法建立的窗體不受各模塊註冊窗體類型AppFormTypes限制,也就是能夠建立任何FORM,存在不肯定性;java

2.動態建立的窗體對象沒法直接對其公共屬性或公共方法進行調用web

3.主應用程序中的LoadComponents方法是經過指定文件夾對全部的DLL文件所有進行獲取而後再進行TYPE解析最終才找到實現了ICompoentConfig的類,這個過程比較繁鎖效率低下;編程

4.編譯後的應用程序根目錄混亂,許多的DLL都與主應用程序EXE在一塊兒;數組

 下面就針對上述問題進行一一解決。緩存

1.爲IAppContext增長几個CreatePlugInForm的擴展方法,同時AppContext實現這幾個方法,代碼以下:安全

IAppContext:session

    /// <summary>
    /// 應用程序上下文對象接口
    /// 做用:用於收集應用程序必備的一些公共信息並共享給整個應用程序全部模塊使用(含動態加載進來的組件)
    /// 做者:Zuowenjun
    /// 2016-3-26
    /// </summary>
    public interface IAppContext
    {
        /// <summary>
        /// 應用程序名稱
        /// </summary>
        string AppName { get; }

        /// <summary>
        /// 應用程序版本
        /// </summary>
        string AppVersion { get; }

        /// <summary>
        /// 用戶登陸信息
        /// </summary>
        object SessionUserInfo { get; }

        /// <summary>
        /// 用戶登陸權限信息
        /// </summary>
        object PermissionInfo { get; }

        /// <summary>
        /// 應用程序全局緩存,整個應用程序(含動態加載的組件)都可進行讀寫訪問
        /// </summary>
        ConcurrentDictionary<string, object> AppCache { get; }

        /// <summary>
        /// 應用程序主界面窗體,各組件中能夠訂閱或獲取主界面的相關信息
        /// </summary>
        Form AppFormContainer { get; }

        /// <summary>
        /// 動態建立在註冊列表中的插件窗體實例
        /// </summary>
        /// <param name="formType"></param>
        /// <returns></returns>
        Form CreatePlugInForm(Type formType,params object[] args);

        /// <summary>
        /// 動態建立在註冊列表中的插件窗體實例
        /// </summary>
        /// <param name="formTypeName"></param>
        /// <returns></returns>
        Form CreatePlugInForm(string formTypeName, params object[] args);

        /// <summary>
        /// 動態建立在註冊列表中的插件窗體實例
        /// </summary>
        /// <param name="formTypeName"></param>
        /// <returns></returns>
        Form CreatePlugInForm<TForm>(params object[] args) where TForm : Form;

    }

AppContext:app

    /// <summary>
    /// 應用程序上下文對象類
    /// 做者:Zuowenjun
    /// 2016-3-26
    /// </summary>
    public class AppContext : IAppContext
    {

        internal static AppContext Current;

        internal Dictionary<string, Type> AppFormTypes
        {
            get;
            set;
        }

        public string AppName
        {
            get;
            private set;
        }

        public string AppVersion
        {
            get;
            private set;
        }

        public object SessionUserInfo
        {
            get;
            private set;
        }

        public object PermissionInfo
        {
            get;
            private set;
        }

        public ConcurrentDictionary<string, object> AppCache
        {
            get;
            private set;
        }

        public System.Windows.Forms.Form AppFormContainer
        {
            get;
            private set;
        }


        public AppContext(string appName, string appVersion, object sessionUserInfo, object permissionInfo, Form appFormContainer)
        {
            this.AppName = appName;
            this.AppVersion = appVersion;
            this.SessionUserInfo = sessionUserInfo;
            this.PermissionInfo = permissionInfo;
            this.AppCache = new ConcurrentDictionary<string, object>();
            this.AppFormContainer = appFormContainer;
        }

        public System.Windows.Forms.Form CreatePlugInForm(Type formType, params object[] args)
        {
            if (this.AppFormTypes.ContainsValue(formType))
            {
                return Activator.CreateInstance(formType, args) as Form;
            }
            else
            {
                throw new ArgumentOutOfRangeException(string.Format("該窗體類型{0}不在任何一個模塊組件窗體類型註冊列表中!", formType.FullName), "formType");
            }
        }

        public System.Windows.Forms.Form CreatePlugInForm(string formTypeName, params object[] args)
        {
            if (!formTypeName.Contains('.'))
            {
                formTypeName = "." + formTypeName;
            }
            var formTypes = this.AppFormTypes.Where(t => t.Key.EndsWith(formTypeName, StringComparison.OrdinalIgnoreCase)).ToArray();
            if (formTypes == null || formTypes.Length != 1)
            {
                throw new ArgumentException(string.Format("從窗體類型註冊列表中未能找到與【{0}】相匹配的惟一窗體類型!", formTypeName), "formTypeName");
            }
            return CreatePlugInForm(formTypes[0].Value, args);
        }


        public Form CreatePlugInForm<TForm>(params object[] args) where TForm : Form
        {
            return CreatePlugInForm(typeof(TForm), args);
        }
    }

從AppContext類中能夠看出,CreatePlugInForm方法有三個重載,分別支持依據TYPE、泛型、模糊類型名來動態建立窗體對象,同時若窗體類型含有參的構造函數,那麼後面的args參數數組賦值便可。jsp

2.爲Form類型增長三個擴展方法,分別是:SetPublicPropertyValue(動態給公共屬性賦值)、GetPublicPropertyValue(動態獲取公共屬性的值)、ExecutePublicMethod(動態執行公共方法(含公共靜態方法)),彌補動態建立的窗體沒法對公共成員進行操做的問題,代碼以下:

    public static class FormExtension
    {
        /// <summary>
        /// 動態給公共屬性賦值
        /// </summary>
        /// <param name="form"></param>
        /// <param name="propertyName"></param>
        /// <param name="propertyValue"></param>
        public static void SetPublicPropertyValue(this Form form, string propertyName, object propertyValue)
        {
            var formType = form.GetType();
            var property = formType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (property != null)
            {
                property.SetValue(form, propertyValue, null);
            }
            else
            {
                throw new Exception(string.Format("沒有找到名稱爲:{0}的公共屬性成員!", propertyName));
            }
        }

        /// <summary>
        /// 動態獲取公共屬性的值
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="form"></param>
        /// <param name="propertyName"></param>
        /// <param name="defaultPropertyValue"></param>
        /// <returns></returns>
        public static TResult GetPublicPropertyValue<TResult>(this Form form, string propertyName, TResult defaultPropertyValue)
        {
            var formType = form.GetType();
            var property = formType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            var proValue = property.GetValue(form, null);
            if (property != null)
            {
                try
                {
                    return (TResult)Convert.ChangeType(proValue, typeof(TResult));
                }
                catch
                {
                    return defaultPropertyValue;
                }
            }
            else
            {
                throw new Exception(string.Format("沒有找到名稱爲:{0}的公共屬性成員!", propertyName));
            }
        }

        /// <summary>
        /// 動態執行公共方法(含公共靜態方法)
        /// </summary>
        /// <param name="form"></param>
        /// <param name="methodName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static object ExecutePublicMethod(this Form form, string methodName, params object[] args)
        {
            var formType = form.GetType();
            var method = formType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (method != null)
            {
                return method.Invoke(form, args);
            }
            else
            {
                throw new Exception(string.Format("沒有找到名稱爲:{0}且形數個數有:{1}個的公共方法成員!", methodName, args == null ? 0 : args.Count()));
            }
        }
    }

使用很簡單就再也不演示說明了。

3.動態加載符合條件的模塊組件,以前的LoadComponents效率過低,而我這裏想實現相似ASP.NET 的Handler或Module能夠動態的從CONFIG文件中進行增減配置,ASP.NET 的Handler、Module配置節點以下:

        <httpHandlers>
            <add path="eurl.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True" />
            <add path="trace.axd" verb="*" type="System.Web.Handlers.TraceHandler" validate="True" />
            <add path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" validate="True" />
            <add verb="*" path="*_AppService.axd" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False" />
            <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True" />
            <add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True" />
            <add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True" />
            <add path="*.asmx" verb="*" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False" />
            <add path="*.rem" verb="*" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="False" />
            <add path="*.soap" verb="*" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="False" />
            <add path="*.asax" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.ascx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.master" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.skin" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.browser" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.sitemap" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.dll.config" verb="GET,HEAD" type="System.Web.StaticFileHandler" validate="True" />
            <add path="*.exe.config" verb="GET,HEAD" type="System.Web.StaticFileHandler" validate="True" />
            <add path="*.config" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.cs" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.csproj" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.vb" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.vbproj" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.webinfo" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.licx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.resx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.resources" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.mdb" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.vjsproj" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.java" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.jsl" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.ldb" verb="*" type="System.Web.HttpForbiddenHandler"  validate="True" />
            <add path="*.ad" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.dd" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.ldd" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.sd" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.cd" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.adprototype" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.lddprototype" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.sdm" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.sdmDocument" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.mdf" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.ldf" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.exclude" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.refresh" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
            <add path="*.svc" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.rules" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.xoml" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.xamlx" verb="*" type="System.Xaml.Hosting.XamlHttpHandlerFactory, System.Xaml.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.aspq" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.cshtm" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.cshtml" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.vbhtm" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.vbhtml" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*" verb="GET,HEAD,POST" type="System.Web.DefaultHttpHandler" validate="True" />
            <add path="*" verb="*" type="System.Web.HttpMethodNotAllowedHandler" validate="True" />
        </httpHandlers>

        <httpModules>
            <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
            <add name="Session" type="System.Web.SessionState.SessionStateModule" />
            <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
            <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
            <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
            <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
            <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
            <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
            <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" />
            <add name="Profile" type="System.Web.Profile.ProfileModule" />
            <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
            <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
            <add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        </httpModules>

若需實現從CONFIG文件配置,那麼就須要增長自定義節點配置,如:compoents,固然若是爲能省事也能夠直接用appSettings節點,要增長自定義節點配置,就須要定義與自定義節點相關的類,具體的實現方式,百度搜索一下就知道了,我這裏也直接給出一個參考地址:http://www.cnblogs.com/lichaoliu/archive/2010/11/03/1868245.html,以下是我實現的compoents節點相關的類:

    /// <summary>
    /// 組件配置節點類
    /// 做者:Zuowenjun
    /// 2016-3-30
    /// </summary>
    public class CompoentConfigurationSection : ConfigurationSection
    {
        private static readonly ConfigurationProperty s_property = new ConfigurationProperty(
                string.Empty, typeof(ComponentCollection), null, ConfigurationPropertyOptions.IsDefaultCollection);

        [ConfigurationProperty("", Options = ConfigurationPropertyOptions.IsDefaultCollection)]
        public ComponentCollection Components
        {
            get
            {
                return (ComponentCollection)base[s_property];
            }
        }


        [ConfigurationProperty("basePath", IsRequired = false)]
        public string BasePath
        {
            get
            {
                return ReMapBasePath(this["basePath"].ToString());
            }
            set
            {
                this["basePath"] = ReMapBasePath(value);
            }
        }

        private string ReMapBasePath(string basePath)
        {
            if (basePath.Trim().StartsWith("~\\"))
            {
                basePath = basePath.Replace("~\\", AppDomain.CurrentDomain.BaseDirectory + "\\");
            }
            return basePath;
        }
    }



    /// <summary>
    /// 組件配置集合類
    /// 做者:Zuowenjun
    /// 2016-3-30
    /// </summary>
    [ConfigurationCollection(typeof(ComponentElement))]
    public class ComponentCollection : ConfigurationElementCollection
    {

        public ComponentCollection():base(StringComparer.OrdinalIgnoreCase)
        {

        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new ComponentElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return (element as ComponentElement).FileName;
        }

        new public ComponentElement this[string fileName]
        {
            get
            {
                return (ComponentElement)base.BaseGet(fileName);
            }
        }

        public void Add(ComponentElement item)
        {
            this.BaseAdd(item);
        }

        public void Clear()
        {
            base.BaseClear();
        }

        public void Remove(string fileName)
        {
            base.BaseRemove(fileName);
        }

    }


    /// <summary>
    /// 組件配置項類
    /// 做者:Zuowenjun
    /// 2016-3-30
    /// </summary>
    public class ComponentElement : ConfigurationElement
    {

        [ConfigurationProperty("fileName", IsRequired = true, IsKey = true)]
        public string FileName
        {
            get { return this["fileName"].ToString(); }
            set { this["fileName"] = value; }
        }

        [ConfigurationProperty("entryType", IsRequired = true)]
        public string EntryType
        {
            get { return this["entryType"].ToString(); }
            set { this["entryType"] = value; }
        }

        [ConfigurationProperty("sortNo", IsRequired = false, DefaultValue = 0)]
        public int SortNo
        {
            get { return Convert.ToInt32(this["sortNo"]); }
            set { this["sortNo"] = value; }
        }
    }

最終實現的配置示例以下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="compoents" type="WMS.PlugIn.Framework.Configuration.CompoentConfigurationSection,WMS.PlugIn.Framework"/>
  </configSections>
  <compoents basePath="~\Libs\">
    <add fileName="WMS.Com.CW.dll" entryType="WMS.Com.CW.CompoentConfig" sortNo="1" />
  </compoents>

而後主應用程序這邊改進LoadComponents方法,具體代碼以下:

        private void LoadComponents()
        {
            var compoents = ConfigurationManager.GetSection("compoents") as CompoentConfigurationSection;
            if (compoents == null) return;

            string basePath = compoents.BasePath;
            if (string.IsNullOrWhiteSpace(basePath))
            {
                basePath = Program.AppLibsDir;
            }

            Type targetFormType = typeof(Form);

            foreach (ComponentElement item in compoents.Components)
            {
                string filePath = Path.Combine(basePath, item.FileName);
                var asy = Assembly.LoadFrom(filePath);
                var type = asy.GetType(item.EntryType, true);
                ICompoent compoent = null;
                var config = (ICompoentConfig)Activator.CreateInstance(type);
                config.CompoentRegister(AppContext.Current, out compoent);//關鍵點在這裏,獲得組件實例化後的compoent
                if (compoent != null)
                {
                    foreach (Type formType in compoent.FormTypes)//將符合的窗體類型集合加到AppContext的AppFormTypes中
                    {
                        if (targetFormType.IsAssignableFrom(formType) && !formType.IsAbstract)
                        {
                            AppContext.Current.AppFormTypes.Add(formType.FullName, formType);
                        }
                    }
                }
            }
        }

對比改進先後的LoadComponents方法,有沒有以爲改進後的代碼效率更高一些了,我認爲效率高在避免了文件夾的掃描及類型的查詢,改進後的方法都是經過配置文件的信息直接獲取程序集及指定的類型信息。

4.改進了插件編程這塊後,最後一個要解決的其實與插件編程無關,但由於我在項目中也同時進行了改進,因此也在此一併說明實現思路。

要想將引用的DLL放到指定的文件夾下,如:Libs,就須要瞭解程序集的尋找原理,具體瞭解請參見:C#開發奇技淫巧三:把dll放在不一樣的目錄讓你的程序更整潔,說白了只要設置或改變其私有目錄privatePath,就能改變程序集加載時尋找的路徑,網上大部份是採用以下配置的方式來修改privatePath,以下:

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <probing privatePath="Libs"/>
     </assemblyBinding>
</runtime>

而我這裏採用另外一種方法:經過訂閱AssemblyResolve事件(該事件是加載程序失敗時觸發)而後在訂閱的事件中動態加載缺失的程序集來實現的,好處是安全,不用擔憂路徑被改形成程序沒法正常運行的狀況,實現代碼以下:

    static class Program
    {
        public static string AppLibsDir = null;

        /// <summary>
        /// 應用程序的主入口點。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            AppLibsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Libs\");
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

            AddEnvironmentPaths(AppLibsDir);
        }

        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            Assembly assembly = null, objExecutingAssemblies = null;
            objExecutingAssemblies = Assembly.GetExecutingAssembly();
            AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();

            foreach (AssemblyName assmblyName in arrReferencedAssmbNames)
            {
                if (assmblyName.FullName.Substring(0, assmblyName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
                {
                    string path = System.IO.Path.Combine(AppLibsDir, args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll");
                    assembly = Assembly.LoadFrom(path);
                    break;
                }
            }
            return assembly;
        }

        static void AddEnvironmentPaths(params string[] paths)
        {
            var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? string.Empty };

            string newPath = string.Join(Path.PathSeparator.ToString(), path.Concat(paths));

            Environment.SetEnvironmentVariable("PATH", newPath);
        }
    }

裏面包括一個動態增長環境路徑的方法:AddEnvironmentPaths,其做用網上也講過了,就是處理經過[DllImport]中的程序集的加載。

這樣就完成了將引用的DLL放到指定的目錄中:libs,固然在主應用程序引用DLL時,請將複製到本地設爲False,這樣編譯後的程序根目錄纔會乾淨如你所願。

以上就是本文的所有內容,代碼也都已貼出來了,你們能夠直接COPY下來用,固然其實模塊化插件編程還有其它的細節,好比:各模塊組件的更新,各模塊組件的安全性問題等,這些你們有興趣也能夠研究一下,本文如有不足,歡迎指出,謝謝!

相關文章
相關標籤/搜索