diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs')
-rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs new file mode 100644 index 00000000..b2cc76fc --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs | |||
@@ -0,0 +1,177 @@ | |||
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.Core.WindowsInstaller.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using System.Threading; | ||
10 | using WixToolset.Core.Bind; | ||
11 | using WixToolset.Core.Cab; | ||
12 | using WixToolset.Data; | ||
13 | |||
14 | /// <summary> | ||
15 | /// Builds cabinets using multiple threads. This implements a thread pool that generates cabinets with multiple | ||
16 | /// threads. Unlike System.Threading.ThreadPool, it waits until all threads are finished. | ||
17 | /// </summary> | ||
18 | internal sealed class CabinetBuilder | ||
19 | { | ||
20 | private Queue cabinetWorkItems; | ||
21 | private object lockObject; | ||
22 | private int threadCount; | ||
23 | |||
24 | // Address of Binder's callback function for Cabinet Splitting | ||
25 | private IntPtr newCabNamesCallBackAddress; | ||
26 | |||
27 | public int MaximumCabinetSizeForLargeFileSplitting { get; set; } | ||
28 | |||
29 | public int MaximumUncompressedMediaSize { get; set; } | ||
30 | |||
31 | /// <summary> | ||
32 | /// Instantiate a new CabinetBuilder. | ||
33 | /// </summary> | ||
34 | /// <param name="threadCount">number of threads to use</param> | ||
35 | /// <param name="newCabNamesCallBackAddress">Address of Binder's callback function for Cabinet Splitting</param> | ||
36 | public CabinetBuilder(int threadCount, IntPtr newCabNamesCallBackAddress) | ||
37 | { | ||
38 | if (0 >= threadCount) | ||
39 | { | ||
40 | throw new ArgumentOutOfRangeException("threadCount"); | ||
41 | } | ||
42 | |||
43 | this.cabinetWorkItems = new Queue(); | ||
44 | this.lockObject = new object(); | ||
45 | |||
46 | this.threadCount = threadCount; | ||
47 | |||
48 | // Set Address of Binder's callback function for Cabinet Splitting | ||
49 | this.newCabNamesCallBackAddress = newCabNamesCallBackAddress; | ||
50 | } | ||
51 | |||
52 | /// <summary> | ||
53 | /// Enqueues a CabinetWorkItem to the queue. | ||
54 | /// </summary> | ||
55 | /// <param name="cabinetWorkItem">cabinet work item</param> | ||
56 | public void Enqueue(CabinetWorkItem cabinetWorkItem) | ||
57 | { | ||
58 | this.cabinetWorkItems.Enqueue(cabinetWorkItem); | ||
59 | } | ||
60 | |||
61 | /// <summary> | ||
62 | /// Create the queued cabinets. | ||
63 | /// </summary> | ||
64 | /// <returns>error message number (zero if no error)</returns> | ||
65 | public void CreateQueuedCabinets() | ||
66 | { | ||
67 | // don't create more threads than the number of cabinets to build | ||
68 | if (this.cabinetWorkItems.Count < this.threadCount) | ||
69 | { | ||
70 | this.threadCount = this.cabinetWorkItems.Count; | ||
71 | } | ||
72 | |||
73 | if (0 < this.threadCount) | ||
74 | { | ||
75 | Thread[] threads = new Thread[this.threadCount]; | ||
76 | |||
77 | for (int i = 0; i < threads.Length; i++) | ||
78 | { | ||
79 | threads[i] = new Thread(new ThreadStart(this.ProcessWorkItems)); | ||
80 | threads[i].Start(); | ||
81 | } | ||
82 | |||
83 | // wait for all threads to finish | ||
84 | foreach (Thread thread in threads) | ||
85 | { | ||
86 | thread.Join(); | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | /// <summary> | ||
92 | /// This function gets called by multiple threads to do actual work. | ||
93 | /// It takes one work item at a time and calls this.CreateCabinet(). | ||
94 | /// It does not return until cabinetWorkItems queue is empty | ||
95 | /// </summary> | ||
96 | private void ProcessWorkItems() | ||
97 | { | ||
98 | try | ||
99 | { | ||
100 | while (true) | ||
101 | { | ||
102 | CabinetWorkItem cabinetWorkItem; | ||
103 | |||
104 | lock (this.cabinetWorkItems) | ||
105 | { | ||
106 | // check if there are any more cabinets to create | ||
107 | if (0 == this.cabinetWorkItems.Count) | ||
108 | { | ||
109 | break; | ||
110 | } | ||
111 | |||
112 | cabinetWorkItem = (CabinetWorkItem)this.cabinetWorkItems.Dequeue(); | ||
113 | } | ||
114 | |||
115 | // create a cabinet | ||
116 | this.CreateCabinet(cabinetWorkItem); | ||
117 | } | ||
118 | } | ||
119 | catch (WixException we) | ||
120 | { | ||
121 | Messaging.Instance.OnMessage(we.Error); | ||
122 | } | ||
123 | catch (Exception e) | ||
124 | { | ||
125 | Messaging.Instance.OnMessage(WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | /// <summary> | ||
130 | /// Creates a cabinet using the wixcab.dll interop layer. | ||
131 | /// </summary> | ||
132 | /// <param name="cabinetWorkItem">CabinetWorkItem containing information about the cabinet to create.</param> | ||
133 | private void CreateCabinet(CabinetWorkItem cabinetWorkItem) | ||
134 | { | ||
135 | Messaging.Instance.OnMessage(WixVerboses.CreateCabinet(cabinetWorkItem.CabinetFile)); | ||
136 | |||
137 | int maxCabinetSize = 0; // The value of 0 corresponds to default of 2GB which means no cabinet splitting | ||
138 | ulong maxPreCompressedSizeInBytes = 0; | ||
139 | |||
140 | if (MaximumCabinetSizeForLargeFileSplitting != 0) | ||
141 | { | ||
142 | // User Specified Max Cab Size for File Splitting, So Check if this cabinet has a single file larger than MaximumUncompressedFileSize | ||
143 | // If a file is larger than MaximumUncompressedFileSize, then the cabinet containing it will have only this file | ||
144 | if (1 == cabinetWorkItem.FileFacades.Count()) | ||
145 | { | ||
146 | // Cabinet has Single File, Check if this is Large File than needs Splitting into Multiple cabs | ||
147 | // Get the Value for Max Uncompressed Media Size | ||
148 | maxPreCompressedSizeInBytes = (ulong)MaximumUncompressedMediaSize * 1024 * 1024; | ||
149 | |||
150 | foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row | ||
151 | { | ||
152 | if ((ulong)facade.File.FileSize >= maxPreCompressedSizeInBytes) | ||
153 | { | ||
154 | // If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting | ||
155 | maxCabinetSize = MaximumCabinetSizeForLargeFileSplitting; | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | // create the cabinet file | ||
162 | string cabinetFileName = Path.GetFileName(cabinetWorkItem.CabinetFile); | ||
163 | string cabinetDirectory = Path.GetDirectoryName(cabinetWorkItem.CabinetFile); | ||
164 | |||
165 | using (WixCreateCab cab = new WixCreateCab(cabinetFileName, cabinetDirectory, cabinetWorkItem.FileFacades.Count(), maxCabinetSize, cabinetWorkItem.MaxThreshold, cabinetWorkItem.CompressionLevel)) | ||
166 | { | ||
167 | foreach (FileFacade facade in cabinetWorkItem.FileFacades) | ||
168 | { | ||
169 | cab.AddFile(facade); | ||
170 | } | ||
171 | |||
172 | cab.Complete(newCabNamesCallBackAddress); | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||