aboutsummaryrefslogtreecommitdiff
path: root/src/samples/Dtf/DDiff/MsiDiffEngine.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/samples/Dtf/DDiff/MsiDiffEngine.cs')
-rw-r--r--src/samples/Dtf/DDiff/MsiDiffEngine.cs276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/samples/Dtf/DDiff/MsiDiffEngine.cs b/src/samples/Dtf/DDiff/MsiDiffEngine.cs
new file mode 100644
index 00000000..91bc2969
--- /dev/null
+++ b/src/samples/Dtf/DDiff/MsiDiffEngine.cs
@@ -0,0 +1,276 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System;
4using System.IO;
5using System.Collections;
6using System.Collections.Generic;
7using WixToolset.Dtf.WindowsInstaller;
8using WixToolset.Dtf.WindowsInstaller.Package;
9
10namespace WixToolset.Dtf.Samples.DDiff
11{
12 public class MsiDiffEngine : IDiffEngine
13 {
14 public MsiDiffEngine()
15 {
16 }
17
18 protected bool IsMsiDatabase(string file)
19 {
20 // TODO: use something smarter?
21 switch(Path.GetExtension(file).ToLower())
22 {
23 case ".msi": return true;
24 case ".msm": return true;
25 case ".pcp": return true;
26 default : return false;
27 }
28 }
29
30 protected bool IsMspPatch(string file)
31 {
32 // TODO: use something smarter?
33 switch(Path.GetExtension(file).ToLower())
34 {
35 case ".msp": return true;
36 default : return false;
37 }
38 }
39
40 public virtual float GetDiffQuality(string diffInput1, string diffInput2, string[] options, IDiffEngineFactory diffFactory)
41 {
42 if(diffInput1 != null && File.Exists(diffInput1) &&
43 diffInput2 != null && File.Exists(diffInput2) &&
44 (IsMsiDatabase(diffInput1) || IsMsiDatabase(diffInput2)))
45 {
46 return .70f;
47 }
48 else if(diffInput1 != null && File.Exists(diffInput1) &&
49 diffInput2 != null && File.Exists(diffInput2) &&
50 (IsMspPatch(diffInput1) || IsMspPatch(diffInput2)))
51 {
52 return .60f;
53 }
54 else
55 {
56 return 0;
57 }
58 }
59
60 public virtual bool GetDiff(string diffInput1, string diffInput2, string[] options, TextWriter diffOutput, string linePrefix, IDiffEngineFactory diffFactory)
61 {
62 bool difference = false;
63 Database db1 = new Database(diffInput1, DatabaseOpenMode.ReadOnly);
64 Database db2 = new Database(diffInput2, DatabaseOpenMode.ReadOnly);
65
66 if(GetSummaryInfoDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true;
67 if(GetDatabaseDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true;
68 if(GetStreamsDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true;
69
70 db1.Close();
71 db2.Close();
72 return difference;
73 }
74
75 protected bool GetSummaryInfoDiff(Database db1, Database db2, string[] options, TextWriter diffOutput, string linePrefix, IDiffEngineFactory diffFactory)
76 {
77 bool difference = false;
78
79 SummaryInfo summInfo1 = db1.SummaryInfo;
80 SummaryInfo summInfo2 = db2.SummaryInfo;
81 if(summInfo1.Title != summInfo2.Title ) { diffOutput.WriteLine("{0}SummaryInformation.Title {{{1}}}->{{{2}}}", linePrefix, summInfo1.Title, summInfo2.Title); difference = true; }
82 if(summInfo1.Subject != summInfo2.Subject ) { diffOutput.WriteLine("{0}SummaryInformation.Subject {{{1}}}->{{{2}}}", linePrefix, summInfo1.Subject, summInfo2.Subject); difference = true; }
83 if(summInfo1.Author != summInfo2.Author ) { diffOutput.WriteLine("{0}SummaryInformation.Author {{{1}}}->{{{2}}}", linePrefix, summInfo1.Author, summInfo2.Author); difference = true; }
84 if(summInfo1.Keywords != summInfo2.Keywords ) { diffOutput.WriteLine("{0}SummaryInformation.Keywords {{{1}}}->{{{2}}}", linePrefix, summInfo1.Keywords, summInfo2.Keywords); difference = true; }
85 if(summInfo1.Comments != summInfo2.Comments ) { diffOutput.WriteLine("{0}SummaryInformation.Comments {{{1}}}->{{{2}}}", linePrefix, summInfo1.Comments, summInfo2.Comments); difference = true; }
86 if(summInfo1.Template != summInfo2.Template ) { diffOutput.WriteLine("{0}SummaryInformation.Template {{{1}}}->{{{2}}}", linePrefix, summInfo1.Template, summInfo2.Template); difference = true; }
87 if(summInfo1.LastSavedBy != summInfo2.LastSavedBy ) { diffOutput.WriteLine("{0}SummaryInformation.LastSavedBy {{{1}}}->{{{2}}}", linePrefix, summInfo1.LastSavedBy, summInfo2.LastSavedBy); difference = true; }
88 if(summInfo1.RevisionNumber != summInfo2.RevisionNumber) { diffOutput.WriteLine("{0}SummaryInformation.RevisionNumber {{{1}}}->{{{2}}}", linePrefix, summInfo1.RevisionNumber, summInfo2.RevisionNumber); difference = true; }
89 if(summInfo1.CreatingApp != summInfo2.CreatingApp ) { diffOutput.WriteLine("{0}SummaryInformation.CreatingApp {{{1}}}->{{{2}}}", linePrefix, summInfo1.CreatingApp, summInfo2.CreatingApp); difference = true; }
90 if(summInfo1.LastPrintTime != summInfo2.LastPrintTime ) { diffOutput.WriteLine("{0}SummaryInformation.LastPrintTime {{{1}}}->{{{2}}}", linePrefix, summInfo1.LastPrintTime, summInfo2.LastPrintTime); difference = true; }
91 if(summInfo1.CreateTime != summInfo2.CreateTime ) { diffOutput.WriteLine("{0}SummaryInformation.CreateTime {{{1}}}->{{{2}}}", linePrefix, summInfo1.CreateTime, summInfo2.CreateTime); difference = true; }
92 if(summInfo1.LastSaveTime != summInfo2.LastSaveTime ) { diffOutput.WriteLine("{0}SummaryInformation.LastSaveTime {{{1}}}->{{{2}}}", linePrefix, summInfo1.LastSaveTime, summInfo2.LastSaveTime); difference = true; }
93 if(summInfo1.CodePage != summInfo2.CodePage ) { diffOutput.WriteLine("{0}SummaryInformation.Codepage {{{1}}}->{{{2}}}", linePrefix, summInfo1.CodePage, summInfo2.CodePage); difference = true; }
94 if(summInfo1.PageCount != summInfo2.PageCount ) { diffOutput.WriteLine("{0}SummaryInformation.PageCount {{{1}}}->{{{2}}}", linePrefix, summInfo1.PageCount, summInfo2.PageCount); difference = true; }
95 if(summInfo1.WordCount != summInfo2.WordCount ) { diffOutput.WriteLine("{0}SummaryInformation.WordCount {{{1}}}->{{{2}}}", linePrefix, summInfo1.WordCount, summInfo2.WordCount); difference = true; }
96 if(summInfo1.CharacterCount != summInfo2.CharacterCount) { diffOutput.WriteLine("{0}SummaryInformation.CharacterCount {{{1}}}->{{{2}}}", linePrefix, summInfo1.CharacterCount, summInfo2.CharacterCount); difference = true; }
97 if(summInfo1.Security != summInfo2.Security ) { diffOutput.WriteLine("{0}SummaryInformation.Security {{{1}}}->{{{2}}}", linePrefix, summInfo1.Security, summInfo2.Security); difference = true; }
98 summInfo1.Close();
99 summInfo2.Close();
100
101 return difference;
102 }
103
104 protected bool GetDatabaseDiff(Database db1, Database db2, string[] options, TextWriter diffOutput, string linePrefix, IDiffEngineFactory diffFactory)
105 {
106 bool difference = false;
107
108 string tempFile = Path.GetTempFileName();
109 if(db2.GenerateTransform(db1, tempFile))
110 {
111 difference = true;
112
113 Database db = db1;
114 db.ViewTransform(tempFile);
115
116 string row, column, change;
117 using (View view = db.OpenView("SELECT `Table`, `Column`, `Row`, `Data`, `Current` " +
118 "FROM `_TransformView` ORDER BY `Table`, `Row`"))
119 {
120 view.Execute();
121
122 foreach (Record rec in view) using (rec)
123 {
124 column = String.Format("{0} {1}", rec[1], rec[2]);
125 change = "";
126 if (rec.IsNull(3))
127 {
128 row = "<DDL>";
129 if (!rec.IsNull(4))
130 {
131 change = "[" + rec[5] + "]: " + DecodeColDef(rec.GetInteger(4));
132 }
133 }
134 else
135 {
136 row = "[" + String.Join(",", rec.GetString(3).Split('\t')) + "]";
137 if (rec.GetString(2) != "INSERT" && rec.GetString(2) != "DELETE")
138 {
139 column = String.Format("{0}.{1}", rec[1], rec[2]);
140 change = "{" + rec[5] + "}->{" + rec[4] + "}";
141 }
142 }
143
144 diffOutput.WriteLine("{0}{1,-25} {2} {3}", linePrefix, column, row, change);
145 }
146 }
147 }
148 File.Delete(tempFile);
149
150 return difference;
151 }
152
153 private string DecodeColDef(int colDef)
154 {
155 const int icdLong = 0x0000;
156 const int icdShort = 0x0400;
157 const int icdObject = 0x0800;
158 const int icdString = 0x0C00;
159 const int icdTypeMask = 0x0F00;
160 const int icdNullable = 0x1000;
161 const int icdPrimaryKey = 0x2000;
162
163 string def = "";
164 switch(colDef & (icdTypeMask))
165 {
166 case icdLong : def = "LONG"; break;
167 case icdShort : def = "SHORT"; break;
168 case icdObject: def = "OBJECT"; break;
169 case icdString: def = "CHAR[" + (colDef & 0xFF) + "]"; break;
170 }
171 if((colDef & icdNullable) != 0)
172 {
173 def = def + " NOT NULL";
174 }
175 if((colDef & icdPrimaryKey) != 0)
176 {
177 def = def + " PRIMARY KEY";
178 }
179 return def;
180 }
181
182 protected bool GetStreamsDiff(Database db1, Database db2, string[] options, TextWriter diffOutput, string linePrefix, IDiffEngineFactory diffFactory)
183 {
184 bool difference = false;
185
186 IList<string> streams1List = db1.ExecuteStringQuery("SELECT `Name` FROM `_Streams`");
187 IList<string> streams2List = db2.ExecuteStringQuery("SELECT `Name` FROM `_Streams`");
188 string[] streams1 = new string[streams1List.Count];
189 string[] streams2 = new string[streams2List.Count];
190 streams1List.CopyTo(streams1, 0);
191 streams2List.CopyTo(streams2, 0);
192
193 IComparer caseInsComp = CaseInsensitiveComparer.Default;
194 Array.Sort(streams1, caseInsComp);
195 Array.Sort(streams2, caseInsComp);
196
197 for (int i1 = 0, i2 = 0; i1 < streams1.Length || i2 < streams2.Length; )
198 {
199 int comp;
200 if (i1 == streams1.Length)
201 {
202 comp = 1;
203 }
204 else if (i2 == streams2.Length)
205 {
206 comp = -1;
207 }
208 else
209 {
210 comp = caseInsComp.Compare(streams1[i1], streams2[i2]);
211 }
212 if(comp < 0)
213 {
214 diffOutput.WriteLine("{0}< {1}", linePrefix, streams1[i1]);
215 i1++;
216 difference = true;
217 }
218 else if(comp > 0)
219 {
220 diffOutput.WriteLine("{0}> {1}", linePrefix, streams2[i2]);
221 i2++;
222 difference = true;
223 }
224 else
225 {
226 if(streams1[i1] != ("" + ((char)5) + "SummaryInformation"))
227 {
228 string tempFile1 = Path.GetTempFileName();
229 string tempFile2 = Path.GetTempFileName();
230
231 using (View view = db1.OpenView(String.Format("SELECT `Data` FROM `_Streams` WHERE `Name` = '{0}'", streams1[i1])))
232 {
233 view.Execute();
234
235 using (Record rec = view.Fetch())
236 {
237 rec.GetStream(1, tempFile1);
238 }
239 }
240
241 using (View view = db2.OpenView(String.Format("SELECT `Data` FROM `_Streams` WHERE `Name` = '{0}'", streams2[i2])))
242 {
243 view.Execute();
244
245 using (Record rec = view.Fetch())
246 {
247 rec.GetStream(1, tempFile2);
248 }
249 }
250
251 IDiffEngine diffEngine = diffFactory.GetDiffEngine(tempFile1, tempFile2, options);
252 StringWriter sw = new StringWriter();
253 if(diffEngine.GetDiff(tempFile1, tempFile2, options, sw, linePrefix + " ", diffFactory))
254 {
255 diffOutput.WriteLine("{0}{1}", linePrefix, streams1[i1]);
256 diffOutput.Write(sw.ToString());
257 difference = true;
258 }
259
260 File.Delete(tempFile1);
261 File.Delete(tempFile2);
262 }
263 i1++;
264 i2++;
265 }
266 }
267
268 return difference;
269 }
270
271 public virtual IDiffEngine Clone()
272 {
273 return new MsiDiffEngine();
274 }
275 }
276}