前一篇中,只是对单一文件保存,进行了改进。
但在反编译时,还要一个一个的处理。
面对海量的代码,更需要一个能将所有的保存的工具。磨刀不误砍柴工,再改进一下工具。
首先找到在:
ILSpy\Languages\CSharpLanguage.cs
中,存在一段被注掉的代码:
// TODO implement extension point // var decompiler = Baml.BamlResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assembly.FileName); /*string xaml = null; try { xaml = decompiler.DecompileBaml(ms, assembly.FileName, new ConnectMethodDecompiler(assembly), new AssemblyResolver(assembly)); } catch (System.Xaml.XamlXmlWriterException) { } // ignore XAML writer exceptions if (xaml != null) { File.WriteAllText(Path.Combine(options.SaveAsProjectDirectory, Path.ChangeExtension(fileName, ".xaml")), xaml); yield return Tuple.Create("Page", Path.ChangeExtension(fileName, ".xaml")); continue; }
可能当时写这段代码的编程人员,还是基于相对旧的ILSpy 的框架来思考的,当时可能ILSpy还没有使用MEF框架。
(目前来讲,是在解决公司交给的具体问题,不是来研究细节的,Managed Extensibility Framework,还没仔细看。
所以,如果你读到这,也不用担心会讲这个玩意。)
如果你曾经了解过C++或是其它的语言里的插件框架,MEF本意就是解决这种问题的。
大意如下:
主工程中,建一个基类,然后对于每一种情况,编写了一个派生类来实现这种工作。
比如在ILSpy 中,有许多种待反编译的文件类型,如C#文件,资源文件,以及属性文件等。
但是,如果有一种现在还不想做的文件,或者是为未来预留的文件需要反编译的,这样主框架,就需要为这种能力预留接口。
但是主框架又不希望对其体系有多大破坏,这样,自然就想到了插件的方式。
世界上的实现有许多,但原理却那么几个,
如果了解COM或是其它的C++的,或是java之类的语言的插件体系,我们知道,不论如何做,有几个条件,是无法改变的:
1。 有一个所有插件共同遵守的基类或是抽象的基类,也就是所谓统一的接口。目的是受控插件接入主框架后,受主框架调度。
2。 有一套动态库加载的机制,如在win32中是loadlibrary之类的函数。
3。 有一个所有插件共同尊守的工厂函数,要说插件,com体系是比较严谨的。但COM一个最大的缺点就是与注册表依赖太大,使得一个系统中,不能有独立存在的插件,比较糟糕。而更加成功的插件,都是把DLL放在主程序身边。微软每次创新,都会搞一些退步的东西出来。
4。主框架不需要对插件形成静态依赖。
ok了,看到这,第一条,和第四条,是对我们当前的情况是有意义的。
再回头,看那段被注掉的代码,里面有一个类:
BamlResourceEntryNode要知道,这个类在ILSpy.BamlDecompiler,这个插件中。
不用看代码我们知道,因为第四条,只能ILSpy.BamlDecompiler依赖ILSpy,而不能反向依赖。
这样,我们不能用这个类。而只能用它的基类ResourceEntryNode,或是基类接口 ILSpyTreeNode.
但老实说,这一段被注掉的代码,本质上就有问题。
因为从整个函数不清IEnumerable<Tuple<string, string>> WriteResourceFilesInProject(LoadedAssembly assembly, DecompilationOptions options, HashSet<string> directories)
来看,
作者的意思,是把界面和底层操作分开。主框架作者不想在这个函数中,用到与界面元素相关的东西。
但ResourceEntryNode是与树相关的。
但还是回到开始,是来解决我们自己的问题的,不是关心这些家伙怎么想,为达目的,有时完全会忘掉这些没用的东西。的确有许多程序员,一辈子就在框架中,没有为世界做什么贡献,结果是,一个十分钟能解决的问题,结果要一天才能掉定。所以,需要用到ResourceEntryNode。
好吧,但今天心情好,就按它的框架来试试吧。
先规划一下,要改哪些地方:
1。要生成指向ResourceEntryNode指针的BamlResourceEntryNode对象,显然,这里只能用ResourceEntryNode工厂里的Create函数。
2。 调用ResourceEntryNode里的一个可以被重载的函数,指向BamlResourceEntryNode里面的反编译函数,
3。 反编译的结果存盘。
当然,还有一个最重要的事情需要考虑,就是主框架与插件之间,如何传递数据和参数。
综合考虑,首先我们要找到那个override函数,在
ILSpyTreeNode : SharpTreeNode 中,找到:
public abstract void Decompile(Language language, ITextOutput output, DecompilationOptions options);
就它了,然后开始工作:
1)ILSpy\Languages\CSharpLanguage.cs
if (fileName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) { MemoryStream ms = new MemoryStream(); entryStream.CopyTo(ms); ICSharpCode.ILSpy.TreeNodes.ILSpyTreeNode node = ICSharpCode.ILSpy.TreeNodes.ResourceEntryNode.Create(fileName, ms); if (node != null) { string xaml=null; try { ICSharpCode.ILSpy.TextView.AvalonEditTextOutput output = new ICSharpCode.ILSpy.TextView.AvalonEditTextOutput(); node.m_assembly = assembly; node.Decompile(this, output, options); xaml= output.GetDocument().Text; } catch (System.Xaml.XamlXmlWriterException) { } // ignore XAML writer exceptions catch (System.Exception ex) { } if (xaml != null) { File.WriteAllText(Path.Combine(options.SaveAsProjectDirectory, Path.ChangeExtension(fileName, ".xaml")), xaml); yield return Tuple.Create("Page", Path.ChangeExtension(fileName, ".xaml")); continue; } }2)ILSpy.BamlDecompiler\BamlResourceEntryNode.cs
public override void Decompile(Language language, ICSharpCode.Decompiler.ITextOutput output, DecompilationOptions options) { //language.WriteCommentLine(output, string.Format("{0} = {1}", key, base.data)); //AvalonEditTextOutput output = new AvalonEditTextOutput(); if (LoadBaml_haoyujie((AvalonEditTextOutput)output)) { // output. } } bool LoadBaml_haoyujie(AvalonEditTextOutput output) { var asm = this.m_assembly; Data.Position = 0; XDocument xamlDocument = LoadIntoDocument(asm.GetAssemblyResolver(), asm.AssemblyDefinition, Data); curstr = xamlDocument.ToString(); output.Write(curstr); return true; }
得承认,的确本人是比较懒的。
为了得到asm,直接改了基类,加了一个成员变量m_assembly:
public abstract class ILSpyTreeNode : SharpTreeNode { FilterSettings filterSettings; bool childrenNeedFiltering; public LoadedAssembly m_assembly; //haoyujie }
好了,解决了。
另外,有处代码改了:
public class ResourceEntryNode : ILSpyTreeNode { protected readonly string key; protected readonly Stream data; }
可执行文件
源码