背景:客户反馈系统出现一个Bug,花了1天时间搞定,但是准备下发程序的时候发现我编译后的dll比目前正在使用的DLL要小70K ,正常的处理是我这边修正好了程序,现场的实施同事做好测试即可下发使用,这次客户发飙了,还直接闹到客户领导那里去了。无奈,要到客户现场去了,必须要找到原因,给客户一个解释。本文就当时这几天处理这个问题的一次回顾和总结吧。
该情况一到手上,我初步判断可能的原因是:svn上的最新的源代码并非对应客户正在使用的Dll,有可能有同事在自己机器上(不受版本控制)对程序进行了修改且下发到科室了。因此首先找到当时负责维护改项目的几个同事,找到了最后离场的同事,询问是否有未提交的变更,但得到的答案是否定的。但我保留对这个回复的怀疑权。
看来只有找到两个程序集之间的差异才能最终知道是否有变更,变更了哪里,首先想到使用Reflector反编译,把反编译的代码和现在svn上的源码进行比对,但这真是一个苦逼且低效率的活,此Dll有8M,8M的代码各位看官可以想象得有多少行。。。。找了几个可能存在的变更类,并未发现任何蛛丝马迹,所看到的的代码均一致。想想这样对代码,把眼睛对瞎了都很难找到有代码变更的位置,因此做如下思考:按理来讲,70k的代码,至少是有增加很多类型,类、事件、委托、字段、方法、接口等,因此考虑自己写一个小工具把客户目前使用的Dll中的类型全部导出,只导出类型的定义即可;然后再把现在的svn上的源码编译成Dll,同样使用该工具导出全部类型,然后通过文本比对工具对比出两个程序集之间类型定义的差异。(嗯,看来这个方法不错,信心满满了)。具体操作:
选择程序集和存放的路径之后,点击“获取程序集类型”按钮,如下:
private void btnGetTypes_Click(object sender, EventArgs e) { this.lblMsg.Text = "正在导出,请稍后..."; this.Refresh(); this.btnGetTypes.Enabled = false; try { string dllPath = txtSelectedAssembly.Text; string savePath = txtAssemblyPath.Text; if (Directory.Exists(savePath)) { Directory.Delete(savePath, true); } Directory.CreateDirectory(savePath); getMember(dllPath, savePath); } catch { MessageBox.Show("转换出错:" + txtSelectedAssembly.Text); } this.lblMsg.Text = "转换结束。"; }
getMember(dllPath, savePath)方法如下:
public void getMember(string dllPath, string savePath) { string assemblyStr = dllPath; Assembly assembly = Assembly.LoadFrom(assemblyStr); System.IO.FileStream[] fileStream = assembly.GetFiles(); Type[] type = assembly.GetExportedTypes(); //BindingFlags bf = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public; foreach (Type t in type) { if (t.IsClass) { string filePath = savePath + "\\" + t.FullName + ".txt"; if (File.Exists(filePath)) { File.Delete(filePath); } FileStream f = File.Create(filePath); f.Dispose();//释放刚创建的txt文档资源 string str = string.Empty; str += t.ToString() + "\r\n"; foreach (MemberInfo mi in t.GetMembers()) { string miType = string.Empty; if (mi is FieldInfo) { miType = " FieldInfo"; } else if (mi is MethodInfo) { miType = " MethodInfo"; } else if (mi is EventInfo) { miType = " EventInfo"; } else if (mi is ConstructorInfo) { miType = " ConstructorInfo"; } else if (mi is PropertyInfo) { miType = " PropertyInfo"; } str += mi.ToString() + " " + miType + "\r\n"; } WriteMemberInfo(str, filePath); } } }
WriteMemberInfo(str,filePath)方法是将程序集中的信息写入记事本,如下:
public void WriteMemberInfo(string strMsg, string filePath) { StreamWriter sw = null; try { sw = new StreamWriter(filePath, true, Encoding.GetEncoding("gb2312")); string ifo = string.Empty;//DateTime.Now.ToString() + ":\r\n"; ifo += strMsg; sw.WriteLine(ifo); sw.Close(); } catch (Exception ex) { if (sw != null) { sw.Close(); sw.Dispose(); } } }
至此,程序集类型导出的就完成了,很简单的一个功能,看看导出的效果吧,为了避嫌,就不导公司的Dll了,咱就导System.Data.dll吧:
找一个类看看具体导出的内容:
字段、属性、方法、事件、构造函数都已经导出。
于是赶紧将两个Dll各自执行一遍,使用比对工具Beyond Compare进行批量比对,
整个程序集有150个类,内容比较我们使用二进制比较。最终发现只有5个类是不一致的:以为这些终找到元凶了,双击进去一看,让人大跌眼镜:
其他几个类基本也是这种情况,这尼玛哪里是什么不同,就一个在上一行,一个在下一行。我只能再次陷入沉思了。
这个结果证明:两个程序集之间在类型上不存在不一致(这里面没有考虑接口,由于公司的接口基本不动,因此此处就没去考虑),也就是说两个程序集之间差异的这70k的大小并没有增加类,没有增加方法,没有定义字段,没有写事件,没有写委托(由于公司的事件和委托基本不是和类同级,而是包含在类中,因此我这里只是导出类中的成员,特此说明,以免遭遇强烈拍砖)。难道这70k的代码全部写在了方法里面???这该动了多少方法啊?!心里有点汗颜了。
没办法,还是只能老老实实的去对代码了,但是这种事情叫人工去做,我只想说这真的会死人。
皱眉之际,还是想到老朋友好工具Reflector了,Reflector后来多了一个插件,叫做Reflector.FileDisassembler,可以将程序集中的类导出为一个源代码文件,果断尝试,如下:
导出的类文件如下:
在VS中查看,如下:
到这里,不管问题能不能解决,我不得不赞叹这个利器,由衷的敬意油然而生,中国人什么时候也能写出这种无与伦比的工具软件呢?哎,不管这些了,赶紧解决手里的事情才最重要,找了几个类,赶紧对比一下,结果让我大吃一惊,见图:
经过多个文件的比较,很显然,左边的代码是被动过对,或者说是被优化过的,如:
if ((this.treeView1.SelectedNode == null) || (this.treeView1.SelectedNode == this.treeView1.Nodes[0])) return null; return this.treeView1.SelectedNode.FullPath.Substring(5);
被变成了:
if ((this.treeView1.SelectedNode != null) && (this.treeView1.SelectedNode != this.treeView1.Nodes[0])) return this.treeView1.SelectedNode.FullPath.Substring(5); return null;
再如:
DataSet nodePath = Function.IntegrateEPR.GetNodePath();
被变成:
DataSet nodePath = Function.get_IntegrateEPR().GetNodePath();
属性全部变成方法了, 明显的,这都不是人干的,是编译器做了转换了。我恍然大悟,原来少的这70k就是这样少的。但这又是为什么呢?难道是编译环境不一样造成?带着这个怀疑,我用win7和xp系统分别编译同一个项目的代码,发现确实存在差异,win7下编译的代码更大,xp环境下编译的代码要小。为什么xp和win7环境编译的dll大小不一致,请各位发表下自己的观点和看法,多谢!