aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.WindowsInstaller.Package/PatchPackage.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/dtf/WixToolset.Dtf.WindowsInstaller.Package/PatchPackage.cs')
-rw-r--r--src/dtf/WixToolset.Dtf.WindowsInstaller.Package/PatchPackage.cs259
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
3namespace 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 &quot;patch special transforms&quot; that are prefixed with &quot;#&quot;
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 /// &quot;patch special transforms&quot; that are prefixed with &quot;#&quot;</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 &quot;patch special transforms&quot; that are prefixed with &quot;#&quot;
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 /// &quot;patch special transforms&quot; that are prefixed with &quot;#&quot;</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 &quot;#&quot;</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 &quot;patch special transforms&quot; that
199 /// are prefixed with &quot;#&quot; 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 &quot;#&quot;</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}