diff options
Diffstat (limited to 'src/dtf/WixToolset.Dtf.WindowsInstaller.Package/PatchPackage.cs')
-rw-r--r-- | src/dtf/WixToolset.Dtf.WindowsInstaller.Package/PatchPackage.cs | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller.Package/PatchPackage.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller.Package/PatchPackage.cs new file mode 100644 index 00000000..54bd2b93 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller.Package/PatchPackage.cs | |||
@@ -0,0 +1,259 @@ | |||
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.Package | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using System.Text; | ||
8 | using System.Collections; | ||
9 | using System.Globalization; | ||
10 | using System.Runtime.InteropServices; | ||
11 | |||
12 | /// <summary> | ||
13 | /// Provides access to convenient properties and operations on a patch package (.MSP). | ||
14 | /// </summary> | ||
15 | public class PatchPackage : Database | ||
16 | { | ||
17 | /// <summary> | ||
18 | /// Creates a new patch package object; opening the patch database in read-only mode. | ||
19 | /// </summary> | ||
20 | /// <param name="packagePath">Path to the patch package (.MSP)</param> | ||
21 | /// <remarks>The PatchPackage object only opens the patch database in read-only mode, because | ||
22 | /// transforms (sub-storages) cannot be read if the database is open in read-write mode.</remarks> | ||
23 | public PatchPackage(string packagePath) | ||
24 | : base(packagePath, (DatabaseOpenMode) ((int) DatabaseOpenMode.ReadOnly | 32)) | ||
25 | // TODO: figure out what to do about DatabaseOpenMode.Patch | ||
26 | { | ||
27 | } | ||
28 | |||
29 | /// <summary> | ||
30 | /// Handle this event to receive status messages when operations are performed on the patch package. | ||
31 | /// </summary> | ||
32 | /// <example> | ||
33 | /// <c>patchPackage.Message += new InstallPackageMessageHandler(Console.WriteLine);</c> | ||
34 | /// </example> | ||
35 | public event InstallPackageMessageHandler Message; | ||
36 | |||
37 | /// <summary> | ||
38 | /// Sends a message to the <see cref="Message"/> event-handler. | ||
39 | /// </summary> | ||
40 | /// <param name="format">Message string, containing 0 or more format items</param> | ||
41 | /// <param name="args">Items to be formatted</param> | ||
42 | protected void LogMessage(string format, params object[] args) | ||
43 | { | ||
44 | if(this.Message != null) | ||
45 | { | ||
46 | this.Message(format, args); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | /// <summary> | ||
51 | /// Gets the patch code (GUID) of the patch package. | ||
52 | /// </summary> | ||
53 | /// <remarks> | ||
54 | /// The patch code is stored in the RevisionNumber field of the patch summary information. | ||
55 | /// </remarks> | ||
56 | public string PatchCode | ||
57 | { | ||
58 | get | ||
59 | { | ||
60 | string guids = this.SummaryInfo.RevisionNumber; | ||
61 | return guids.Substring(0, guids.IndexOf('}') + 1); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | /// <summary> | ||
66 | /// Gets the list of patch codes that are replaced by this patch package. | ||
67 | /// </summary> | ||
68 | /// <returns>Array of replaced patch codes (GUIDs)</returns> | ||
69 | /// <remarks> | ||
70 | /// The list of replaced patch codes is stored in the RevisionNumber field of the patch summary information. | ||
71 | /// </remarks> | ||
72 | public string[] GetReplacedPatchCodes() | ||
73 | { | ||
74 | ArrayList patchCodeList = new ArrayList(); | ||
75 | string guids = this.SummaryInfo.RevisionNumber; | ||
76 | int thisGuid = guids.IndexOf('}') + 1; | ||
77 | int nextGuid = guids.IndexOf('}', thisGuid) + 1; | ||
78 | while(nextGuid > 0) | ||
79 | { | ||
80 | patchCodeList.Add(guids.Substring(thisGuid, (nextGuid - thisGuid))); | ||
81 | thisGuid = nextGuid; | ||
82 | nextGuid = guids.IndexOf('}', thisGuid) + 1; | ||
83 | } | ||
84 | return (string[]) patchCodeList.ToArray(typeof(string)); | ||
85 | } | ||
86 | |||
87 | /// <summary> | ||
88 | /// Gets the list of product codes of products targeted by this patch package. | ||
89 | /// </summary> | ||
90 | /// <returns>Array of product codes (GUIDs)</returns> | ||
91 | /// <remarks> | ||
92 | /// The list of target product codes is stored in the Template field of the patch summary information. | ||
93 | /// </remarks> | ||
94 | public string[] GetTargetProductCodes() | ||
95 | { | ||
96 | string productList = this.SummaryInfo.Template; | ||
97 | return productList.Split(';'); | ||
98 | } | ||
99 | |||
100 | /// <summary> | ||
101 | /// Gets the names of the transforms included in the patch package. | ||
102 | /// </summary> | ||
103 | /// <returns>Array of transform names</returns> | ||
104 | /// <remarks> | ||
105 | /// The returned list does not include the "patch special transforms" that are prefixed with "#" | ||
106 | /// <p>The list of transform names is stored in the LastSavedBy field of the patch summary information.</p> | ||
107 | /// </remarks> | ||
108 | public string[] GetTransforms() | ||
109 | { | ||
110 | return this.GetTransforms(false); | ||
111 | } | ||
112 | /// <summary> | ||
113 | /// Gets the names of the transforms included in the patch package. | ||
114 | /// </summary> | ||
115 | /// <param name="includeSpecialTransforms">Specifies whether to include the | ||
116 | /// "patch special transforms" that are prefixed with "#"</param> | ||
117 | /// <returns>Array of transform names</returns> | ||
118 | /// <remarks> | ||
119 | /// The list of transform names is stored in the LastSavedBy field of the patch summary information. | ||
120 | /// </remarks> | ||
121 | public string[] GetTransforms(bool includeSpecialTransforms) | ||
122 | { | ||
123 | ArrayList transformArray = new ArrayList(); | ||
124 | string transformList = this.SummaryInfo.LastSavedBy; | ||
125 | foreach(string transform in transformList.Split(';', ':')) | ||
126 | { | ||
127 | if(transform.Length != 0 && (includeSpecialTransforms || !transform.StartsWith("#", StringComparison.Ordinal))) | ||
128 | { | ||
129 | transformArray.Add(transform); | ||
130 | } | ||
131 | } | ||
132 | return (string[]) transformArray.ToArray(typeof(string)); | ||
133 | } | ||
134 | |||
135 | /// <summary> | ||
136 | /// Gets information about the transforms included in the patch package. | ||
137 | /// </summary> | ||
138 | /// <returns>Array containing information about each transform</returns> | ||
139 | /// <remarks> | ||
140 | /// The returned info does not include the "patch special transforms" that are prefixed with "#" | ||
141 | /// </remarks> | ||
142 | public TransformInfo[] GetTransformsInfo() | ||
143 | { | ||
144 | return this.GetTransformsInfo(false); | ||
145 | } | ||
146 | |||
147 | /// <summary> | ||
148 | /// Gets information about the transforms included in the patch package. | ||
149 | /// </summary> | ||
150 | /// <param name="includeSpecialTransforms">Specifies whether to include the | ||
151 | /// "patch special transforms" that are prefixed with "#"</param> | ||
152 | /// <returns>Array containing information about each transform</returns> | ||
153 | public TransformInfo[] GetTransformsInfo(bool includeSpecialTransforms) | ||
154 | { | ||
155 | string[] transforms = this.GetTransforms(includeSpecialTransforms); | ||
156 | ArrayList transformInfoArray = new ArrayList(transforms.Length); | ||
157 | foreach(string transform in transforms) | ||
158 | { | ||
159 | transformInfoArray.Add(this.GetTransformInfo(transform)); | ||
160 | } | ||
161 | return (TransformInfo[]) transformInfoArray.ToArray(typeof(TransformInfo)); | ||
162 | } | ||
163 | |||
164 | /// <summary> | ||
165 | /// Gets information about a transforms included in the patch package. | ||
166 | /// </summary> | ||
167 | /// <param name="transform">Name of the transform to extract; this may optionally be a | ||
168 | /// special transform prefixed by "#"</param> | ||
169 | /// <returns>Information about the transform</returns> | ||
170 | public TransformInfo GetTransformInfo(string transform) | ||
171 | { | ||
172 | string tempTransformFile = null; | ||
173 | try | ||
174 | { | ||
175 | tempTransformFile = Path.GetTempFileName(); | ||
176 | this.ExtractTransform(transform, tempTransformFile); | ||
177 | using(SummaryInfo transformSummInfo = new SummaryInfo(tempTransformFile, false)) | ||
178 | { | ||
179 | return new TransformInfo(transform, transformSummInfo); | ||
180 | } | ||
181 | } | ||
182 | finally | ||
183 | { | ||
184 | if(tempTransformFile != null && File.Exists(tempTransformFile)) | ||
185 | { | ||
186 | File.Delete(tempTransformFile); | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
191 | /// <summary> | ||
192 | /// Analyzes the transforms included in the patch package to find the ones that | ||
193 | /// are applicable to an install package. | ||
194 | /// </summary> | ||
195 | /// <param name="installPackage">The install package to validate the transforms against</param> | ||
196 | /// <returns>Array of valid transform names</returns> | ||
197 | /// <remarks> | ||
198 | /// The returned list does not include the "patch special transforms" that | ||
199 | /// are prefixed with "#" If a transform is valid, then its corresponding | ||
200 | /// special transform is assumed to be valid as well. | ||
201 | /// </remarks> | ||
202 | public string[] GetValidTransforms(InstallPackage installPackage) | ||
203 | { | ||
204 | ArrayList transformArray = new ArrayList(); | ||
205 | string transformList = this.SummaryInfo.LastSavedBy; | ||
206 | foreach(string transform in transformList.Split(';', ':')) | ||
207 | { | ||
208 | if(transform.Length != 0 && !transform.StartsWith("#", StringComparison.Ordinal)) | ||
209 | { | ||
210 | this.LogMessage("Checking validity of transform {0}", transform); | ||
211 | string tempTransformFile = null; | ||
212 | try | ||
213 | { | ||
214 | tempTransformFile = Path.GetTempFileName(); | ||
215 | this.ExtractTransform(transform, tempTransformFile); | ||
216 | if(installPackage.IsTransformValid(tempTransformFile)) | ||
217 | { | ||
218 | this.LogMessage("Found valid transform: {0}", transform); | ||
219 | transformArray.Add(transform); | ||
220 | } | ||
221 | } | ||
222 | finally | ||
223 | { | ||
224 | if(tempTransformFile != null && File.Exists(tempTransformFile)) | ||
225 | { | ||
226 | try { File.Delete(tempTransformFile); } | ||
227 | catch(IOException) { } | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | return (string[]) transformArray.ToArray(typeof(string)); | ||
233 | } | ||
234 | |||
235 | /// <summary> | ||
236 | /// Extracts a transform (.MST) from a patch package. | ||
237 | /// </summary> | ||
238 | /// <param name="transform">Name of the transform to extract; this may optionally be a | ||
239 | /// special transform prefixed by "#"</param> | ||
240 | /// <param name="extractFile">Location where the transform will be extracted</param> | ||
241 | public void ExtractTransform(string transform, string extractFile) | ||
242 | { | ||
243 | using(View stgView = this.OpenView("SELECT `Name`, `Data` FROM `_Storages` WHERE `Name` = '{0}'", transform)) | ||
244 | { | ||
245 | stgView.Execute(); | ||
246 | Record stgRec = stgView.Fetch(); | ||
247 | if(stgRec == null) | ||
248 | { | ||
249 | this.LogMessage("Transform not found: {0}", transform); | ||
250 | throw new InstallerException("Transform not found: " + transform); | ||
251 | } | ||
252 | using(stgRec) | ||
253 | { | ||
254 | stgRec.GetStream("Data", extractFile); | ||
255 | } | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | } | ||