diff options
Diffstat (limited to '')
-rw-r--r-- | src/dtf/WixToolset.Dtf.Compression.Cab/CabPacker.cs | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.Compression.Cab/CabPacker.cs b/src/dtf/WixToolset.Dtf.Compression.Cab/CabPacker.cs new file mode 100644 index 00000000..ec6e3bda --- /dev/null +++ b/src/dtf/WixToolset.Dtf.Compression.Cab/CabPacker.cs | |||
@@ -0,0 +1,653 @@ | |||
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.Dtf.Compression.Cab | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using System.Text; | ||
8 | using System.Collections.Generic; | ||
9 | using System.Globalization; | ||
10 | using System.Runtime.InteropServices; | ||
11 | using System.Diagnostics.CodeAnalysis; | ||
12 | |||
13 | internal class CabPacker : CabWorker | ||
14 | { | ||
15 | private const string TempStreamName = "%%TEMP%%"; | ||
16 | |||
17 | private NativeMethods.FCI.Handle fciHandle; | ||
18 | |||
19 | // These delegates need to be saved as member variables | ||
20 | // so that they don't get GC'd. | ||
21 | private NativeMethods.FCI.PFNALLOC fciAllocMemHandler; | ||
22 | private NativeMethods.FCI.PFNFREE fciFreeMemHandler; | ||
23 | private NativeMethods.FCI.PFNOPEN fciOpenStreamHandler; | ||
24 | private NativeMethods.FCI.PFNREAD fciReadStreamHandler; | ||
25 | private NativeMethods.FCI.PFNWRITE fciWriteStreamHandler; | ||
26 | private NativeMethods.FCI.PFNCLOSE fciCloseStreamHandler; | ||
27 | private NativeMethods.FCI.PFNSEEK fciSeekStreamHandler; | ||
28 | private NativeMethods.FCI.PFNFILEPLACED fciFilePlacedHandler; | ||
29 | private NativeMethods.FCI.PFNDELETE fciDeleteFileHandler; | ||
30 | private NativeMethods.FCI.PFNGETTEMPFILE fciGetTempFileHandler; | ||
31 | |||
32 | private NativeMethods.FCI.PFNGETNEXTCABINET fciGetNextCabinet; | ||
33 | private NativeMethods.FCI.PFNSTATUS fciCreateStatus; | ||
34 | private NativeMethods.FCI.PFNGETOPENINFO fciGetOpenInfo; | ||
35 | |||
36 | private IPackStreamContext context; | ||
37 | |||
38 | private FileAttributes fileAttributes; | ||
39 | private DateTime fileLastWriteTime; | ||
40 | |||
41 | private int maxCabBytes; | ||
42 | |||
43 | private long totalFolderBytesProcessedInCurrentCab; | ||
44 | |||
45 | private CompressionLevel compressionLevel; | ||
46 | private bool dontUseTempFiles; | ||
47 | private IList<Stream> tempStreams; | ||
48 | |||
49 | public CabPacker(CabEngine cabEngine) | ||
50 | : base(cabEngine) | ||
51 | { | ||
52 | this.fciAllocMemHandler = this.CabAllocMem; | ||
53 | this.fciFreeMemHandler = this.CabFreeMem; | ||
54 | this.fciOpenStreamHandler = this.CabOpenStreamEx; | ||
55 | this.fciReadStreamHandler = this.CabReadStreamEx; | ||
56 | this.fciWriteStreamHandler = this.CabWriteStreamEx; | ||
57 | this.fciCloseStreamHandler = this.CabCloseStreamEx; | ||
58 | this.fciSeekStreamHandler = this.CabSeekStreamEx; | ||
59 | this.fciFilePlacedHandler = this.CabFilePlaced; | ||
60 | this.fciDeleteFileHandler = this.CabDeleteFile; | ||
61 | this.fciGetTempFileHandler = this.CabGetTempFile; | ||
62 | this.fciGetNextCabinet = this.CabGetNextCabinet; | ||
63 | this.fciCreateStatus = this.CabCreateStatus; | ||
64 | this.fciGetOpenInfo = this.CabGetOpenInfo; | ||
65 | this.tempStreams = new List<Stream>(); | ||
66 | this.compressionLevel = CompressionLevel.Normal; | ||
67 | } | ||
68 | |||
69 | public bool UseTempFiles | ||
70 | { | ||
71 | get | ||
72 | { | ||
73 | return !this.dontUseTempFiles; | ||
74 | } | ||
75 | |||
76 | set | ||
77 | { | ||
78 | this.dontUseTempFiles = !value; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | public CompressionLevel CompressionLevel | ||
83 | { | ||
84 | get | ||
85 | { | ||
86 | return this.compressionLevel; | ||
87 | } | ||
88 | |||
89 | set | ||
90 | { | ||
91 | this.compressionLevel = value; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] | ||
96 | private void CreateFci(long maxArchiveSize) | ||
97 | { | ||
98 | NativeMethods.FCI.CCAB ccab = new NativeMethods.FCI.CCAB(); | ||
99 | if (maxArchiveSize > 0 && maxArchiveSize < ccab.cb) | ||
100 | { | ||
101 | ccab.cb = Math.Max( | ||
102 | NativeMethods.FCI.MIN_DISK, (int) maxArchiveSize); | ||
103 | } | ||
104 | |||
105 | object maxFolderSizeOption = this.context.GetOption( | ||
106 | "maxFolderSize", null); | ||
107 | if (maxFolderSizeOption != null) | ||
108 | { | ||
109 | long maxFolderSize = Convert.ToInt64( | ||
110 | maxFolderSizeOption, CultureInfo.InvariantCulture); | ||
111 | if (maxFolderSize > 0 && maxFolderSize < ccab.cbFolderThresh) | ||
112 | { | ||
113 | ccab.cbFolderThresh = (int) maxFolderSize; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | this.maxCabBytes = ccab.cb; | ||
118 | ccab.szCab = this.context.GetArchiveName(0); | ||
119 | if (ccab.szCab == null) | ||
120 | { | ||
121 | throw new FileNotFoundException( | ||
122 | "Cabinet name not provided by stream context."); | ||
123 | } | ||
124 | ccab.setID = (short) new Random().Next( | ||
125 | Int16.MinValue, Int16.MaxValue + 1); | ||
126 | this.CabNumbers[ccab.szCab] = 0; | ||
127 | this.currentArchiveName = ccab.szCab; | ||
128 | this.totalArchives = 1; | ||
129 | this.CabStream = null; | ||
130 | |||
131 | this.Erf.Clear(); | ||
132 | this.fciHandle = NativeMethods.FCI.Create( | ||
133 | this.ErfHandle.AddrOfPinnedObject(), | ||
134 | this.fciFilePlacedHandler, | ||
135 | this.fciAllocMemHandler, | ||
136 | this.fciFreeMemHandler, | ||
137 | this.fciOpenStreamHandler, | ||
138 | this.fciReadStreamHandler, | ||
139 | this.fciWriteStreamHandler, | ||
140 | this.fciCloseStreamHandler, | ||
141 | this.fciSeekStreamHandler, | ||
142 | this.fciDeleteFileHandler, | ||
143 | this.fciGetTempFileHandler, | ||
144 | ccab, | ||
145 | IntPtr.Zero); | ||
146 | this.CheckError(false); | ||
147 | } | ||
148 | |||
149 | public void Pack( | ||
150 | IPackStreamContext streamContext, | ||
151 | IEnumerable<string> files, | ||
152 | long maxArchiveSize) | ||
153 | { | ||
154 | if (streamContext == null) | ||
155 | { | ||
156 | throw new ArgumentNullException("streamContext"); | ||
157 | } | ||
158 | |||
159 | if (files == null) | ||
160 | { | ||
161 | throw new ArgumentNullException("files"); | ||
162 | } | ||
163 | |||
164 | lock (this) | ||
165 | { | ||
166 | try | ||
167 | { | ||
168 | this.context = streamContext; | ||
169 | |||
170 | this.ResetProgressData(); | ||
171 | |||
172 | this.CreateFci(maxArchiveSize); | ||
173 | |||
174 | foreach (string file in files) | ||
175 | { | ||
176 | FileAttributes attributes; | ||
177 | DateTime lastWriteTime; | ||
178 | Stream fileStream = this.context.OpenFileReadStream( | ||
179 | file, | ||
180 | out attributes, | ||
181 | out lastWriteTime); | ||
182 | if (fileStream != null) | ||
183 | { | ||
184 | this.totalFileBytes += fileStream.Length; | ||
185 | this.totalFiles++; | ||
186 | this.context.CloseFileReadStream(file, fileStream); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | long uncompressedBytesInFolder = 0; | ||
191 | this.currentFileNumber = -1; | ||
192 | |||
193 | foreach (string file in files) | ||
194 | { | ||
195 | FileAttributes attributes; | ||
196 | DateTime lastWriteTime; | ||
197 | Stream fileStream = this.context.OpenFileReadStream( | ||
198 | file, out attributes, out lastWriteTime); | ||
199 | if (fileStream == null) | ||
200 | { | ||
201 | continue; | ||
202 | } | ||
203 | |||
204 | if (fileStream.Length >= (long) NativeMethods.FCI.MAX_FOLDER) | ||
205 | { | ||
206 | throw new NotSupportedException(String.Format( | ||
207 | CultureInfo.InvariantCulture, | ||
208 | "File {0} exceeds maximum file size " + | ||
209 | "for cabinet format.", | ||
210 | file)); | ||
211 | } | ||
212 | |||
213 | if (uncompressedBytesInFolder > 0) | ||
214 | { | ||
215 | // Automatically create a new folder if this file | ||
216 | // won't fit in the current folder. | ||
217 | bool nextFolder = uncompressedBytesInFolder | ||
218 | + fileStream.Length >= (long) NativeMethods.FCI.MAX_FOLDER; | ||
219 | |||
220 | // Otherwise ask the client if it wants to | ||
221 | // move to the next folder. | ||
222 | if (!nextFolder) | ||
223 | { | ||
224 | object nextFolderOption = streamContext.GetOption( | ||
225 | "nextFolder", | ||
226 | new object[] { file, this.currentFolderNumber }); | ||
227 | nextFolder = Convert.ToBoolean( | ||
228 | nextFolderOption, CultureInfo.InvariantCulture); | ||
229 | } | ||
230 | |||
231 | if (nextFolder) | ||
232 | { | ||
233 | this.FlushFolder(); | ||
234 | uncompressedBytesInFolder = 0; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | if (this.currentFolderTotalBytes > 0) | ||
239 | { | ||
240 | this.currentFolderTotalBytes = 0; | ||
241 | this.currentFolderNumber++; | ||
242 | uncompressedBytesInFolder = 0; | ||
243 | } | ||
244 | |||
245 | this.currentFileName = file; | ||
246 | this.currentFileNumber++; | ||
247 | |||
248 | this.currentFileTotalBytes = fileStream.Length; | ||
249 | this.currentFileBytesProcessed = 0; | ||
250 | this.OnProgress(ArchiveProgressType.StartFile); | ||
251 | |||
252 | uncompressedBytesInFolder += fileStream.Length; | ||
253 | |||
254 | this.AddFile( | ||
255 | file, | ||
256 | fileStream, | ||
257 | attributes, | ||
258 | lastWriteTime, | ||
259 | false, | ||
260 | this.CompressionLevel); | ||
261 | } | ||
262 | |||
263 | this.FlushFolder(); | ||
264 | this.FlushCabinet(); | ||
265 | } | ||
266 | finally | ||
267 | { | ||
268 | if (this.CabStream != null) | ||
269 | { | ||
270 | this.context.CloseArchiveWriteStream( | ||
271 | this.currentArchiveNumber, | ||
272 | this.currentArchiveName, | ||
273 | this.CabStream); | ||
274 | this.CabStream = null; | ||
275 | } | ||
276 | |||
277 | if (this.FileStream != null) | ||
278 | { | ||
279 | this.context.CloseFileReadStream( | ||
280 | this.currentFileName, this.FileStream); | ||
281 | this.FileStream = null; | ||
282 | } | ||
283 | this.context = null; | ||
284 | |||
285 | if (this.fciHandle != null) | ||
286 | { | ||
287 | this.fciHandle.Dispose(); | ||
288 | this.fciHandle = null; | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | |||
294 | internal override int CabOpenStreamEx(string path, int openFlags, int shareMode, out int err, IntPtr pv) | ||
295 | { | ||
296 | if (this.CabNumbers.ContainsKey(path)) | ||
297 | { | ||
298 | Stream stream = this.CabStream; | ||
299 | if (stream == null) | ||
300 | { | ||
301 | short cabNumber = this.CabNumbers[path]; | ||
302 | |||
303 | this.currentFolderTotalBytes = 0; | ||
304 | |||
305 | stream = this.context.OpenArchiveWriteStream(cabNumber, path, true, this.CabEngine); | ||
306 | if (stream == null) | ||
307 | { | ||
308 | throw new FileNotFoundException( | ||
309 | String.Format(CultureInfo.InvariantCulture, "Cabinet {0} not provided.", cabNumber)); | ||
310 | } | ||
311 | this.currentArchiveName = path; | ||
312 | |||
313 | this.currentArchiveTotalBytes = Math.Min( | ||
314 | this.totalFolderBytesProcessedInCurrentCab, this.maxCabBytes); | ||
315 | this.currentArchiveBytesProcessed = 0; | ||
316 | |||
317 | this.OnProgress(ArchiveProgressType.StartArchive); | ||
318 | this.CabStream = stream; | ||
319 | } | ||
320 | path = CabWorker.CabStreamName; | ||
321 | } | ||
322 | else if (path == CabPacker.TempStreamName) | ||
323 | { | ||
324 | // Opening memory stream for a temp file. | ||
325 | Stream stream = new MemoryStream(); | ||
326 | this.tempStreams.Add(stream); | ||
327 | int streamHandle = this.StreamHandles.AllocHandle(stream); | ||
328 | err = 0; | ||
329 | return streamHandle; | ||
330 | } | ||
331 | else if (path != CabWorker.CabStreamName) | ||
332 | { | ||
333 | // Opening a file on disk for a temp file. | ||
334 | path = Path.Combine(Path.GetTempPath(), path); | ||
335 | Stream stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite); | ||
336 | this.tempStreams.Add(stream); | ||
337 | stream = new DuplicateStream(stream); | ||
338 | int streamHandle = this.StreamHandles.AllocHandle(stream); | ||
339 | err = 0; | ||
340 | return streamHandle; | ||
341 | } | ||
342 | return base.CabOpenStreamEx(path, openFlags, shareMode, out err, pv); | ||
343 | } | ||
344 | |||
345 | internal override int CabWriteStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv) | ||
346 | { | ||
347 | int count = base.CabWriteStreamEx(streamHandle, memory, cb, out err, pv); | ||
348 | if (count > 0 && err == 0) | ||
349 | { | ||
350 | Stream stream = this.StreamHandles[streamHandle]; | ||
351 | if (DuplicateStream.OriginalStream(stream) == | ||
352 | DuplicateStream.OriginalStream(this.CabStream)) | ||
353 | { | ||
354 | this.currentArchiveBytesProcessed += cb; | ||
355 | if (this.currentArchiveBytesProcessed > this.currentArchiveTotalBytes) | ||
356 | { | ||
357 | this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes; | ||
358 | } | ||
359 | } | ||
360 | } | ||
361 | return count; | ||
362 | } | ||
363 | |||
364 | internal override int CabCloseStreamEx(int streamHandle, out int err, IntPtr pv) | ||
365 | { | ||
366 | Stream stream = DuplicateStream.OriginalStream(this.StreamHandles[streamHandle]); | ||
367 | |||
368 | if (stream == DuplicateStream.OriginalStream(this.FileStream)) | ||
369 | { | ||
370 | this.context.CloseFileReadStream(this.currentFileName, stream); | ||
371 | this.FileStream = null; | ||
372 | long remainder = this.currentFileTotalBytes - this.currentFileBytesProcessed; | ||
373 | this.currentFileBytesProcessed += remainder; | ||
374 | this.fileBytesProcessed += remainder; | ||
375 | this.OnProgress(ArchiveProgressType.FinishFile); | ||
376 | |||
377 | this.currentFileTotalBytes = 0; | ||
378 | this.currentFileBytesProcessed = 0; | ||
379 | this.currentFileName = null; | ||
380 | } | ||
381 | else if (stream == DuplicateStream.OriginalStream(this.CabStream)) | ||
382 | { | ||
383 | if (stream.CanWrite) | ||
384 | { | ||
385 | stream.Flush(); | ||
386 | } | ||
387 | |||
388 | this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes; | ||
389 | this.OnProgress(ArchiveProgressType.FinishArchive); | ||
390 | this.currentArchiveNumber++; | ||
391 | this.totalArchives++; | ||
392 | |||
393 | this.context.CloseArchiveWriteStream( | ||
394 | this.currentArchiveNumber, | ||
395 | this.currentArchiveName, | ||
396 | stream); | ||
397 | |||
398 | this.currentArchiveName = this.NextCabinetName; | ||
399 | this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes = 0; | ||
400 | this.totalFolderBytesProcessedInCurrentCab = 0; | ||
401 | |||
402 | this.CabStream = null; | ||
403 | } | ||
404 | else // Must be a temp stream | ||
405 | { | ||
406 | stream.Close(); | ||
407 | this.tempStreams.Remove(stream); | ||
408 | } | ||
409 | return base.CabCloseStreamEx(streamHandle, out err, pv); | ||
410 | } | ||
411 | |||
412 | /// <summary> | ||
413 | /// Disposes of resources allocated by the cabinet engine. | ||
414 | /// </summary> | ||
415 | /// <param name="disposing">If true, the method has been called directly or indirectly by a user's code, | ||
416 | /// so managed and unmanaged resources will be disposed. If false, the method has been called by the | ||
417 | /// runtime from inside the finalizer, and only unmanaged resources will be disposed.</param> | ||
418 | [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] | ||
419 | protected override void Dispose(bool disposing) | ||
420 | { | ||
421 | try | ||
422 | { | ||
423 | if (disposing) | ||
424 | { | ||
425 | if (this.fciHandle != null) | ||
426 | { | ||
427 | this.fciHandle.Dispose(); | ||
428 | this.fciHandle = null; | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | finally | ||
433 | { | ||
434 | base.Dispose(disposing); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | private static NativeMethods.FCI.TCOMP GetCompressionType(CompressionLevel compLevel) | ||
439 | { | ||
440 | if (compLevel < CompressionLevel.Min) | ||
441 | { | ||
442 | return NativeMethods.FCI.TCOMP.TYPE_NONE; | ||
443 | } | ||
444 | else | ||
445 | { | ||
446 | if (compLevel > CompressionLevel.Max) | ||
447 | { | ||
448 | compLevel = CompressionLevel.Max; | ||
449 | } | ||
450 | |||
451 | int lzxWindowMax = | ||
452 | ((int) NativeMethods.FCI.TCOMP.LZX_WINDOW_HI >> (int) NativeMethods.FCI.TCOMP.SHIFT_LZX_WINDOW) - | ||
453 | ((int) NativeMethods.FCI.TCOMP.LZX_WINDOW_LO >> (int) NativeMethods.FCI.TCOMP.SHIFT_LZX_WINDOW); | ||
454 | int lzxWindow = lzxWindowMax * | ||
455 | (compLevel - CompressionLevel.Min) / (CompressionLevel.Max - CompressionLevel.Min); | ||
456 | |||
457 | return (NativeMethods.FCI.TCOMP) ((int) NativeMethods.FCI.TCOMP.TYPE_LZX | | ||
458 | ((int) NativeMethods.FCI.TCOMP.LZX_WINDOW_LO + | ||
459 | (lzxWindow << (int) NativeMethods.FCI.TCOMP.SHIFT_LZX_WINDOW))); | ||
460 | } | ||
461 | } | ||
462 | |||
463 | [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] | ||
464 | private void AddFile( | ||
465 | string name, | ||
466 | Stream stream, | ||
467 | FileAttributes attributes, | ||
468 | DateTime lastWriteTime, | ||
469 | bool execute, | ||
470 | CompressionLevel compLevel) | ||
471 | { | ||
472 | this.FileStream = stream; | ||
473 | this.fileAttributes = attributes & | ||
474 | (FileAttributes.Archive | FileAttributes.Hidden | FileAttributes.ReadOnly | FileAttributes.System); | ||
475 | this.fileLastWriteTime = lastWriteTime; | ||
476 | this.currentFileName = name; | ||
477 | |||
478 | NativeMethods.FCI.TCOMP tcomp = CabPacker.GetCompressionType(compLevel); | ||
479 | |||
480 | IntPtr namePtr = IntPtr.Zero; | ||
481 | try | ||
482 | { | ||
483 | Encoding nameEncoding = Encoding.ASCII; | ||
484 | if (Encoding.UTF8.GetByteCount(name) > name.Length) | ||
485 | { | ||
486 | nameEncoding = Encoding.UTF8; | ||
487 | this.fileAttributes |= FileAttributes.Normal; // _A_NAME_IS_UTF | ||
488 | } | ||
489 | |||
490 | byte[] nameBytes = nameEncoding.GetBytes(name); | ||
491 | namePtr = Marshal.AllocHGlobal(nameBytes.Length + 1); | ||
492 | Marshal.Copy(nameBytes, 0, namePtr, nameBytes.Length); | ||
493 | Marshal.WriteByte(namePtr, nameBytes.Length, 0); | ||
494 | |||
495 | this.Erf.Clear(); | ||
496 | NativeMethods.FCI.AddFile( | ||
497 | this.fciHandle, | ||
498 | String.Empty, | ||
499 | namePtr, | ||
500 | execute, | ||
501 | this.fciGetNextCabinet, | ||
502 | this.fciCreateStatus, | ||
503 | this.fciGetOpenInfo, | ||
504 | tcomp); | ||
505 | } | ||
506 | finally | ||
507 | { | ||
508 | if (namePtr != IntPtr.Zero) | ||
509 | { | ||
510 | Marshal.FreeHGlobal(namePtr); | ||
511 | } | ||
512 | } | ||
513 | |||
514 | this.CheckError(false); | ||
515 | this.FileStream = null; | ||
516 | this.currentFileName = null; | ||
517 | } | ||
518 | |||
519 | private void FlushFolder() | ||
520 | { | ||
521 | this.Erf.Clear(); | ||
522 | NativeMethods.FCI.FlushFolder(this.fciHandle, this.fciGetNextCabinet, this.fciCreateStatus); | ||
523 | this.CheckError(false); | ||
524 | } | ||
525 | |||
526 | private void FlushCabinet() | ||
527 | { | ||
528 | this.Erf.Clear(); | ||
529 | NativeMethods.FCI.FlushCabinet(this.fciHandle, false, this.fciGetNextCabinet, this.fciCreateStatus); | ||
530 | this.CheckError(false); | ||
531 | } | ||
532 | |||
533 | private int CabGetOpenInfo( | ||
534 | string path, | ||
535 | out short date, | ||
536 | out short time, | ||
537 | out short attribs, | ||
538 | out int err, | ||
539 | IntPtr pv) | ||
540 | { | ||
541 | CompressionEngine.DateTimeToDosDateAndTime(this.fileLastWriteTime, out date, out time); | ||
542 | attribs = (short) this.fileAttributes; | ||
543 | |||
544 | Stream stream = this.FileStream; | ||
545 | this.FileStream = new DuplicateStream(stream); | ||
546 | int streamHandle = this.StreamHandles.AllocHandle(stream); | ||
547 | err = 0; | ||
548 | return streamHandle; | ||
549 | } | ||
550 | |||
551 | private int CabFilePlaced( | ||
552 | IntPtr pccab, | ||
553 | string filePath, | ||
554 | long fileSize, | ||
555 | int continuation, | ||
556 | IntPtr pv) | ||
557 | { | ||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | private int CabGetNextCabinet(IntPtr pccab, uint prevCabSize, IntPtr pv) | ||
562 | { | ||
563 | NativeMethods.FCI.CCAB nextCcab = new NativeMethods.FCI.CCAB(); | ||
564 | Marshal.PtrToStructure(pccab, nextCcab); | ||
565 | |||
566 | nextCcab.szDisk = String.Empty; | ||
567 | nextCcab.szCab = this.context.GetArchiveName(nextCcab.iCab); | ||
568 | this.CabNumbers[nextCcab.szCab] = (short) nextCcab.iCab; | ||
569 | this.NextCabinetName = nextCcab.szCab; | ||
570 | |||
571 | Marshal.StructureToPtr(nextCcab, pccab, false); | ||
572 | return 1; | ||
573 | } | ||
574 | |||
575 | private int CabCreateStatus(NativeMethods.FCI.STATUS typeStatus, uint cb1, uint cb2, IntPtr pv) | ||
576 | { | ||
577 | switch (typeStatus) | ||
578 | { | ||
579 | case NativeMethods.FCI.STATUS.FILE: | ||
580 | if (cb2 > 0 && this.currentFileBytesProcessed < this.currentFileTotalBytes) | ||
581 | { | ||
582 | if (this.currentFileBytesProcessed + cb2 > this.currentFileTotalBytes) | ||
583 | { | ||
584 | cb2 = (uint) this.currentFileTotalBytes - (uint) this.currentFileBytesProcessed; | ||
585 | } | ||
586 | this.currentFileBytesProcessed += cb2; | ||
587 | this.fileBytesProcessed += cb2; | ||
588 | |||
589 | this.OnProgress(ArchiveProgressType.PartialFile); | ||
590 | } | ||
591 | break; | ||
592 | |||
593 | case NativeMethods.FCI.STATUS.FOLDER: | ||
594 | if (cb1 == 0) | ||
595 | { | ||
596 | this.currentFolderTotalBytes = cb2 - this.totalFolderBytesProcessedInCurrentCab; | ||
597 | this.totalFolderBytesProcessedInCurrentCab = cb2; | ||
598 | } | ||
599 | else if (this.currentFolderTotalBytes == 0) | ||
600 | { | ||
601 | this.OnProgress(ArchiveProgressType.PartialArchive); | ||
602 | } | ||
603 | break; | ||
604 | |||
605 | case NativeMethods.FCI.STATUS.CABINET: | ||
606 | break; | ||
607 | } | ||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | private int CabGetTempFile(IntPtr tempNamePtr, int tempNameSize, IntPtr pv) | ||
612 | { | ||
613 | string tempFileName; | ||
614 | if (this.UseTempFiles) | ||
615 | { | ||
616 | tempFileName = Path.GetFileName(Path.GetTempFileName()); | ||
617 | } | ||
618 | else | ||
619 | { | ||
620 | tempFileName = CabPacker.TempStreamName; | ||
621 | } | ||
622 | |||
623 | byte[] tempNameBytes = Encoding.ASCII.GetBytes(tempFileName); | ||
624 | if (tempNameBytes.Length >= tempNameSize) | ||
625 | { | ||
626 | return -1; | ||
627 | } | ||
628 | |||
629 | Marshal.Copy(tempNameBytes, 0, tempNamePtr, tempNameBytes.Length); | ||
630 | Marshal.WriteByte(tempNamePtr, tempNameBytes.Length, 0); // null-terminator | ||
631 | return 1; | ||
632 | } | ||
633 | |||
634 | private int CabDeleteFile(string path, out int err, IntPtr pv) | ||
635 | { | ||
636 | try | ||
637 | { | ||
638 | // Deleting a temp file - don't bother if it is only a memory stream. | ||
639 | if (path != CabPacker.TempStreamName) | ||
640 | { | ||
641 | path = Path.Combine(Path.GetTempPath(), path); | ||
642 | File.Delete(path); | ||
643 | } | ||
644 | } | ||
645 | catch (IOException) | ||
646 | { | ||
647 | // Failure to delete a temp file is not fatal. | ||
648 | } | ||
649 | err = 0; | ||
650 | return 1; | ||
651 | } | ||
652 | } | ||
653 | } | ||