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

来源:http://www.cnblogs.com/zuowj/archive/2016/04/12/5383672.html
-Advertisement-
Play Games

上一篇《分享在winform下實現模塊化插件編程》已經實現了模塊化編程,但我認為不夠完美,存在以下幾個問題: 1.IAppContext中的CreatePlugInForm方法只能依據完整的窗體類型名稱formTypeName來動態創建窗體對象,調用不夠方便,且該方法創建的窗體不受各模塊註冊窗體類型 ...


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

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

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

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

4.編譯後的應用程式根目錄混亂,許多的DLL都與主應用程式EXE在一起;

 下麵就針對上述問題進行一一解決。

1.為IAppContext增加幾個CreatePlugInForm的擴展方法,同時AppContext實現這幾個方法,代碼如下:

IAppContext:

    /// <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:

    /// <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參數數組賦值即可。

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下來用,當然其實模塊化插件編程還有其它的細節,比如:各模塊組件的更新,各模塊組件的安全性問題等,這些大家有興趣也可以研究一下,本文若有不足,歡迎指出,謝謝!

  

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1唯一約束unique和主鍵key的區別? 1、什麼是數據的存儲引擎? 存儲引擎就是如何存儲數據、如何為存儲的數據建立索引和如何更新、查詢數據等技術的實現方法。因為在關係資料庫中數據的存儲是以表的形式存儲的,所以存儲引擎也可以稱為表類型(即存儲和操作該表的類型),在Oracle和SQL Server ...
  • 1概述 1.1 介紹 Valgrind是一套Linux下,開放源代碼(GPL V2)的模擬調試工具的集合。Valgrind由內核(core)以及基於內核的其他調試工具組成。內核類似於一個框架(framework),它模擬了一個CPU環境,並提供服務給其他工具;而其他工具則類似於插件 (plug-in ...
  • 1上下鍵讓文件為選中狀態 2左鍵選擇文件夾,再次左鍵摺疊。 ...
  • 今天,筆者在備份文件的時候,將一個word文檔從移動硬碟複製到桌面。經過一系列“復(meng)雜(bi)”的操作之後,筆者突然發現,文件無法刪除了。當右鍵文件點擊“刪除”時,出現對話框顯示“正在準備 再迴圈”的字樣。 筆者嘗試了許多方法,例如:當出現“正在準備 再迴圈”字樣時,註銷電腦;下載文件粉 ...
  • 通過上一篇的介紹我們應該對實現在ServiceProvider的總體設計有了一個大致的瞭解,但是我們刻意迴避一個重要的話題,即服務實例最終究竟是採用何種方式提供出來的。ServiceProvider最終採用何種方式提供我們所需的服務實例取決於最終選擇了怎樣的ServiceCallSite,而服務註冊 ...
  • 一、題目描述 構造程式並測試,分別是: 構造程式並測試,分別是: 1.不能觸發Fault。 2.觸發Fault,但是不能觸發Error。 3.觸發Error,但是不能產生Failure。 二、結對對象 結對對象博客: 結對照片: 貢獻比例: 三、構造程式並測試 1.不能觸發Fault. 1.1:構造 ...
  • 介面 什麼是介面? 介面代表一種能力,和抽象類類似但比抽象類的抽象程度更高! 介面的定義: 1 public interface IEat//定義一個介面 2 { 3 void Eat(string food);//為該介面定義一種能力 4 } 從上邊的例子中我們可以看到,介面中的方法是沒有方法體的 ...
  • 預設情況下,當EF調用SaveChanges()時,會把生成的所有SQL命令“包”到一個“事務(transaction)”中,只要有一個數據更新操作失敗,整個事務將回滾。 在多數情況下,如果你總在數據更新操作代碼中使用一個而不是多個DbContext對象,並且只是在最後調用一次SaveChanges ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...