WorldWind学习系列八:Load/Unload Plugins——直捣黄龙篇

第一部分

打开PluginDialog.cs窗体时,会调用273行的

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->  private void PluginDialog_Load(object sender, System.EventArgs e)
  {
   //加载插件到ListView控件中     
   AddPluginList();
   
   //Force UI state update
   listView_SelectedIndexChanged(this,EventArgs.Empty);
   //根据ListView当前选中项,更新窗体按钮功能的可用性
   UpdateUIStates();
  }

  /// <summary>
  /// Fill the list view with currently installed plugins.
  /// </summary>
  void AddPluginList()
  {
   listView.Items.Clear();
   foreach (PluginInfo pi in compiler.Plugins) 
   {
    PluginListItem li = new PluginListItem(pi);
    listView.Items.Add(li);
   }
  }

此处PluginInfo里面有个知识点,请看下面两个截图:(暂缺)

 

就是怎样读取文件头的元数据?PluginInfo.cs 188行ReadMetaData()通过一行行地读取文件内容,从而解析出所需的原数据。

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->/// <summary>
  /// Reads strings from the source file header tags
  /// </summary>
  private void ReadMetaData()
  {
   try
   {
    if(m_fullPath==null)
     // Source code comments not available
     return;

    // Initialize variables (prevents more than one call here)
    if(m_name==null)
     m_name = "";
    if(m_description==null)
     m_description = "";
    if(m_developer==null)
     m_developer = "";
    if(m_webSite==null)
     m_webSite = "";
    if(m_references==null)
     m_references = "";

    using(TextReader tr = File.OpenText(m_fullPath))
    {
     //注意:这里将插件文件的所有行内容都读取一遍啦,其实元数据都在前面几行的。
     while(true)
     {
      string line = tr.ReadLine();
      if(line==null)
       break;

      FindTagInLine(line, "NAME", ref m_name);
      FindTagInLine(line, "DESCRIPTION", ref m_description);
      FindTagInLine(line, "DEVELOPER", ref m_developer);
      FindTagInLine(line, "WEBSITE", ref m_webSite);
      FindTagInLine(line, "REFERENCES", ref m_references);
                                         //下面是我修改添加的,为了提升一定的效率
    if(m_name!=string.Empty&& m_description!=string.Empty&& m_developer!=string.Empty&& m_webSite!=string.Empty&&m_references!=string.Empty)
      return;

     }
    }
   }
   catch(IOException)
   {
    // Ignore
   }
   finally
   {
    if(m_name.Length==0)
     // If name is not defined, use the filename
     m_name = Path.GetFileNameWithoutExtension(m_fullPath);
   }
  }

我们看看236行的FindTagInLine方法。

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> /// <summary>
  /// Extract tag value from input source line.
  /// </summary>
  static void FindTagInLine(string inputLine, string tag, ref string value)
  {
   if(value!=string.Empty)
    // Already found
    return;

   // Pattern: _TAG:_<value>EOL
   tag = " " + tag + ": ";
   int index = inputLine.IndexOf(tag);
   if(index<0)
    return;
   //获取冒号后面的所有内容。
   value = inputLine.Substring(index+tag.Length);
  }

第二部分

   窗体中的Load和Unload功能,分别调用了306行PluginLoad(PluginListItem pi)、324行的 public void PluginUnload(PluginListItem pi)。
  真正实现装载和卸载的是PluginCompiler.cs里的244行的 Load(PluginInfo pi)和277行的 Unload(PluginInfo pi)。

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> /// <summary>
  /// Load a plugin
  /// </summary>
  public void Load(PluginInfo pi)
  {
   if(pi.Plugin == null)
   {
    // Try to find a suitable compiler
    string extension = Path.GetExtension(pi.FullPath).ToLower();
    Assembly asm = null;
    if(extension==".dll")
    {
     // Load pre-compiled assembly ,此处利用了反射动态加载
     asm = Assembly.LoadFile(pi.FullPath);
    }
    else
    {     //CodeDomProvider知识点
     CodeDomProvider cdp = (CodeDomProvider)codeDomProviders[extension];
     if(cdp==null)
      return;
     //使用特定的编译器,将插件类文件编译为dll
     asm = Compile(pi, cdp);
    }

    pi.Plugin = GetPluginInterface(asm);
   }

   string pluginPath = MainApplication.DirectoryPath;
   if( pi.FullPath != null && pi.FullPath.Length > 0)
    pluginPath = Path.GetDirectoryName(pi.FullPath);

   pi.Plugin.PluginLoad(worldWind, pluginPath);
  }

从代码中,可看到加载插件分为两种方式:一种是加载预编译的插件程序集(即:dll文件);一种是加载插件类文件,实现动态编译。
第一种方式:通过反射机制的Assembly,实现运行时加载插件DLL,关键是学习这种方式, Assembly asm = null; asm = Assembly.LoadFile(pi.FullPath);
第二种方式:CodeDomProvider请参看(强烈推荐CodeDomProvider学习系列网址http://www.cnblogs.com/lichdr/category/12610.html
codeDomProviders就是一个HashTable对象,里面存放的是类文件的后缀名(.cs,.vb),是在PluginCompiler.cs构造函数中调用

AddCodeProvider(new Microsoft.CSharp.CSharpCodeProvider() );
   AddCodeProvider(new Microsoft.VisualBasic.VBCodeProvider() );
   AddCodeProvider(new Microsoft.JScript.JScriptCodeProvider() );

C#、VB、J#都是WorldWind里支持动态编译插件类的语言。

/// <summary>
  /// Adds a compiler to the list of available codeDomProviders
  /// </summary>
  public void AddCodeProvider( CodeDomProvider cdp )
  {
   // Add leading dot since that's what Path.GetExtension uses
   codeDomProviders.Add("."+cdp.FileExtension, cdp);
  }

我们看看WW是如何做到运行时动态编译的,261行代码:asm = Compile(pi, cdp);原来是通过Complie()方法实现将插件类文件编译为dll。

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->     /// <summary>
  /// Compiles a file to an assembly using specified compiler.
  /// </summary>
  Assembly Compile( PluginInfo pi, CodeDomProvider cdp )
  {
   // Compile
   //ICodeCompiler compiler = cdp.CreateCompiler();

   if(cdp is Microsoft.JScript.JScriptCodeProvider)
    // JSCript doesn't support unsafe code
    cp.CompilerOptions = "";
   else
    cp.CompilerOptions = "/unsafe";

   // Add references
   cp.ReferencedAssemblies.Clear();
   //添加引用:PluginCompiler.cs构造函数中88-99行代码worldWind下的所有引用到集合对象m_worldWindReferencesList中的,实际用到的引用没有这么多的,完全是“宁多不可少”!
 foreach( string reference in m_worldWindReferencesList)
    cp.ReferencedAssemblies.Add(reference);

   // Add reference to core functions for VB.Net users ,添加VB核心编译引用
   if(cdp is Microsoft.VisualBasic.VBCodeProvider)
    cp.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll");

   // Add references specified in the plugin,添加插件内部自己特有的引用
   foreach( string reference in pi.References.Split(','))
    AddCompilerReference( pi.FullPath, reference.Trim() );
   //调用CompileAssemblyFromFile方法实现编译
   CompilerResults cr = cdp.CompileAssemblyFromFile( cp, pi.FullPath );
   if(cr.Errors.HasErrors || cr.Errors.HasWarnings)
   {
    // Handle compiler errors
    StringBuilder error = new StringBuilder();
    foreach (CompilerError err in cr.Errors)
    {
     string type = (err.IsWarning ? "Warning" : "Error");
     if(error.Length>0)
      error.Append(Environment.NewLine);
     error.AppendFormat("{0} {1}: Line {2} Column {3}: {4}", type, err.ErrorNumber, err.Line, err.Column, err.ErrorText );
    }
                Log.Write(Log.Levels.Error, LogCategory, error.ToString());
                if(cr.Errors.HasErrors)
        throw new Exception( error.ToString() );
   }

   // Success, return our new assembly,返回编译结果
   return cr.CompiledAssembly;
  }

继续分析,看PluginCompiler.cs中264行, pi.Plugin = GetPluginInterface(asm);从编译后的插件工程中,获取Plugin对象实例。static Plugin GetPluginInterface(Assembly asm)中关键的和值得我们学习借鉴的就是:
     Plugin pluginInstance = (Plugin) asm.CreateInstance( t.ToString() ); 
     return pluginInstance;
学习Assembly的CreateInstance方法。
PluginCompiler.cs中271行pi.Plugin.PluginLoad(worldWind, pluginPath);中开始调用各用户插件(Plugin)中重写的Load()方法将加载到WorldWind.
我在查找资料学习CodeDomProvider花了不少实际,原来网上很多资料了,原来运行时编译的强大功能早就有了。等稍后有时间我一定深入学习一下该部分内容。

插件Unload功能:
使用的是public void PluginUnload(PluginListItem pi),里面328行调用了PluginCompiler.cs中的public void Uninstall(PluginInfo pi),该函数方法的关键代码是pi.Plugin.PluginUnload();继续跟踪进去,发现真正实现插件卸载的是Plugin.cs及其子类重载过的Unload()方法。自己写插件时需要重写该方法的。

Install插件功能:
PluginDialog.cs
384行 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> private void buttonInstall_Click(object sender, System.EventArgs e)
  {
   Form installDialog = new PluginInstallDialog(compiler);
   installDialog.Icon = this.Icon;
   installDialog.ShowDialog();

   // Rescan for plugins
   compiler.FindPlugins();
   AddPluginList();
  }


 

此处我们需要关注学习两方面:PluginInstallDialog.cs 和compiler.FindPlugins();
PluginInstallDialog中插件来源分为:Web 和File。
查看代码171行:

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->    if(IsWeb)
     InstallFromUrl(new Uri(url.Text));
    else if(IsFile)
     InstallFromFile(url.Text);
    else
    {
     MessageBox.Show("Please specify an existing filename or a web url starting with 'http://'.", "Not found", MessageBoxButtons.OK, MessageBoxIcon.Error );
     url.Focus();
     return;
    }


从网络上安装插件,实质上就是使用WebDownload类下载插件文件到插件目录下。

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->  /// <summary>
  /// Install plugin from web (url).
  /// </summary>
  /// <param name="pluginUrl">http:// URL</param>
  void InstallFromUrl( Uri uri )
  {
   string fileName = Path.GetFileName( uri.LocalPath );
   string destPath = GetDestinationPath( fileName );
   if(destPath == null)
    return;

   using(WebDownload dl = new WebDownload(uri.ToString()))
    dl.DownloadFile(destPath);

   ShowSuccessMessage( fileName );
  }


从文件系统中安装插件,直接拷贝插件文件到插件目录Plugins下。

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->  /// <summary>
  /// Install plugin from local file.
  /// </summary>
  /// <param name="pluginPath">Plugin path/filename.</param>
  void InstallFromFile( string pluginPath )
  {
   string fileName = Path.GetFileName( pluginPath );
   string destPath = GetDestinationPath( fileName );
   if(destPath == null)
    return;

   File.Copy(pluginPath, destPath);

   ShowSuccessMessage( fileName );
  }

compiler.FindPlugins();调用了PluginCompiler类的FindPlugins()方法,用来重新扫描Plugins目录及子目录,获取到所有的插件。接着调用PluginDialog.cs的AddPluginList();用来更新插件列表。

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->  /// <summary>
  /// Build/update the list of available plugins.
  /// </summary>
  public void FindPlugins()
  {
   if(!Directory.Exists(m_pluginRootDirectory))
    return;

   // Plugins should reside in subdirectories of path
   foreach(string directory in Directory.GetDirectories(m_pluginRootDirectory))
    AddPlugin(directory);

   // Also scan Plugins base directory
   AddPlugin(m_pluginRootDirectory);
  }

Uninstall插件功能:
关键代码:421行compiler.Uninstall( pi.PluginInfo );

卸载代码 


Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->  /// <summary>
  /// Uninstall/delete a plugin.
  /// </summary>
  /// <param name="pi"></param>
  public void Uninstall(PluginInfo pi)
  {
   // Unload the plugin
   Unload(pi);

   File.Delete( pi.FullPath );

   m_plugins.Remove( pi );
  }

首先调用用Unload插件功能,然后把插件文件删除掉,更新插件列表m_plugins.Remove( pi )。









 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值