aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs177
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
3namespace 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