WorldWind源码剖析系列:插件类Plugin、插件信息类PluginInfo和插件编译器类PluginCompiler

插件类Plugin是所有由插件编译器加载的插件子类的抽象父类,提供对插件的轻量级的访问控制功能。

插件信息类PluginInfo用来存储关于某个插件的信息的类,可以理解为对插件类Plugin类的进一步抽象封装,提供了比插件类Plugin更为详细的插件信息。

插件编译器类PluginCompiler用来加载插件脚本,编译和执行插件。

在加载的时候,WorldWind主程序首先调用PluginCompiler函数,在WorldWind根目录的Plugins目录中进行查找,将所有存在的插件文件添加到插件列表中,最后根据用户选择加载运行。这个插件文件的类型既可以是像.cs这类的未编译的源代码,又可以是像.dll这类已经编译为二进制文件的可直接运行的库文件。

在代码中,查找过程如下:

MainApplication.OpenStartupWorld()

一>OpenWorld();

一>InitializePluginCompiler();

compiler=new PluginCompiler(this,pluginRoot);

compiler.FindPlugins(Assembly.GetExecutingAssembly());

//查找程序集内部的的插件类

compiler.FindPlugins();

//查找外部插件类,在plugin文件夹中

compiler.LoadStartupPlugins();

//加载插件

以下代码省略……

插件相关类的关系如下图所示:

图1 WorldWind的插件运行机制

这三个类的类图如下。

 

插件类Plugin包含的主要的字段、属性和方法如下:

protected MainApplication m_Application:加载该插件的主程序。

protected string m_PluginDirectory:插件所在的目录。

protected bool m_isLoaded:插件是否被加载的标识。

public virtual void Load():加载插件的虚函数,可被子类重载。被虚函数public virtual void PluginLoad( MainApplication parent, string pluginDirectory )调用。

public virtual void UnLoad():卸载插件的虚函数,可被子类重载。被虚函数public virtual void PluginUnload()调用。

插件信息类PluginInfo包含的主要的字段、属性和方法如下:

Plugin m_plugin:所封装的插件对象。

string m_fullPath:插件的完全路径。

string m_name:从插件注释头节点中获取的插件的名称。

string m_description:插件的描述。

string m_developer:插件的开发者。

string m_webSite:插件的网络站点。

string m_references:插件的引用。

public string ID:只读属性实际返回的的是不含扩展名的插件名称。

public bool IsCurrentlyLoaded:检查一个插件是否为当前正在加载的插件。

public bool IsLoadedAtStartup:设置插件是否在主程序已启动时就加载的标识。

private void ReadMetaData():该方法从源文件的头节点中读取元数据字符串。

static void FindTagInLine(string inputLine, string tag, ref string value) :该静态方法从输入的一行源数据中返回指定的tag节点的值。

插件编译器类PluginCompiler包含的主要的字段、属性和方法如下:

MainApplication worldWind:加载该插件的主程序。

const string LogCategory = "PLUG":加载该插件时的日志分类类别。

Hashtable codeDomProviders = new Hashtable():codeDomProviders是用来存储codeDomProvider对象的哈希表对象。其中,CodeDom在Microsoft .NET Framework 的中文全名是“代码文档对象模型”, 使用该模型建立的代码文档可以被.NET Framework编译成应用程序集。CodeDomProvider 为抽象类,可用于创建和检索代码生成器和代码编译器的实例。 代码生成器可用于以特定的语言生成代码,而代码编译器可用于将代码编译为程序集,其派生类有CsharpCodeProvider、VBCodeProvider和JScriptCodeProvider。

CompilerParameters cp = new CompilerParameters():表示用于调用编译器的参数。提供用C#动态编译、执行代码的能力。

ArrayList m_plugins = new ArrayList():存储插件的列表。

StringCollection m_worldWindReferencesList = new StringCollection():主程序对插件的引用列表。StringCollection类型表示字符串的集合。

string m_pluginRootDirectory:插件的根路径。

public PluginCompiler( MainApplication worldWind, string pluginDirectory ):方法实例化一个插件编译器对象。

public void AddCodeProvider( CodeDomProvider cdp ): 方法CodeDomProvider的派生类子对象到哈希表对象codeDomProviders中。因为用户可能是用C#、VB或Jscript等语言开发插件。

public void FindPlugins( Assembly assembly ):从指定的程序集中查找插件实例,并将找到的插件信息类PluginInfo对象保存到列表m_plugins中。查找条件是:找到的类型必须是类类型、公开的、父类是Plugin类。

public void FindPlugins():构造或更新可用的插件列表。内部调用void AddPlugin(string path)方法。

void AddPlugin(string path) :从指定的路径中生成插件信息类PluginInfo对象并将其保存到列表m_plugins中。内部调用public bool HasCompiler(string fileExtension)和static public bool IsPreCompiled(string fileExtension)。

public void LoadStartupPlugins():加载那些被设置为在主程序一启动时就加载的插件。内部调用public void Load(PluginInfo pi) 方法。

public void Load(PluginInfo pi):加载指定的插件。具体实现为:程序集反射出插件对象或动态编译生成插件所在的程序集对象,然后再通过从程序集中返回需要加载的插件对象,得到插件对象后再通过调用的是Plugin类的派生类重载后的PluginLoad函数实现插件的真正加载功能。内部主要调用Assembly.LoadFile(pi.FullPath)、Assembly Compile( PluginInfo pi, CodeDomProvider cdp)和static Plugin GetPluginInterface(Assembly asm) 等方法。

public void Unload(PluginInfo pi) :卸载指定的插件,卸载时实际上调用的是Plugin 类或Plugin 派生类重载后的PluginUnload函数。

public void Uninstall(PluginInfo pi):卸载程序集并删除其文件。内部调用Unload

public void Dispose():关闭并析构插件列表中的所有插件对象。

Assembly Compile( PluginInfo pi, CodeDomProvider cdp ):使用指定的编译器动态编译一个文件生成程序集对象,内部包含插件对象。

static Plugin GetPluginInterface(Assembly asm) :从指定的程序集中返回需要加载的插件对象。查找条件是:找到的类型必须是类类型、公开的、父类是Plugin类。

附:

AppDomain 类:表示应用程序域,它是一个应用程序在其中执行的独立环境。 此类不能被继承。

应用程序域(由 AppDomain 对象表示)为执行托管代码提供隔离、卸载和安全边界。

  • 使用应用程序域隔离可能终止进程的任务。 如果正在执行任务的 AppDomain 的状态变得不稳定,则可以卸载 AppDomain,但不会影响进程。当进程必须不重新启动而长时间运行时,这一点很重要。 还可使用应用程序域隔离不应共享数据的任务。
  • 如果程序集被加载到默认应用程序域中,则当进程运行时将无法从内存中卸载该程序集。 但是,如果打开另一个应用程序域来加载和执行程序集,则卸载该应用程序域时也会同时卸载程序集。 使用此技术最小化长时间运行的进程的工作集,这些进程偶尔会使用大型 DLL。

  多个应用程序域可以在一个进程中运行;但是,在应用程序域和线程之间没有一对一的关联。 多个线程可以属于一个应用程序域,尽管给定的线程并不局限于一个应用程序域,但在任何给定时间,线程都在一个应用程序域中执行。

  使用 CreateDomain 方法创建应用程序域。AppDomain 实例用于加载和执行程序集(Assembly)。当不再使用 AppDomain 时,可以将它卸载。

  AppDomain 类实现一组事件,这些事件使应用程序可以在加载程序集、要卸载应用程序域或引发未经处理的异常时进行响应。

原文地址:https://www.cnblogs.com/rainbow70626/p/4658955.html