diff options
Diffstat (limited to '')
-rw-r--r-- | src/dtf/WixToolset.Dtf.WindowsInstaller/DatabaseTransform.cs | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/DatabaseTransform.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/DatabaseTransform.cs new file mode 100644 index 00000000..fa843012 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/DatabaseTransform.cs | |||
@@ -0,0 +1,278 @@ | |||
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 | namespace WixToolset.Dtf.WindowsInstaller | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using System.Globalization; | ||
8 | using System.Diagnostics.CodeAnalysis; | ||
9 | |||
10 | public partial class Database | ||
11 | { | ||
12 | /// <summary> | ||
13 | /// Creates a transform that, when applied to the object database, results in the reference database. | ||
14 | /// </summary> | ||
15 | /// <param name="referenceDatabase">Database that does not include the changes</param> | ||
16 | /// <param name="transformFile">Name of the generated transform file, or null to only | ||
17 | /// check whether or not the two database are identical</param> | ||
18 | /// <returns>true if a transform is generated, or false if a transform is not generated | ||
19 | /// because there are no differences between the two databases.</returns> | ||
20 | /// <exception cref="InstallerException">the transform could not be generated</exception> | ||
21 | /// <exception cref="InvalidHandleException">a Database handle is invalid</exception> | ||
22 | /// <remarks><p> | ||
23 | /// A transform can add non-primary key columns to the end of a table. A transform cannot | ||
24 | /// be created that adds primary key columns to a table. A transform cannot be created that | ||
25 | /// changes the order, names, or definitions of columns. | ||
26 | /// </p><p> | ||
27 | /// If the transform is to be applied during an installation you must use the | ||
28 | /// <see cref="Database.CreateTransformSummaryInfo"/> method to populate the | ||
29 | /// summary information stream. | ||
30 | /// </p><p> | ||
31 | /// Win32 MSI API: | ||
32 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabasegeneratetransform.asp">MsiDatabaseGenerateTransform</a> | ||
33 | /// </p></remarks> | ||
34 | [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] | ||
35 | public bool GenerateTransform(Database referenceDatabase, string transformFile) | ||
36 | { | ||
37 | if (referenceDatabase == null) | ||
38 | { | ||
39 | throw new ArgumentNullException("referenceDatabase"); | ||
40 | } | ||
41 | |||
42 | if (String.IsNullOrEmpty(transformFile)) | ||
43 | { | ||
44 | throw new ArgumentNullException("transformFile"); | ||
45 | } | ||
46 | |||
47 | uint ret = NativeMethods.MsiDatabaseGenerateTransform((int) this.Handle, (int) referenceDatabase.Handle, transformFile, 0, 0); | ||
48 | if (ret == (uint) NativeMethods.Error.NO_DATA) | ||
49 | { | ||
50 | return false; | ||
51 | } | ||
52 | else if (ret != 0) | ||
53 | { | ||
54 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
55 | } | ||
56 | return true; | ||
57 | } | ||
58 | |||
59 | /// <summary> | ||
60 | /// Creates and populates the summary information stream of an existing transform file, and | ||
61 | /// fills in the properties with the base and reference ProductCode and ProductVersion. | ||
62 | /// </summary> | ||
63 | /// <param name="referenceDatabase">Database that does not include the changes</param> | ||
64 | /// <param name="transformFile">Name of the generated transform file</param> | ||
65 | /// <param name="errors">Error conditions that should be suppressed | ||
66 | /// when the transform is applied</param> | ||
67 | /// <param name="validations">Defines which properties should be validated | ||
68 | /// to verify that this transform can be applied to a database.</param> | ||
69 | /// <exception cref="InstallerException">the transform summary info could not be | ||
70 | /// generated</exception> | ||
71 | /// <exception cref="InvalidHandleException">a Database handle is invalid</exception> | ||
72 | /// <remarks><p> | ||
73 | /// Win32 MSI API: | ||
74 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msicreatetransformsummaryinfo.asp">MsiCreateTransformSummaryInfo</a> | ||
75 | /// </p></remarks> | ||
76 | [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] | ||
77 | public void CreateTransformSummaryInfo( | ||
78 | Database referenceDatabase, | ||
79 | string transformFile, | ||
80 | TransformErrors errors, | ||
81 | TransformValidations validations) | ||
82 | { | ||
83 | if (referenceDatabase == null) | ||
84 | { | ||
85 | throw new ArgumentNullException("referenceDatabase"); | ||
86 | } | ||
87 | |||
88 | if (String.IsNullOrEmpty(transformFile)) | ||
89 | { | ||
90 | throw new ArgumentNullException("transformFile"); | ||
91 | } | ||
92 | |||
93 | uint ret = NativeMethods.MsiCreateTransformSummaryInfo( | ||
94 | (int) this.Handle, | ||
95 | (int) referenceDatabase.Handle, | ||
96 | transformFile, | ||
97 | (int) errors, | ||
98 | (int) validations); | ||
99 | if (ret != 0) | ||
100 | { | ||
101 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /// <summary> | ||
106 | /// Apply a transform to the database, recording the changes in the "_TransformView" table. | ||
107 | /// </summary> | ||
108 | /// <param name="transformFile">Path to the transform file</param> | ||
109 | /// <exception cref="InstallerException">the transform could not be applied</exception> | ||
110 | /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> | ||
111 | /// <remarks><p> | ||
112 | /// Win32 MSI API: | ||
113 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseapplytransform.asp">MsiDatabaseApplyTransform</a> | ||
114 | /// </p></remarks> | ||
115 | public void ViewTransform(string transformFile) | ||
116 | { | ||
117 | TransformErrors transformErrors = | ||
118 | TransformErrors.AddExistingRow | | ||
119 | TransformErrors.DelMissingRow | | ||
120 | TransformErrors.AddExistingTable | | ||
121 | TransformErrors.DelMissingTable | | ||
122 | TransformErrors.UpdateMissingRow | | ||
123 | TransformErrors.ChangeCodePage | | ||
124 | TransformErrors.ViewTransform; | ||
125 | this.ApplyTransform(transformFile, transformErrors); | ||
126 | } | ||
127 | |||
128 | /// <summary> | ||
129 | /// Apply a transform to the database, suppressing any error conditions | ||
130 | /// specified by the transform's summary information. | ||
131 | /// </summary> | ||
132 | /// <param name="transformFile">Path to the transform file</param> | ||
133 | /// <exception cref="InstallerException">the transform could not be applied</exception> | ||
134 | /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> | ||
135 | /// <remarks><p> | ||
136 | /// Win32 MSI API: | ||
137 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseapplytransform.asp">MsiDatabaseApplyTransform</a> | ||
138 | /// </p></remarks> | ||
139 | public void ApplyTransform(string transformFile) | ||
140 | { | ||
141 | if (String.IsNullOrEmpty(transformFile)) | ||
142 | { | ||
143 | throw new ArgumentNullException("transformFile"); | ||
144 | } | ||
145 | |||
146 | TransformErrors errorConditionsToSuppress; | ||
147 | using (SummaryInfo transformSummInfo = new SummaryInfo(transformFile, false)) | ||
148 | { | ||
149 | int errorConditions = transformSummInfo.CharacterCount & 0xFFFF; | ||
150 | errorConditionsToSuppress = (TransformErrors) errorConditions; | ||
151 | } | ||
152 | this.ApplyTransform(transformFile, errorConditionsToSuppress); | ||
153 | } | ||
154 | |||
155 | /// <summary> | ||
156 | /// Apply a transform to the database, specifying error conditions to suppress. | ||
157 | /// </summary> | ||
158 | /// <param name="transformFile">Path to the transform file</param> | ||
159 | /// <param name="errorConditionsToSuppress">Error conditions that are to be suppressed</param> | ||
160 | /// <exception cref="InstallerException">the transform could not be applied</exception> | ||
161 | /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> | ||
162 | /// <remarks><p> | ||
163 | /// Win32 MSI API: | ||
164 | /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseapplytransform.asp">MsiDatabaseApplyTransform</a> | ||
165 | /// </p></remarks> | ||
166 | public void ApplyTransform(string transformFile, TransformErrors errorConditionsToSuppress) | ||
167 | { | ||
168 | if (String.IsNullOrEmpty(transformFile)) | ||
169 | { | ||
170 | throw new ArgumentNullException("transformFile"); | ||
171 | } | ||
172 | |||
173 | uint ret = NativeMethods.MsiDatabaseApplyTransform((int) this.Handle, transformFile, (int) errorConditionsToSuppress); | ||
174 | if (ret != 0) | ||
175 | { | ||
176 | throw InstallerException.ExceptionFromReturnCode(ret); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /// <summary> | ||
181 | /// Checks whether a transform is valid for this Database, according to its validation data and flags. | ||
182 | /// </summary> | ||
183 | /// <param name="transformFile">Path to the transform file</param> | ||
184 | /// <returns>true if the transform can be validly applied to this Database; false otherwise</returns> | ||
185 | /// <exception cref="InstallerException">the transform could not be applied</exception> | ||
186 | /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> | ||
187 | public bool IsTransformValid(string transformFile) | ||
188 | { | ||
189 | if (String.IsNullOrEmpty(transformFile)) | ||
190 | { | ||
191 | throw new ArgumentNullException("transformFile"); | ||
192 | } | ||
193 | |||
194 | using (SummaryInfo transformSummInfo = new SummaryInfo(transformFile, false)) | ||
195 | { | ||
196 | return this.IsTransformValid(transformSummInfo); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /// <summary> | ||
201 | /// Checks whether a transform is valid for this Database, according to its SummaryInfo data. | ||
202 | /// </summary> | ||
203 | /// <param name="transformSummaryInfo">SummaryInfo data of a transform file</param> | ||
204 | /// <returns>true if the transform can be validly applied to this Database; false otherwise</returns> | ||
205 | /// <exception cref="InstallerException">error processing summary info</exception> | ||
206 | /// <exception cref="InvalidHandleException">the Database or SummaryInfo handle is invalid</exception> | ||
207 | public bool IsTransformValid(SummaryInfo transformSummaryInfo) | ||
208 | { | ||
209 | if (transformSummaryInfo == null) | ||
210 | { | ||
211 | throw new ArgumentNullException("transformSummaryInfo"); | ||
212 | } | ||
213 | |||
214 | string[] rev = transformSummaryInfo.RevisionNumber.Split(new char[] { ';' }, 3); | ||
215 | string targetProductCode = rev[0].Substring(0, 38); | ||
216 | string targetProductVersion = rev[0].Substring(38); | ||
217 | string upgradeCode = rev[2]; | ||
218 | |||
219 | string[] templ = transformSummaryInfo.Template.Split(new char[] { ';' }, 2); | ||
220 | int targetProductLanguage = 0; | ||
221 | if (templ.Length >= 2 && templ[1].Length > 0) | ||
222 | { | ||
223 | targetProductLanguage = Int32.Parse(templ[1], CultureInfo.InvariantCulture.NumberFormat); | ||
224 | } | ||
225 | |||
226 | int flags = transformSummaryInfo.CharacterCount; | ||
227 | int validateFlags = flags >> 16; | ||
228 | |||
229 | string thisProductCode = this.ExecutePropertyQuery("ProductCode"); | ||
230 | string thisProductVersion = this.ExecutePropertyQuery("ProductVersion"); | ||
231 | string thisUpgradeCode = this.ExecutePropertyQuery("UpgradeCode"); | ||
232 | string thisProductLang = this.ExecutePropertyQuery("ProductLanguage"); | ||
233 | int thisProductLanguage = 0; | ||
234 | if (!String.IsNullOrEmpty(thisProductLang)) | ||
235 | { | ||
236 | thisProductLanguage = Int32.Parse(thisProductLang, CultureInfo.InvariantCulture.NumberFormat); | ||
237 | } | ||
238 | |||
239 | if ((validateFlags & (int) TransformValidations.Product) != 0 && | ||
240 | thisProductCode != targetProductCode) | ||
241 | { | ||
242 | return false; | ||
243 | } | ||
244 | |||
245 | if ((validateFlags & (int) TransformValidations.UpgradeCode) != 0 && | ||
246 | thisUpgradeCode != upgradeCode) | ||
247 | { | ||
248 | return false; | ||
249 | } | ||
250 | |||
251 | if ((validateFlags & (int) TransformValidations.Language) != 0 && | ||
252 | targetProductLanguage != 0 && thisProductLanguage != targetProductLanguage) | ||
253 | { | ||
254 | return false; | ||
255 | } | ||
256 | |||
257 | Version thisProductVer = new Version(thisProductVersion); | ||
258 | Version targetProductVer = new Version(targetProductVersion); | ||
259 | if ((validateFlags & (int) TransformValidations.UpdateVersion) != 0) | ||
260 | { | ||
261 | if (thisProductVer.Major != targetProductVer.Major) return false; | ||
262 | if (thisProductVer.Minor != targetProductVer.Minor) return false; | ||
263 | if (thisProductVer.Build != targetProductVer.Build) return false; | ||
264 | } | ||
265 | else if ((validateFlags & (int) TransformValidations.MinorVersion) != 0) | ||
266 | { | ||
267 | if (thisProductVer.Major != targetProductVer.Major) return false; | ||
268 | if (thisProductVer.Minor != targetProductVer.Minor) return false; | ||
269 | } | ||
270 | else if ((validateFlags & (int) TransformValidations.MajorVersion) != 0) | ||
271 | { | ||
272 | if (thisProductVer.Major != targetProductVer.Major) return false; | ||
273 | } | ||
274 | |||
275 | return true; | ||
276 | } | ||
277 | } | ||
278 | } | ||