aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-10-14 16:12:07 -0700
committerRob Mensching <rob@firegiant.com>2017-10-14 16:12:07 -0700
commitdbde9e7104b907bbbaea17e21247d8cafc8b3a4c (patch)
tree0f5fbbb6fe12c6b2e5e622a0e18ce4c5b4eb2b96 /src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
parentfbf986eb97f68396797a89fc7d40dec07b775440 (diff)
downloadwix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.tar.gz
wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.tar.bz2
wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.zip
Massive refactoring to introduce the concept of IBackend
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
new file mode 100644
index 00000000..ae76037d
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
@@ -0,0 +1,226 @@
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.Core.WindowsInstaller.Databases
4{
5 using System;
6 using System.Collections.Generic;
7 using System.ComponentModel;
8 using System.Globalization;
9 using System.IO;
10 using System.Linq;
11 using System.Runtime.InteropServices;
12 using WixToolset.Data;
13 using WixToolset.Data.Rows;
14 using WixToolset.MergeMod;
15 using WixToolset.Msi;
16 using WixToolset.Core.Native;
17 using WixToolset.Core.Bind;
18 using WixToolset.Core.Cab;
19
20 /// <summary>
21 /// Retrieve files information and extract them from merge modules.
22 /// </summary>
23 internal class ExtractMergeModuleFilesCommand
24 {
25 public IEnumerable<FileFacade> FileFacades { private get; set; }
26
27 public Table FileTable { private get; set; }
28
29 public Table WixFileTable { private get; set; }
30
31 public Table WixMergeTable { private get; set; }
32
33 public int OutputInstallerVersion { private get; set; }
34
35 public bool SuppressLayout { private get; set; }
36
37 public string TempFilesLocation { private get; set; }
38
39 public IEnumerable<FileFacade> MergeModulesFileFacades { get; private set; }
40
41 public void Execute()
42 {
43 List<FileFacade> mergeModulesFileFacades = new List<FileFacade>();
44
45 IMsmMerge2 merge = MsmInterop.GetMsmMerge();
46
47 // Index all of the file rows to be able to detect collisions with files in the Merge Modules.
48 // It may seem a bit expensive to build up this index solely for the purpose of checking collisions
49 // and you may be thinking, "Surely, we must need the file rows indexed elsewhere." It turns out
50 // there are other cases where we need all the file rows indexed, however they are not common cases.
51 // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let
52 // this case be slightly more expensive because the cost of maintaining an indexed file row collection
53 // is a lot more costly for the common cases.
54 Dictionary<string, FileFacade> indexedFileFacades = this.FileFacades.ToDictionary(f => f.File.File, StringComparer.Ordinal);
55
56 foreach (WixMergeRow wixMergeRow in this.WixMergeTable.Rows)
57 {
58 bool containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades);
59
60 // If the module has files and creating layout
61 if (containsFiles && !this.SuppressLayout)
62 {
63 this.ExtractFilesFromMergeModule(merge, wixMergeRow);
64 }
65 }
66
67 this.MergeModulesFileFacades = mergeModulesFileFacades;
68 }
69
70 private bool CreateFacadesForMergeModuleFiles(WixMergeRow wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades)
71 {
72 bool containsFiles = false;
73
74 try
75 {
76 // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module.
77 using (Database db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly))
78 {
79 if (db.TableExists("File") && db.TableExists("Component"))
80 {
81 Dictionary<string, FileFacade> uniqueModuleFileIdentifiers = new Dictionary<string, FileFacade>(StringComparer.OrdinalIgnoreCase);
82
83 using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`"))
84 {
85 // add each file row from the merge module into the file row collection (check for errors along the way)
86 while (true)
87 {
88 using (Record record = view.Fetch())
89 {
90 if (null == record)
91 {
92 break;
93 }
94
95 // NOTE: this is very tricky - the merge module file rows are not added to the
96 // file table because they should not be created via idt import. Instead, these
97 // rows are created by merging in the actual modules.
98 FileRow fileRow = (FileRow)this.FileTable.CreateRow(wixMergeRow.SourceLineNumbers, false);
99 fileRow.File = record[1];
100 fileRow.Compressed = wixMergeRow.FileCompression;
101
102 WixFileRow wixFileRow = (WixFileRow)this.WixFileTable.CreateRow(wixMergeRow.SourceLineNumbers, false);
103 wixFileRow.Directory = record[2];
104 wixFileRow.DiskId = wixMergeRow.DiskId;
105 wixFileRow.PatchGroup = -1;
106 wixFileRow.Source = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", wixMergeRow.Number.ToString(CultureInfo.InvariantCulture), Path.DirectorySeparatorChar, record[1]);
107
108 FileFacade mergeModuleFileFacade = new FileFacade(true, fileRow, wixFileRow);
109
110 FileFacade collidingFacade;
111
112 // If case-sensitive collision with another merge module or a user-authored file identifier.
113 if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade))
114 {
115 Messaging.Instance.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, collidingFacade.File.File));
116 }
117 else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module
118 {
119 Messaging.Instance.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, mergeModuleFileFacade.File.File, collidingFacade.File.File));
120 }
121 else // no collision
122 {
123 mergeModulesFileFacades.Add(mergeModuleFileFacade);
124
125 // Keep updating the indexes as new rows are added.
126 indexedFileFacades.Add(mergeModuleFileFacade.File.File, mergeModuleFileFacade);
127 uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.File, mergeModuleFileFacade);
128 }
129
130 containsFiles = true;
131 }
132 }
133 }
134 }
135
136 // Get the summary information to detect the Schema
137 using (SummaryInformation summaryInformation = new SummaryInformation(db))
138 {
139 string moduleInstallerVersionString = summaryInformation.GetProperty(14);
140
141 try
142 {
143 int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture);
144 if (moduleInstallerVersion > this.OutputInstallerVersion)
145 {
146 Messaging.Instance.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleInstallerVersion, this.OutputInstallerVersion));
147 }
148 }
149 catch (FormatException)
150 {
151 throw new WixException(WixErrors.MissingOrInvalidModuleInstallerVersion(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile, moduleInstallerVersionString));
152 }
153 }
154 }
155 }
156 catch (FileNotFoundException)
157 {
158 throw new WixException(WixErrors.FileNotFound(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile));
159 }
160 catch (Win32Exception)
161 {
162 throw new WixException(WixErrors.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile));
163 }
164
165 return containsFiles;
166 }
167
168 private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeRow wixMergeRow)
169 {
170 bool moduleOpen = false;
171 short mergeLanguage;
172
173 try
174 {
175 mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture);
176 }
177 catch (System.FormatException)
178 {
179 Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language));
180 return;
181 }
182
183 try
184 {
185 merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage);
186 moduleOpen = true;
187
188 string safeMergeId = wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat);
189
190 // extract the module cabinet, then explode all of the files to a temp directory
191 string moduleCabPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, safeMergeId, ".module.cab");
192 merge.ExtractCAB(moduleCabPath);
193
194 string mergeIdPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", safeMergeId);
195 Directory.CreateDirectory(mergeIdPath);
196
197 using (var extractCab = new WixExtractCab())
198 {
199 try
200 {
201 extractCab.Extract(moduleCabPath, mergeIdPath);
202 }
203 catch (FileNotFoundException)
204 {
205 throw new WixException(WixErrors.CabFileDoesNotExist(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath));
206 }
207 catch
208 {
209 throw new WixException(WixErrors.CabExtractionFailed(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath));
210 }
211 }
212 }
213 catch (COMException ce)
214 {
215 throw new WixException(WixErrors.UnableToOpenModule(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile, ce.Message));
216 }
217 finally
218 {
219 if (moduleOpen)
220 {
221 merge.CloseModule();
222 }
223 }
224 }
225 }
226}