前一篇中,只是对单一文件保存,进行了改进。
但在反编译时,还要一个一个的处理。
面对海量的代码,更需要一个能将所有的保存的工具。磨刀不误砍柴工,再改进一下工具。
首先找到在:
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 的框架来思考的,当时可能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;
}