周一上班接着补充。
这些天状态的确低迷。上午对着电脑发了好一会呆。但活还是要干的。
上午的改进完了以后,面对一些小问题。
1。如何自动在反编译后的工程中,加入引用路径。
2。 在资源文件中,如果用到了模板类,则编不过。
- 如何自动在反编译后的工程中,加入引用路径。
这个走了一点弯路。原以来为这个信息在.csproj文件,原来不是这样,而是在.csproj.user中。
有些吃惊,一直以为那个文件没有用。
先说一下为什么要有这样的需求:
因为正在看的程序,内容很多,但所感兴趣的东西,只是冰山一角,虽然这个一角也不小,所以,只需要看几个DLL就OK了。
但这些DLL是引用了其它的DLL的,所以,加引用路径是最简单的事情。当然,可以事先把一些可以注册的库用:gacutil /i
注册一下,也是个办法。
这里选择用引用路径。
发现引用路径在.csproj.user中,就好办了,只要找到一个地方,加一段代码就OK了:
在:ILSpy\TextView\DecompilerTextView.cs
中改进一下如下的代码就OK了。
Task<AvalonEditTextOutput> SaveToDiskAsync(DecompilationContext context, string fileName)
{
TaskCompletionSource<AvalonEditTextOutput> tcs = new TaskCompletionSource<AvalonEditTextOutput>();
Thread thread = new Thread(new ThreadStart(
delegate {
try {
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
//haoyujie 写入工程文件
using (StreamWriter w = new StreamWriter(fileName)) {
try {
DecompileNodes(context, new PlainTextOutput(w));
} catch (OperationCanceledException) {
w.WriteLine();
w.WriteLine("Decompiled was cancelled.");
throw;
}
}
//把user设置拷到指定位置
//最后,把引用路径模板拷过去,因为需要引用许多其它的库,引用路径在这个文件中指定
string strUserTmppath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory + "hayujie_usertmp.usertmp");
string strDescUserpath = fileName + ".user";
FileInfo fi = new FileInfo(strUserTmppath);
if (File.Exists(strDescUserpath))
{
File.Delete(strDescUserpath);
}
fi.CopyTo(strDescUserpath);
stopwatch.Stop();
AvalonEditTextOutput output = new AvalonEditTextOutput();
output.WriteLine("Decompilation complete in " + stopwatch.Elapsed.TotalSeconds.ToString("F1") + " seconds.");
output.WriteLine();
output.AddButton(null, "Open Explorer", delegate { Process.Start("explorer", "/select,\"" + fileName + "\""); });
output.WriteLine();
tcs.SetResult(output);
} catch (OperationCanceledException) {
tcs.SetCanceled();
#if DEBUG
} catch (AggregateException ex) {
tcs.SetException(ex);
#else
} catch (Exception ex) {
tcs.SetException(ex);
#endif
}
}));
thread.Start();
return tcs.Task;
}
至于里面提到的模板文件hayujie_usertmp.usertmp,可以自己想名字,内容也可以自己写,给出一个例子:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ReferencePath>E:\work\lib\</ReferencePath>
</PropertyGroup>
</Project>
提不起精神,感觉代码写得对不起观众。
- 在资源文件中,如果用到了模板类,则编不过。
在反编译过程中,发现有一个resource.baml反编译不成功。
仔细分析后一天多,才明白,这里面用到了模板类,不了解在C#中叫什么,在C++里要模板,
也就是说,在一个核心库中定义了一个模板类,然后这个模板类的实现(c#是不是叫装箱),相当于一个类,在使用者的库中才被编译器实现,也就是说,实际上它在两个地方,当然在C++只会存在于一个地方,因为C++的模板,是预编译阶段完成的。
但好象C#的实现,有点象容器(所以模板在C#中好象就叫容器类),两边的库都要用到,这也容易理解,C#是解释性语言。
各有各的好外,总得来说,还是装箱这种方式,简单得多。
而这个类,恰好被resource.baml里面引用到了。而ILSPY就是错了。
具体出错的内容
模板类名`[[实现类的命名空间,类名,版本号,KEY]]
注意,那个`,是最奇怪的,不清楚是怎么出来的,60H,就是键盘上esc 下面的那个键。见鬼啊。而正常的类名,只有一个模板类名。然后ILSpy ,以大无畏的猜测的精视,猜出前面是命名空间,后面是类名,当然,大多数时候,它都是对的。
对微软的统一语言进行时的二进制码不熟,所以,一步步逆推到这,就停下了,因为再整,就对不起老板给开的工资了,毕竟还有工作要做。
如果有哥们对二进制码非常熟的,请告知。
从内存中,看到ILSPY对于模板类的反编译,并不正确之后,决定稍稍修剪一下代码,虽然结果不完全正确,也总比什么都不出来强。
后来,真的可用了。
这里,把改完的代码放在这里:
ILSpy.BamlDecompiler\Ricciolo.StylesExplorer.MarkupReflection\XmlBamlReader.cs
void ReadTypeInfo()
{
short typeId = reader.ReadInt16();
short assemblyId = reader.ReadInt16();
string fullName = reader.ReadString();
assemblyId = (short)(assemblyId & 0xfff);
TypeDeclaration declaration;
int indexpp = fullName.IndexOf("[["); //说明这个类是模板类
if (indexpp >= 0)
{
int indexppend = fullName.IndexOf("]]"); //说明这个类是模板类
fullName=fullName.Substring(indexpp+2,indexppend-indexpp-2);
}
if (fullName.IndexOf(',')>=0)
{
int douhao = fullName.IndexOf(',');
fullName = fullName.Substring(0,douhao);
}
int length = fullName.LastIndexOf('.');
if (length != -1)
{
string name = fullName.Substring(length + 1);
string namespaceName = fullName.Substring(0, length);
declaration = new TypeDeclaration(this, this.Resolver, name, namespaceName, assemblyId);
}
else
{
declaration = new TypeDeclaration(this, this.Resolver, fullName, string.Empty, assemblyId);
}
this.typeTable.Add(typeId, declaration);
}
这段代码,对原有的代码进行了修改,最后可以工作,但结果应该是不正确的,但也不耽误用了。
另外,这里,把在逆推的过程中,用到的代码,也贴出来,减少大家逆推的时间。
因为逆推有几个难题:
1。 出错的时候,读入错误信息的地方,早就过去了。
2。文件流stream,是顺序的,不是随机的,这样,就不能再读一次。
所以,加了这段代码分析内存:
ILSpy.BamlDecompiler\Ricciolo.StylesExplorer.MarkupReflection\BamlBinaryReader.cs
public override string ReadString()
{
//haoyujie,为调试资源错误,因为现在在资源中,如果出现模板类,就会出错。
string strInner = base.ReadString();
if (strInner.StartsWith("出错的那个模板的类的名称")) //这句是重点,加入拦截
{
int ll = 500;
Stream basestream = base.BaseStream;
byte[] buffer = new byte[ll];
if (basestream.CanSeek)
{
basestream.Seek(-ll, SeekOrigin.Current);
}
buffer = base.ReadBytes(ll);
}
return strInner;
}
就这么多吧。
这个系列,也就基本上结束了。