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