WorldWind学习系列八:LoadUnload Plugins——直捣黄龙篇

it2024-11-08  24

第一部分

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

   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()通过一行行地读取文件内容,从而解析出所需的原数据。

///   <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方法。

  ///   <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)。

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

      ///   <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.cs384行 

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

     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类下载插件文件到插件目录下。

   ///   <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下。

   ///   <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();用来更新插件列表。

   ///   <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 );

卸载代码    ///   <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 )。

 

其他部分:

WorldWind学习系列七:Load/Unload Plugins——投石问路篇

WorldWind学习系列六:渲染过程解析篇

WorldWind学习系列五:插件加载过程全解析

WorldWind学习系列四:功能分析——Show Planet Axis、Show Position 、Show Cross Hairs功能

WorldWind学习系列三:简单功能分析——主窗体的键盘监听处理及拷贝和粘贴位置坐标功能

WorldWind学习系列三:功能分析——截屏功能和“关于”窗体分析

WorldWind学习系列二:擒贼先擒王篇2

WorldWind学习系列二:擒贼先擒王篇1

WorldWind学习系列一:顺利起航篇

 

 

转载于:https://www.cnblogs.com/wuhenke/archive/2009/12/16/1625939.html

相关资源:数据结构—成绩单生成器
最新回复(0)