diff options
Diffstat (limited to 'src/samples/Dtf/DDiff/MsiDiffEngine.cs')
-rw-r--r-- | src/samples/Dtf/DDiff/MsiDiffEngine.cs | 276 |
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 | |||
3 | using System; | ||
4 | using System.IO; | ||
5 | using System.Collections; | ||
6 | using System.Collections.Generic; | ||
7 | using WixToolset.Dtf.WindowsInstaller; | ||
8 | using WixToolset.Dtf.WindowsInstaller.Package; | ||
9 | |||
10 | namespace 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 | } | ||