搜档网
当前位置:搜档网 › WorldWind系列八 :Load、Unload Plugins 直捣黄龙篇

WorldWind系列八 :Load、Unload Plugins 直捣黄龙篇

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

来源:博客园作者:无痕客

第一部分

打开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();

}

///

/// Fill the list view with currently installed plugins.

///

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

///

/// Reads strings from the source file header tags

///

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_develop er!=string.Empty&& m_webSite!=string.Empty&&m_references!=string.Empt y)

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

///

/// Extract tag value from input source line.

///

static void FindTagInLine(string inputLine, string tag, ref strin

g value)

{

if(value!=string.Empty)

// Already found

return;

// Pattern: _TAG:_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)。

///

/// Load a plugin

///

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[extensio n];

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学习系列

https://www.sodocs.net/doc/2013186907.html,/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里支持动态编译插件类的语言。

///

/// Adds a compiler to the list of available codeDomProviders

///

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。

///

/// Compiles a file to an assembly using specified compiler.

///

Assembly Compile( PluginInfo pi, CodeDomProvider cdp )

{

// Compile

//ICodeCompiler compiler = cdp.CreateCompiler();

if(cdp is Microsoft.JScript.JScriptCodeProvider)

// JSCript doesn't support unsafe code

https://www.sodocs.net/doc/2013186907.html,pilerOptions = "";

else

https://www.sodocs.net/doc/2013186907.html,pilerOptions = "/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 https://www.sodocs.net/doc/2013186907.html, 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 = https://www.sodocs.net/doc/2013186907.html,pileAssemblyFromFile( cp, pi.FullPat

h );

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, er r.ErrorNumber, err.Line, err.Column, err.ErrorText );

}

Log.Write(Log.Levels.Error, LogCategory, error.ToStri ng());

if(cr.Errors.HasErrors)

throw new Exception( error.ToString() );

}

// Success, return our new assembly,返回编译结果

return https://www.sodocs.net/doc/2013186907.html,piledAssembly;

}

继续分析,看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行

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 ur l starting with 'http://'.", "Not found", MessageBoxButtons.OK, Messa geBoxIcon.Error );

url.Focus();

return;

}

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

///

/// Install plugin from web (url).

///

///http:// URL

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

///

/// Install plugin from local file.

///

///Plugin path/filename.

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

///

/// Build/update the list of available plugins.

///

public void FindPlugins()

{

if(!Directory.Exists(m_pluginRootDirectory))

return;

// Plugins should reside in subdirectories of path

foreach(string directory in Directory.GetDirectories(m_pluginRootD irectory))

AddPlugin(directory);

// Also scan Plugins base directory

AddPlugin(m_pluginRootDirectory);

}

Uninstall插件功能:

关键代码:421行compiler.Uninstall( pi.PluginInfo );

卸载代码

///

/// Uninstall/delete a plugin.

///

///

public void Uninstall(PluginInfo pi)

{

// Unload the plugin

Unload(pi);

File.Delete( pi.FullPath );

m_plugins.Remove( pi );

}

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

相关主题