SevenZipCompressor.cs 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025
  1. namespace SevenZip
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Runtime.InteropServices;
  9. #if NET472 || NETSTANDARD2_0
  10. using System.Security.Permissions;
  11. #endif
  12. using SevenZip.Sdk;
  13. using SevenZip.Sdk.Compression.Lzma;
  14. /// <summary>
  15. /// Class to pack data into archives supported by 7-Zip.
  16. /// </summary>
  17. /// <example>
  18. /// var compr = new SevenZipCompressor();
  19. /// compr.CompressDirectory(@"C:\Dir", @"C:\Archive.7z");
  20. /// </example>
  21. public sealed partial class SevenZipCompressor
  22. #if UNMANAGED
  23. : SevenZipBase
  24. #endif
  25. {
  26. #if UNMANAGED
  27. #region Fields
  28. private bool _compressingFilesOnDisk;
  29. /// <summary>
  30. /// Gets or sets the archiving compression level.
  31. /// </summary>
  32. public CompressionLevel CompressionLevel { get; set; }
  33. private OutArchiveFormat _archiveFormat = OutArchiveFormat.SevenZip;
  34. private CompressionMethod _compressionMethod = CompressionMethod.Default;
  35. /// <summary>
  36. /// Gets the custom compression parameters - for advanced users only.
  37. /// </summary>
  38. public Dictionary<string, string> CustomParameters { get; private set; }
  39. private long _volumeSize;
  40. private string _archiveName;
  41. /// <summary>
  42. /// Gets or sets the value indicating whether to include empty directories to archives. Default is true.
  43. /// </summary>
  44. public bool IncludeEmptyDirectories { get; set; }
  45. /// <summary>
  46. /// Gets or sets the value indicating whether to preserve the directory root for CompressDirectory.
  47. /// </summary>
  48. public bool PreserveDirectoryRoot { get; set; }
  49. /// <summary>
  50. /// Gets or sets the value indicating whether to preserve the directory structure.
  51. /// </summary>
  52. public bool DirectoryStructure { get; set; }
  53. private bool _directoryCompress;
  54. /// <summary>
  55. /// Gets or sets the compression mode.
  56. /// </summary>
  57. public CompressionMode CompressionMode { get; set; }
  58. private UpdateData _updateData;
  59. private uint _oldFilesCount;
  60. /// <summary>
  61. /// Gets or sets the value indicating whether to encrypt 7-Zip archive headers.
  62. /// </summary>
  63. public bool EncryptHeaders { get; set; }
  64. /// <summary>
  65. /// Gets or sets the value indicating whether to compress files only open for writing.
  66. /// </summary>
  67. public bool ScanOnlyWritable { get; set; }
  68. /// <summary>
  69. /// Gets or sets the encryption method for zip archives.
  70. /// </summary>
  71. public ZipEncryptionMethod ZipEncryptionMethod { get; set; }
  72. /// <summary>
  73. /// Gets or sets the temporary folder path.
  74. /// </summary>
  75. public string TempFolderPath { get; set; }
  76. /// <summary>
  77. /// Gets or sets the default archive item name used when an item to be compressed has no name,
  78. /// for example, when you compress a MemoryStream instance.
  79. /// </summary>
  80. public string DefaultItemName { get; set; }
  81. /// <summary>
  82. /// Gets or sets the value indicating whether to compress as fast as possible, without calling events.
  83. /// </summary>
  84. public bool FastCompression { get; set; }
  85. #endregion
  86. #endif
  87. private static volatile int _lzmaDictionarySize = 1 << 22;
  88. #if UNMANAGED
  89. private void CommonInit()
  90. {
  91. DirectoryStructure = true;
  92. IncludeEmptyDirectories = true;
  93. CompressionLevel = CompressionLevel.Normal;
  94. CompressionMode = CompressionMode.Create;
  95. ZipEncryptionMethod = ZipEncryptionMethod.ZipCrypto;
  96. CustomParameters = new Dictionary<string, string>();
  97. _updateData = new UpdateData();
  98. DefaultItemName = "default";
  99. }
  100. /// <summary>
  101. /// Initializes a new instance of the SevenZipCompressor class.
  102. /// </summary>
  103. public SevenZipCompressor()
  104. {
  105. try
  106. {
  107. TempFolderPath = Path.GetTempPath();
  108. }
  109. catch (System.Security.SecurityException) // Registry access is not allowed, etc.
  110. {
  111. throw new SevenZipCompressionFailedException("Path.GetTempPath() threw a System.Security.SecurityException. You must call SevenZipCompressor constructor overload with your own temporary path.");
  112. }
  113. CommonInit();
  114. }
  115. /// <summary>
  116. /// Initializes a new instance of the SevenZipCompressor class.
  117. /// </summary>
  118. /// <param name="temporaryPath">Your own temporary path (default is set in the parameterless constructor overload.)</param>
  119. public SevenZipCompressor(string temporaryPath)
  120. {
  121. TempFolderPath = temporaryPath;
  122. if (!Directory.Exists(TempFolderPath))
  123. {
  124. try
  125. {
  126. Directory.CreateDirectory(TempFolderPath);
  127. }
  128. catch (Exception)
  129. {
  130. throw new SevenZipCompressionFailedException("The specified temporary path is invalid.");
  131. }
  132. }
  133. CommonInit();
  134. }
  135. #endif
  136. /// <summary>
  137. /// Checks if the specified stream supports compression.
  138. /// </summary>
  139. /// <param name="stream">The stream to check.</param>
  140. private static void ValidateStream(Stream stream)
  141. {
  142. if (!stream.CanWrite || !stream.CanSeek)
  143. {
  144. throw new ArgumentException("The specified stream can not seek or is not writable.", nameof(stream));
  145. }
  146. }
  147. #if UNMANAGED
  148. #region Private functions
  149. private IOutArchive MakeOutArchive(IInStream inArchiveStream)
  150. {
  151. var inArchive = SevenZipLibraryManager.InArchive(Formats.InForOutFormats[_archiveFormat], this);
  152. using (var openCallback = GetArchiveOpenCallback())
  153. {
  154. ulong checkPos = 1 << 15;
  155. if (inArchive.Open(inArchiveStream, ref checkPos, openCallback) != (int) OperationResult.Ok)
  156. {
  157. if (!ThrowException(null, new SevenZipArchiveException("Can not update the archive: Open() failed.")))
  158. {
  159. return null;
  160. }
  161. }
  162. _oldFilesCount = inArchive.GetNumberOfItems();
  163. }
  164. return (IOutArchive) inArchive;
  165. }
  166. /// <summary>
  167. /// Guaranties the correct work of the SetCompressionProperties function
  168. /// </summary>
  169. /// <param name="method">The compression method to check</param>
  170. /// <returns>The value indicating whether the specified method is valid for the current ArchiveFormat</returns>
  171. private bool MethodIsValid(CompressionMethod method)
  172. {
  173. if (method == CompressionMethod.Default)
  174. {
  175. return true;
  176. }
  177. // TODO: Decide what to do with a returned "false" from this method!
  178. switch (_archiveFormat)
  179. {
  180. case OutArchiveFormat.GZip:
  181. {
  182. return method == CompressionMethod.Deflate;
  183. }
  184. case OutArchiveFormat.BZip2:
  185. {
  186. return method == CompressionMethod.BZip2;
  187. }
  188. case OutArchiveFormat.SevenZip:
  189. {
  190. return method != CompressionMethod.Deflate && method != CompressionMethod.Deflate64;
  191. }
  192. case OutArchiveFormat.Tar:
  193. {
  194. return method == CompressionMethod.Copy;
  195. }
  196. case OutArchiveFormat.Zip:
  197. {
  198. return method != CompressionMethod.Lzma2;
  199. }
  200. default:
  201. {
  202. return true;
  203. }
  204. }
  205. }
  206. private bool SwitchIsInCustomParameters(string name)
  207. {
  208. return CustomParameters.ContainsKey(name);
  209. }
  210. /// <summary>
  211. /// Sets the compression properties
  212. /// </summary>
  213. private void SetCompressionProperties()
  214. {
  215. switch (_archiveFormat)
  216. {
  217. case OutArchiveFormat.Tar:
  218. {
  219. break;
  220. }
  221. default:
  222. {
  223. var setter =
  224. CompressionMode == CompressionMode.Create && _updateData.FileNamesToModify == null ?
  225. (ISetProperties) SevenZipLibraryManager.OutArchive(_archiveFormat, this) :
  226. (ISetProperties) SevenZipLibraryManager.InArchive(Formats.InForOutFormats[_archiveFormat], this);
  227. if (setter == null)
  228. {
  229. if (!ThrowException(null, new CompressionFailedException("The specified archive format is unsupported.")))
  230. {
  231. return;
  232. }
  233. }
  234. if (_volumeSize > 0 && ArchiveFormat != OutArchiveFormat.SevenZip)
  235. {
  236. throw new CompressionFailedException("Unfortunately, the creation of multi-volume non-7Zip archives is not implemented.");
  237. }
  238. #region Check for "forbidden" parameters
  239. if (CustomParameters.ContainsKey("x"))
  240. {
  241. if (!ThrowException(null, new CompressionFailedException("Use the \"CompressionLevel\" property instead of the \"x\" parameter.")))
  242. {
  243. return;
  244. }
  245. }
  246. if (CustomParameters.ContainsKey("em"))
  247. {
  248. if (!ThrowException(null, new CompressionFailedException("Use the \"ZipEncryptionMethod\" property instead of the \"em\" parameter.")))
  249. {
  250. return;
  251. }
  252. }
  253. if (CustomParameters.ContainsKey("m"))
  254. {
  255. if (!ThrowException(null, new CompressionFailedException("Use the \"CompressionMethod\" property instead of the \"m\" parameter.")))
  256. {
  257. return;
  258. }
  259. }
  260. #endregion
  261. var names = new List<IntPtr>(2 + CustomParameters.Count);
  262. var values = new List<PropVariant>(2 + CustomParameters.Count);
  263. #if NET472 || NETSTANDARD2_0
  264. var sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
  265. sp.Demand();
  266. #endif
  267. #region Initialize compression properties
  268. names.Add(Marshal.StringToBSTR("x"));
  269. values.Add(new PropVariant());
  270. if (_compressionMethod != CompressionMethod.Default)
  271. {
  272. names.Add(_archiveFormat == OutArchiveFormat.Zip ?
  273. Marshal.StringToBSTR("m") :
  274. Marshal.StringToBSTR("0"));
  275. var pv = new PropVariant
  276. {
  277. VarType = VarEnum.VT_BSTR,
  278. Value = Marshal.StringToBSTR(Formats.MethodNames[_compressionMethod])
  279. };
  280. values.Add(pv);
  281. }
  282. foreach (var pair in CustomParameters)
  283. {
  284. #region Validate parameters against compression method.
  285. if (_compressionMethod != CompressionMethod.Ppmd && (pair.Key.Equals("mem") || pair.Key.Equals("o")))
  286. {
  287. ThrowException(null, new CompressionFailedException($"Parameter \"{pair.Key}\" is only valid with the PPMd compression method."));
  288. }
  289. #endregion
  290. names.Add(Marshal.StringToBSTR(pair.Key));
  291. var pv = new PropVariant();
  292. if (pair.Value.All(char.IsDigit))
  293. {
  294. pv.VarType = VarEnum.VT_UI4;
  295. pv.UInt32Value = Convert.ToUInt32(pair.Value, CultureInfo.InvariantCulture);
  296. }
  297. else
  298. {
  299. pv.VarType = VarEnum.VT_BSTR;
  300. pv.Value = Marshal.StringToBSTR(pair.Value);
  301. }
  302. values.Add(pv);
  303. }
  304. #endregion
  305. #region Set compression level
  306. var clpv = values[0];
  307. clpv.VarType = VarEnum.VT_UI4;
  308. switch (CompressionLevel)
  309. {
  310. case CompressionLevel.None:
  311. {
  312. clpv.UInt32Value = 0;
  313. break;
  314. }
  315. case CompressionLevel.Fast:
  316. {
  317. clpv.UInt32Value = 1;
  318. break;
  319. }
  320. case CompressionLevel.Low:
  321. {
  322. clpv.UInt32Value = 3;
  323. break;
  324. }
  325. case CompressionLevel.Normal:
  326. {
  327. clpv.UInt32Value = 5;
  328. break;
  329. }
  330. case CompressionLevel.High:
  331. {
  332. clpv.UInt32Value = 7;
  333. break;
  334. }
  335. case CompressionLevel.Ultra:
  336. {
  337. clpv.UInt32Value = 9;
  338. break;
  339. }
  340. }
  341. values[0] = clpv;
  342. #endregion
  343. #region Encrypt headers
  344. if (EncryptHeaders && _archiveFormat == OutArchiveFormat.SevenZip && !SwitchIsInCustomParameters("he"))
  345. {
  346. names.Add(Marshal.StringToBSTR("he"));
  347. var tmp = new PropVariant {VarType = VarEnum.VT_BSTR, Value = Marshal.StringToBSTR("on")};
  348. values.Add(tmp);
  349. }
  350. #endregion
  351. #region Zip Encryption
  352. if (_archiveFormat == OutArchiveFormat.Zip &&
  353. ZipEncryptionMethod != ZipEncryptionMethod.ZipCrypto &&
  354. !SwitchIsInCustomParameters("em"))
  355. {
  356. names.Add(Marshal.StringToBSTR("em"));
  357. var tmp = new PropVariant
  358. {
  359. VarType = VarEnum.VT_BSTR,
  360. Value = Marshal.StringToBSTR(Enum.GetName(typeof(ZipEncryptionMethod), ZipEncryptionMethod))
  361. };
  362. values.Add(tmp);
  363. }
  364. #endregion
  365. var namesHandle = GCHandle.Alloc(names.ToArray(), GCHandleType.Pinned);
  366. var valuesHandle = GCHandle.Alloc(values.ToArray(), GCHandleType.Pinned);
  367. try
  368. {
  369. setter?.SetProperties(namesHandle.AddrOfPinnedObject(), valuesHandle.AddrOfPinnedObject(), names.Count);
  370. }
  371. finally
  372. {
  373. namesHandle.Free();
  374. valuesHandle.Free();
  375. }
  376. break;
  377. }
  378. }
  379. }
  380. /// <summary>
  381. /// Finds the common root of file names
  382. /// </summary>
  383. /// <param name="files">Array of file names</param>
  384. /// <returns>Common root</returns>
  385. private static int CommonRoot(ICollection<string> files)
  386. {
  387. var splitFileNames = new List<string[]>(files.Count);
  388. splitFileNames.AddRange(files.Select(fn => fn.Split(Path.DirectorySeparatorChar)));
  389. var minSplitLength = splitFileNames[0].Length - 1;
  390. if (files.Count > 1)
  391. {
  392. for (var i = 1; i < files.Count; i++)
  393. {
  394. if (minSplitLength > splitFileNames[i].Length)
  395. {
  396. minSplitLength = splitFileNames[i].Length;
  397. }
  398. }
  399. }
  400. var res = "";
  401. for (var i = 0; i < minSplitLength; i++)
  402. {
  403. var common = true;
  404. for (var j = 1; j < files.Count; j++)
  405. {
  406. if (!(common &= splitFileNames[j - 1][i] == splitFileNames[j][i]))
  407. {
  408. break;
  409. }
  410. }
  411. if (common)
  412. {
  413. res += splitFileNames[0][i] + Path.DirectorySeparatorChar;
  414. }
  415. else
  416. {
  417. break;
  418. }
  419. }
  420. return res.Length;
  421. }
  422. /// <summary>
  423. /// Validates the common root
  424. /// </summary>
  425. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  426. /// <param name="files">Array of file names</param>
  427. private static void CheckCommonRoot(IReadOnlyList<string> files, ref int commonRootLength)
  428. {
  429. string commonRoot;
  430. try
  431. {
  432. commonRoot = files[0].Substring(0, commonRootLength);
  433. }
  434. catch (ArgumentOutOfRangeException)
  435. {
  436. throw new SevenZipInvalidFileNamesException("invalid common root.");
  437. }
  438. if (commonRoot.EndsWith(new string(Path.DirectorySeparatorChar, 1), StringComparison.CurrentCulture))
  439. {
  440. commonRoot = commonRoot.Substring(0, commonRootLength - 1);
  441. commonRootLength--;
  442. }
  443. if (files.Any(fn => !fn.StartsWith(commonRoot, StringComparison.CurrentCulture)))
  444. {
  445. throw new SevenZipInvalidFileNamesException("invalid common root.");
  446. }
  447. }
  448. /// <summary>
  449. /// Ensures that directory directory is not empty
  450. /// </summary>
  451. /// <param name="directory">Directory name</param>
  452. /// <returns>False if is not empty</returns>
  453. private static bool RecursiveDirectoryEmptyCheck(string directory)
  454. {
  455. var di = new DirectoryInfo(directory);
  456. if (di.GetFiles().Length > 0)
  457. {
  458. return false;
  459. }
  460. var empty = true;
  461. foreach (var cdi in di.GetDirectories())
  462. {
  463. empty &= RecursiveDirectoryEmptyCheck(cdi.FullName);
  464. if (!empty)
  465. {
  466. return false;
  467. }
  468. }
  469. return true;
  470. }
  471. /// <summary>
  472. /// Makes special FileInfo array for the archive file table.
  473. /// </summary>
  474. /// <param name="files">Array of files to pack.</param>
  475. /// <param name="commonRootLength">The length of the common root of file names</param>
  476. /// <param name="directoryCompress">The value indicating whether to produce the array for files in a particular directory or just for an array of files.</param>
  477. /// <param name="directoryStructure">Preserve directory structure.</param>
  478. /// <returns>Special FileInfo array for the archive file table.</returns>
  479. private static FileInfo[] ProduceFileInfoArray(
  480. IReadOnlyList<string> files, int commonRootLength,
  481. bool directoryCompress, bool directoryStructure)
  482. {
  483. var fis = new List<FileInfo>(files.Count);
  484. var commonRoot = files[0].Substring(0, commonRootLength);
  485. if (directoryCompress)
  486. {
  487. fis.AddRange(files.Select(fn => new FileInfo(fn)));
  488. }
  489. else
  490. {
  491. if (!directoryStructure)
  492. {
  493. fis.AddRange(from fn in files where !Directory.Exists(fn) select new FileInfo(fn));
  494. }
  495. else
  496. {
  497. var fns = new List<string>(files.Count);
  498. CheckCommonRoot(files, ref commonRootLength);
  499. if (commonRootLength > 0)
  500. {
  501. commonRootLength++;
  502. foreach (var f in files)
  503. {
  504. var splitAfn = f.Substring(commonRootLength).Split(Path.DirectorySeparatorChar);
  505. var cfn = commonRoot;
  506. foreach (var t in splitAfn)
  507. {
  508. cfn += Path.DirectorySeparatorChar + t;
  509. if (!fns.Contains(cfn))
  510. {
  511. fis.Add(new FileInfo(cfn));
  512. fns.Add(cfn);
  513. }
  514. }
  515. }
  516. }
  517. else
  518. {
  519. foreach (var f in files)
  520. {
  521. var splitAfn = f.Substring(commonRootLength).Split(Path.DirectorySeparatorChar);
  522. var cfn = splitAfn[0];
  523. for (var i = 1; i < splitAfn.Length; i++)
  524. {
  525. cfn += Path.DirectorySeparatorChar + splitAfn[i];
  526. if (!fns.Contains(cfn))
  527. {
  528. fis.Add(new FileInfo(cfn));
  529. fns.Add(cfn);
  530. }
  531. }
  532. }
  533. }
  534. }
  535. }
  536. return fis.ToArray();
  537. }
  538. /// <summary>
  539. /// Recursive function for adding files in directory
  540. /// </summary>
  541. /// <param name="directory">Directory directory</param>
  542. /// <param name="files">List of files</param>
  543. /// <param name="searchPattern">Search string, such as "*.txt"</param>
  544. private void AddFilesFromDirectory(string directory, ICollection<string> files, string searchPattern)
  545. {
  546. var di = new DirectoryInfo(directory);
  547. foreach (var fi in di.GetFiles(searchPattern))
  548. {
  549. if (!ScanOnlyWritable)
  550. {
  551. files.Add(fi.FullName);
  552. }
  553. else
  554. {
  555. try
  556. {
  557. using (fi.OpenWrite())
  558. {
  559. }
  560. files.Add(fi.FullName);
  561. }
  562. catch (IOException)
  563. {
  564. }
  565. }
  566. }
  567. foreach (var cdi in di.GetDirectories())
  568. {
  569. if (IncludeEmptyDirectories)
  570. {
  571. files.Add(cdi.FullName);
  572. }
  573. AddFilesFromDirectory(cdi.FullName, files, searchPattern);
  574. }
  575. }
  576. #endregion
  577. #region GetArchiveUpdateCallback overloads
  578. /// <summary>
  579. /// Performs the common ArchiveUpdateCallback initialization.
  580. /// </summary>
  581. /// <param name="auc">The ArchiveUpdateCallback instance to initialize.</param>
  582. private void CommonUpdateCallbackInit(ArchiveUpdateCallback auc)
  583. {
  584. auc.FileCompressionStarted += FileCompressionStartedEventProxy;
  585. auc.Compressing += CompressingEventProxy;
  586. auc.FileCompressionFinished += FileCompressionFinishedEventProxy;
  587. auc.DefaultItemName = DefaultItemName;
  588. auc.FastCompression = FastCompression;
  589. }
  590. /// <summary>
  591. /// Performs the common ArchiveUpdateCallback initialization.
  592. /// </summary>
  593. /// <param name="auc">The ArchiveUpdateCallback instance to initialize.</param>
  594. private void CommonUpdateCallbackInitWithFileTime(ArchiveUpdateCallbackWithFileTime auc)
  595. {
  596. auc.FileCompressionStarted += FileCompressionStartedEventProxy;
  597. auc.Compressing += CompressingEventProxy;
  598. auc.FileCompressionFinished += FileCompressionFinishedEventProxy;
  599. auc.DefaultItemName = DefaultItemName;
  600. auc.FastCompression = FastCompression;
  601. }
  602. private float GetDictionarySize()
  603. {
  604. var dictionarySize = 0.001f;
  605. switch (_compressionMethod)
  606. {
  607. case CompressionMethod.Default:
  608. case CompressionMethod.Lzma:
  609. case CompressionMethod.Lzma2:
  610. {
  611. switch (CompressionLevel)
  612. {
  613. case CompressionLevel.None:
  614. {
  615. dictionarySize = 0.001f;
  616. break;
  617. }
  618. case CompressionLevel.Fast:
  619. {
  620. dictionarySize = 1.0f / 16 * 7.5f + 4;
  621. break;
  622. }
  623. case CompressionLevel.Low:
  624. {
  625. dictionarySize = 7.5f * 11.5f + 4;
  626. break;
  627. }
  628. case CompressionLevel.Normal:
  629. {
  630. dictionarySize = 16 * 11.5f + 4;
  631. break;
  632. }
  633. case CompressionLevel.High:
  634. {
  635. dictionarySize = 32 * 11.5f + 4;
  636. break;
  637. }
  638. case CompressionLevel.Ultra:
  639. {
  640. dictionarySize = 64 * 11.5f + 4;
  641. break;
  642. }
  643. }
  644. break;
  645. }
  646. case CompressionMethod.BZip2:
  647. {
  648. switch (CompressionLevel)
  649. {
  650. case CompressionLevel.None:
  651. {
  652. dictionarySize = 0;
  653. break;
  654. }
  655. case CompressionLevel.Fast:
  656. {
  657. dictionarySize = 0.095f;
  658. break;
  659. }
  660. case CompressionLevel.Low:
  661. {
  662. dictionarySize = 0.477f;
  663. break;
  664. }
  665. case CompressionLevel.Normal:
  666. case CompressionLevel.High:
  667. case CompressionLevel.Ultra:
  668. {
  669. dictionarySize = 0.858f;
  670. break;
  671. }
  672. }
  673. break;
  674. }
  675. case CompressionMethod.Deflate:
  676. case CompressionMethod.Deflate64:
  677. {
  678. dictionarySize = 32;
  679. break;
  680. }
  681. case CompressionMethod.Ppmd:
  682. {
  683. dictionarySize = 16;
  684. break;
  685. }
  686. }
  687. return dictionarySize;
  688. }
  689. /// <summary>
  690. /// Produces a new instance of ArchiveUpdateCallback class.
  691. /// </summary>
  692. /// <param name="files">Array of FileInfo - files to pack</param>
  693. /// <param name="rootLength">Length of the common root of file names</param>
  694. /// <param name="password">The archive password</param>
  695. /// <returns></returns>
  696. private ArchiveUpdateCallback GetArchiveUpdateCallback(
  697. FileInfo[] files, int rootLength, string password)
  698. {
  699. SetCompressionProperties();
  700. var auc = (string.IsNullOrEmpty(password))
  701. ? new ArchiveUpdateCallback(files, rootLength, this, GetUpdateData(), DirectoryStructure)
  702. {DictionarySize = GetDictionarySize()}
  703. : new ArchiveUpdateCallback(files, rootLength, password, this, GetUpdateData(), DirectoryStructure)
  704. {DictionarySize = GetDictionarySize()};
  705. CommonUpdateCallbackInit(auc);
  706. return auc;
  707. }
  708. /// <summary>
  709. /// Produces a new instance of ArchiveUpdateCallback class.
  710. /// </summary>
  711. /// <param name="inStream">The archive input stream.</param>
  712. /// <param name="password">The archive password.</param>
  713. /// <returns></returns>
  714. private ArchiveUpdateCallback GetArchiveUpdateCallback(Stream inStream, string password)
  715. {
  716. SetCompressionProperties();
  717. var auc = (string.IsNullOrEmpty(password))
  718. ? new ArchiveUpdateCallback(inStream, this, GetUpdateData(), DirectoryStructure)
  719. {DictionarySize = GetDictionarySize()}
  720. : new ArchiveUpdateCallback(inStream, password, this, GetUpdateData(), DirectoryStructure)
  721. {DictionarySize = GetDictionarySize()};
  722. CommonUpdateCallbackInit(auc);
  723. return auc;
  724. }
  725. /// <summary>
  726. /// Produces a new instance of ArchiveUpdateCallback class.
  727. /// </summary>
  728. /// <param name="streamDict">Dictionary&lt;name of the archive entry, stream&gt;.</param>
  729. /// <param name="password">The archive password</param>
  730. /// <returns></returns>
  731. private ArchiveUpdateCallback GetArchiveUpdateCallback(
  732. IDictionary<string, Stream> streamDict, string password)
  733. {
  734. SetCompressionProperties();
  735. var auc = (string.IsNullOrEmpty(password))
  736. ? new ArchiveUpdateCallback(streamDict, this, GetUpdateData(), DirectoryStructure)
  737. {DictionarySize = GetDictionarySize()}
  738. : new ArchiveUpdateCallback(streamDict, password, this, GetUpdateData(), DirectoryStructure)
  739. {DictionarySize = GetDictionarySize()};
  740. CommonUpdateCallbackInit(auc);
  741. return auc;
  742. }
  743. /// <summary>
  744. /// Produces a new instance of ArchiveUpdateCallback class.
  745. /// </summary>
  746. /// <param name="streamDict">Dictionary&lt;name of the archive entry, stream&gt;.</param>
  747. /// <param name="password">The archive password</param>
  748. /// <returns></returns>
  749. private ArchiveUpdateCallbackWithFileTime GetArchiveUpdateCallbackWithFileTime(
  750. IDictionary<string, CompressStreamEntry> streamDict, string password)
  751. {
  752. SetCompressionProperties();
  753. var auc = (string.IsNullOrEmpty(password))
  754. ? new ArchiveUpdateCallbackWithFileTime(streamDict, this, GetUpdateData(), DirectoryStructure)
  755. { DictionarySize = GetDictionarySize() }
  756. : new ArchiveUpdateCallbackWithFileTime(streamDict, password, this, GetUpdateData(), DirectoryStructure)
  757. { DictionarySize = GetDictionarySize() };
  758. CommonUpdateCallbackInitWithFileTime(auc);
  759. return auc;
  760. }
  761. #endregion
  762. #region Service "Get" functions
  763. private void FreeCompressionCallback(ArchiveUpdateCallback callback)
  764. {
  765. callback.FileCompressionStarted -= FileCompressionStartedEventProxy;
  766. callback.Compressing -= CompressingEventProxy;
  767. callback.FileCompressionFinished -= FileCompressionFinishedEventProxy;
  768. }
  769. private void FreeCompressionCallbackWithFileTime(ArchiveUpdateCallbackWithFileTime callback)
  770. {
  771. callback.FileCompressionStarted -= FileCompressionStartedEventProxy;
  772. callback.Compressing -= CompressingEventProxy;
  773. callback.FileCompressionFinished -= FileCompressionFinishedEventProxy;
  774. }
  775. private string GetTempArchiveFileName(string archiveName)
  776. {
  777. return Path.Combine(TempFolderPath, Path.GetFileName(archiveName) + ".~");
  778. }
  779. private FileStream GetArchiveFileStream(string archiveName)
  780. {
  781. if ((CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null) && !File.Exists(archiveName))
  782. {
  783. if (
  784. !ThrowException(null,
  785. new CompressionFailedException("file \"" + archiveName + "\" does not exist.")))
  786. {
  787. return null;
  788. }
  789. }
  790. return _volumeSize == 0
  791. ? CompressionMode == CompressionMode.Create && _updateData.FileNamesToModify == null
  792. ? File.Create(archiveName)
  793. : File.Create(GetTempArchiveFileName(archiveName))
  794. : null;
  795. }
  796. private void FinalizeUpdate()
  797. {
  798. if (_volumeSize == 0 && (CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null))
  799. {
  800. File.Move(GetTempArchiveFileName(_archiveName), _archiveName);
  801. }
  802. }
  803. private UpdateData GetUpdateData()
  804. {
  805. if (_updateData.FileNamesToModify == null)
  806. {
  807. var updateData = new UpdateData {Mode = (InternalCompressionMode) ((int) CompressionMode)};
  808. switch (CompressionMode)
  809. {
  810. case CompressionMode.Create:
  811. {
  812. updateData.FilesCount = uint.MaxValue;
  813. break;
  814. }
  815. case CompressionMode.Append:
  816. {
  817. updateData.FilesCount = _oldFilesCount;
  818. break;
  819. }
  820. }
  821. return updateData;
  822. }
  823. return _updateData;
  824. }
  825. private ISequentialOutStream GetOutStream(Stream outStream)
  826. {
  827. if (!_compressingFilesOnDisk)
  828. {
  829. return new OutStreamWrapper(outStream, false);
  830. }
  831. if (_volumeSize == 0 || CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null)
  832. {
  833. return new OutStreamWrapper(outStream, true);
  834. }
  835. return new OutMultiStreamWrapper(_archiveName, _volumeSize);
  836. }
  837. private IInStream GetInStream()
  838. {
  839. return File.Exists(_archiveName) &&
  840. (CompressionMode != CompressionMode.Create && _compressingFilesOnDisk ||
  841. _updateData.FileNamesToModify != null)
  842. ? new InStreamWrapper(
  843. new FileStream(_archiveName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
  844. true)
  845. : null;
  846. }
  847. private ArchiveOpenCallback GetArchiveOpenCallback()
  848. {
  849. return string.IsNullOrEmpty(Password)
  850. ? new ArchiveOpenCallback(_archiveName)
  851. : new ArchiveOpenCallback(_archiveName, Password);
  852. }
  853. #endregion
  854. #region Core public Members
  855. #region Events
  856. /// <summary>
  857. /// Occurs when the next file is going to be packed.
  858. /// </summary>
  859. /// <remarks>Occurs when 7-zip engine requests for an input stream for the next file to pack it</remarks>
  860. public event EventHandler<FileNameEventArgs> FileCompressionStarted;
  861. /// <summary>
  862. /// Occurs when the current file was compressed.
  863. /// </summary>
  864. public event EventHandler<EventArgs> FileCompressionFinished;
  865. /// <summary>
  866. /// Occurs when data are being compressed
  867. /// </summary>
  868. /// <remarks>Use this event for accurate progress handling and various ProgressBar.StepBy(e.PercentDelta) routines</remarks>
  869. public event EventHandler<ProgressEventArgs> Compressing;
  870. /// <summary>
  871. /// Occurs when all files information was determined and SevenZipCompressor is about to start to compress them.
  872. /// </summary>
  873. /// <remarks>The incoming int value indicates the number of scanned files.</remarks>
  874. public event EventHandler<IntEventArgs> FilesFound;
  875. /// <summary>
  876. /// Occurs when the compression procedure is finished
  877. /// </summary>
  878. public event EventHandler<EventArgs> CompressionFinished;
  879. #region Event proxies
  880. /// <summary>
  881. /// Event proxy for FileCompressionStarted.
  882. /// </summary>
  883. /// <param name="sender">The sender of the event.</param>
  884. /// <param name="e">The event arguments.</param>
  885. private void FileCompressionStartedEventProxy(object sender, FileNameEventArgs e)
  886. {
  887. OnEvent(FileCompressionStarted, e, false);
  888. }
  889. /// <summary>
  890. /// Event proxy for FileCompressionFinished.
  891. /// </summary>
  892. /// <param name="sender">The sender of the event.</param>
  893. /// <param name="e">The event arguments.</param>
  894. private void FileCompressionFinishedEventProxy(object sender, EventArgs e)
  895. {
  896. OnEvent(FileCompressionFinished, e, false);
  897. }
  898. /// <summary>
  899. /// Event proxy for Compressing.
  900. /// </summary>
  901. /// <param name="sender">The sender of the event.</param>
  902. /// <param name="e">The event arguments.</param>
  903. private void CompressingEventProxy(object sender, ProgressEventArgs e)
  904. {
  905. OnEvent(Compressing, e, false);
  906. }
  907. /// <summary>
  908. /// Event proxy for FilesFound.
  909. /// </summary>
  910. /// <param name="sender">The sender of the event.</param>
  911. /// <param name="e">The event arguments.</param>
  912. private void FilesFoundEventProxy(object sender, IntEventArgs e)
  913. {
  914. OnEvent(FilesFound, e, false);
  915. }
  916. #endregion
  917. #endregion
  918. #region Properties
  919. /// <summary>
  920. /// Gets or sets the archive format
  921. /// </summary>
  922. public OutArchiveFormat ArchiveFormat
  923. {
  924. get => _archiveFormat;
  925. set
  926. {
  927. _archiveFormat = value;
  928. if (!MethodIsValid(_compressionMethod))
  929. {
  930. _compressionMethod = CompressionMethod.Default;
  931. }
  932. }
  933. }
  934. /// <summary>
  935. /// Gets or sets the compression method
  936. /// </summary>
  937. public CompressionMethod CompressionMethod
  938. {
  939. get => _compressionMethod;
  940. set => _compressionMethod = !MethodIsValid(value) ? CompressionMethod.Default : value;
  941. }
  942. /// <summary>
  943. /// Gets or sets the size in bytes of an archive volume (0 for no volumes).
  944. /// </summary>
  945. public long VolumeSize
  946. {
  947. get => _volumeSize;
  948. set => _volumeSize = value > 0 ? value : 0;
  949. }
  950. #endregion
  951. #region CompressFiles overloads
  952. /// <summary>
  953. /// Packs files into the archive.
  954. /// </summary>
  955. /// <param name="fileFullNames">Array of file names to pack.</param>
  956. /// <param name="archiveName">The archive file name.</param>
  957. public void CompressFiles(
  958. string archiveName, params string[] fileFullNames)
  959. {
  960. CompressFilesEncrypted(archiveName, string.Empty, fileFullNames);
  961. }
  962. /// <summary>
  963. /// Packs files into the archive.
  964. /// </summary>
  965. /// <param name="fileFullNames">Array of file names to pack.</param>
  966. /// <param name="archiveStream">The archive output stream.
  967. /// Use CompressFiles(string archiveName ... ) overloads for archiving to disk.</param>
  968. public void CompressFiles(
  969. Stream archiveStream, params string[] fileFullNames)
  970. {
  971. CompressFilesEncrypted(archiveStream, string.Empty, fileFullNames);
  972. }
  973. /// <summary>
  974. /// Packs files into the archive.
  975. /// </summary>
  976. /// <param name="fileFullNames">Array of file names to pack.</param>
  977. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  978. /// <param name="archiveName">The archive file name.</param>
  979. public void CompressFiles(
  980. string archiveName, int commonRootLength, params string[] fileFullNames)
  981. {
  982. CompressFilesEncrypted(archiveName, commonRootLength, string.Empty, fileFullNames);
  983. }
  984. /// <summary>
  985. /// Packs files into the archive.
  986. /// </summary>
  987. /// <param name="fileFullNames">Array of file names to pack.</param>
  988. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  989. /// <param name="archiveStream">The archive output stream.
  990. /// Use CompressFiles(string archiveName, ... ) overloads for archiving to disk.</param>
  991. public void CompressFiles(
  992. Stream archiveStream, int commonRootLength, params string[] fileFullNames)
  993. {
  994. fileFullNames = GetFullFilePaths(fileFullNames);
  995. CompressFilesEncrypted(archiveStream, commonRootLength, string.Empty, fileFullNames);
  996. }
  997. /// <summary>
  998. /// Packs files into the archive.
  999. /// </summary>
  1000. /// <param name="fileFullNames">Array of file names to pack.</param>
  1001. /// <param name="archiveName">The archive file name.</param>
  1002. /// <param name="password">The archive password.</param>
  1003. public void CompressFilesEncrypted(
  1004. string archiveName, string password, params string[] fileFullNames)
  1005. {
  1006. fileFullNames = GetFullFilePaths(fileFullNames);
  1007. CompressFilesEncrypted(archiveName, CommonRoot(fileFullNames), password, fileFullNames);
  1008. }
  1009. /// <summary>
  1010. /// Packs files into the archive.
  1011. /// </summary>
  1012. /// <param name="fileFullNames">Array of file names to pack.</param>
  1013. /// <param name="archiveStream">The archive output stream.
  1014. /// Use CompressFiles( ... string archiveName ... ) overloads for archiving to disk.</param>
  1015. /// <param name="password">The archive password.</param>
  1016. public void CompressFilesEncrypted(
  1017. Stream archiveStream, string password, params string[] fileFullNames)
  1018. {
  1019. fileFullNames = GetFullFilePaths(fileFullNames);
  1020. CompressFilesEncrypted(archiveStream, CommonRoot(fileFullNames), password, fileFullNames);
  1021. }
  1022. /// <summary>
  1023. /// Packs files into the archive.
  1024. /// </summary>
  1025. /// <param name="fileFullNames">Array of file names to pack.</param>
  1026. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  1027. /// <param name="archiveName">The archive file name.</param>
  1028. /// <param name="password">The archive password.</param>
  1029. public void CompressFilesEncrypted(string archiveName, int commonRootLength, string password, params string[] fileFullNames)
  1030. {
  1031. _compressingFilesOnDisk = true;
  1032. _archiveName = archiveName;
  1033. using (var fs = GetArchiveFileStream(archiveName))
  1034. {
  1035. if (fs == null && _volumeSize == 0)
  1036. {
  1037. return;
  1038. }
  1039. CompressFilesEncrypted(fs, commonRootLength, password, fileFullNames);
  1040. }
  1041. FinalizeUpdate();
  1042. }
  1043. /// <summary>
  1044. /// Packs files into the archive.
  1045. /// </summary>
  1046. /// <param name="fileFullNames">Array of file names to pack.</param>
  1047. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  1048. /// <param name="archiveStream">The archive output stream.
  1049. /// Use CompressFiles( ... string archiveName ... ) overloads for archiving to disk.</param>
  1050. /// <param name="password">The archive password.</param>
  1051. public void CompressFilesEncrypted(
  1052. Stream archiveStream, int commonRootLength, string password, params string[] fileFullNames)
  1053. {
  1054. ClearExceptions();
  1055. if (fileFullNames.Length > 1 &&
  1056. (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
  1057. _archiveFormat == OutArchiveFormat.XZ))
  1058. {
  1059. if (!ThrowException(null,
  1060. new CompressionFailedException("Can not compress more than one file in this format.")))
  1061. {
  1062. return;
  1063. }
  1064. }
  1065. UpdateCompressorPassword(password);
  1066. if (_volumeSize == 0 || !_compressingFilesOnDisk)
  1067. {
  1068. ValidateStream(archiveStream);
  1069. }
  1070. FileInfo[] files = null;
  1071. try
  1072. {
  1073. files = ProduceFileInfoArray(fileFullNames, commonRootLength, _directoryCompress, DirectoryStructure);
  1074. }
  1075. catch (Exception e)
  1076. {
  1077. if (!ThrowException(null, e))
  1078. {
  1079. return;
  1080. }
  1081. }
  1082. _directoryCompress = false;
  1083. FilesFound?.Invoke(this, new IntEventArgs(fileFullNames.Length));
  1084. try
  1085. {
  1086. ISequentialOutStream sequentialArchiveStream;
  1087. using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
  1088. {
  1089. IInStream inArchiveStream;
  1090. using ((inArchiveStream = GetInStream()) as IDisposable)
  1091. {
  1092. IOutArchive outArchive;
  1093. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1094. {
  1095. SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
  1096. outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
  1097. }
  1098. else
  1099. {
  1100. // Create IInArchive, read it and convert to IOutArchive
  1101. SevenZipLibraryManager.LoadLibrary(this, Formats.InForOutFormats[_archiveFormat]);
  1102. if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
  1103. {
  1104. return;
  1105. }
  1106. }
  1107. using (var auc = GetArchiveUpdateCallback(files, commonRootLength, password))
  1108. {
  1109. try
  1110. {
  1111. if (files != null)
  1112. CheckedExecute(
  1113. outArchive.UpdateItems(
  1114. sequentialArchiveStream, (uint) files.Length + _oldFilesCount, auc),
  1115. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1116. }
  1117. finally
  1118. {
  1119. FreeCompressionCallback(auc);
  1120. }
  1121. }
  1122. }
  1123. }
  1124. }
  1125. finally
  1126. {
  1127. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1128. {
  1129. SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
  1130. }
  1131. else
  1132. {
  1133. SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
  1134. File.Delete(_archiveName);
  1135. }
  1136. _compressingFilesOnDisk = false;
  1137. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1138. }
  1139. ThrowUserException();
  1140. }
  1141. #endregion
  1142. #region CompressDirectory overloads
  1143. /// <summary>
  1144. /// Packs all files in the specified directory.
  1145. /// </summary>
  1146. /// <param name="directory">The directory to compress.</param>
  1147. /// <param name="archiveName">The archive file name.</param>
  1148. /// <param name="password">The archive password.</param>
  1149. /// <param name="searchPattern">Search string, such as "*.txt".</param>
  1150. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1151. public void CompressDirectory(string directory, string archiveName, string password = "", string searchPattern = "*", bool recursion = true)
  1152. {
  1153. _compressingFilesOnDisk = true;
  1154. _archiveName = archiveName;
  1155. using (var fs = GetArchiveFileStream(archiveName))
  1156. {
  1157. if (fs == null && _volumeSize == 0)
  1158. {
  1159. return;
  1160. }
  1161. CompressDirectory(directory, fs, password, searchPattern, recursion);
  1162. }
  1163. FinalizeUpdate();
  1164. }
  1165. /// <summary>
  1166. /// Packs all files in the specified directory.
  1167. /// </summary>
  1168. /// <param name="directory">The directory to compress.</param>
  1169. /// <param name="archiveStream">The archive output stream.
  1170. /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
  1171. /// <param name="password">The archive password.</param>
  1172. /// <param name="searchPattern">Search string, such as "*.txt".</param>
  1173. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1174. public void CompressDirectory(string directory, Stream archiveStream, string password = "", string searchPattern = "*", bool recursion = true)
  1175. {
  1176. var files = new List<string>();
  1177. if (!Directory.Exists(directory))
  1178. {
  1179. throw new ArgumentException("Directory \"" + directory + "\" does not exist!");
  1180. }
  1181. // Get full path, in case this is eg. an SFN path.
  1182. directory = Path.GetFullPath(directory);
  1183. if (RecursiveDirectoryEmptyCheck(directory))
  1184. {
  1185. throw new SevenZipInvalidFileNamesException("the specified directory is empty!");
  1186. }
  1187. if (recursion)
  1188. {
  1189. AddFilesFromDirectory(directory, files, searchPattern);
  1190. }
  1191. else
  1192. {
  1193. files.AddRange((new DirectoryInfo(directory)).GetFiles(searchPattern).Select(fi => fi.FullName));
  1194. }
  1195. var commonRootLength = directory.Length;
  1196. if (directory.EndsWith("\\", StringComparison.OrdinalIgnoreCase))
  1197. {
  1198. directory = directory.Substring(0, directory.Length - 1);
  1199. }
  1200. else
  1201. {
  1202. commonRootLength++;
  1203. }
  1204. if (PreserveDirectoryRoot)
  1205. {
  1206. var upperRoot = Path.GetDirectoryName(directory);
  1207. if (upperRoot != null)
  1208. {
  1209. commonRootLength = upperRoot.Length + (upperRoot.EndsWith("\\", StringComparison.OrdinalIgnoreCase) ? 0 : 1);
  1210. }
  1211. }
  1212. _directoryCompress = true;
  1213. CompressFilesEncrypted(archiveStream, commonRootLength, password, files.ToArray());
  1214. }
  1215. #endregion
  1216. #region CompressFileDictionary overloads
  1217. /// <summary>
  1218. /// Packs the specified file dictionary.
  1219. /// </summary>
  1220. /// <param name="fileDictionary">Dictionary&lt;name of the archive entry, file name&gt;.
  1221. /// If a file name is null, the corresponding archive entry becomes a directory.</param>
  1222. /// <param name="archiveName">The archive file name.</param>
  1223. /// <param name="password">The archive password.</param>
  1224. public void CompressFileDictionary(
  1225. IDictionary<string, string> fileDictionary, string archiveName, string password = "")
  1226. {
  1227. _compressingFilesOnDisk = true;
  1228. _archiveName = archiveName;
  1229. using (var fs = GetArchiveFileStream(archiveName))
  1230. {
  1231. if (fs == null && _volumeSize == 0)
  1232. {
  1233. return;
  1234. }
  1235. CompressFileDictionary(fileDictionary, fs, password);
  1236. }
  1237. FinalizeUpdate();
  1238. }
  1239. /// <summary>
  1240. /// Packs the specified file dictionary.
  1241. /// </summary>
  1242. /// <param name="fileDictionary">Dictionary&lt;name of the archive entry, file name&gt;.
  1243. /// If a file name is null, the corresponding archive entry becomes a directory.</param>
  1244. /// <param name="archiveStream">The archive output stream.
  1245. /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1246. /// <param name="password">The archive password.</param>
  1247. public void CompressFileDictionary(IDictionary<string, string> fileDictionary, Stream archiveStream, string password = "")
  1248. {
  1249. var streamDict = new Dictionary<string, Stream>(fileDictionary.Count);
  1250. foreach (var pair in fileDictionary)
  1251. {
  1252. if (pair.Value == null)
  1253. {
  1254. streamDict.Add(pair.Key, null);
  1255. }
  1256. else
  1257. {
  1258. if (!File.Exists(pair.Value))
  1259. {
  1260. throw new CompressionFailedException(
  1261. "The file corresponding to the archive entry \"" + pair.Key + "\" does not exist.");
  1262. }
  1263. streamDict.Add(
  1264. pair.Key,
  1265. new FileStream(pair.Value, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
  1266. }
  1267. }
  1268. //The created streams will be automatically disposed inside.
  1269. CompressStreamDictionary(streamDict, archiveStream, password);
  1270. }
  1271. #endregion
  1272. #region CompressStreamDictionary overloads
  1273. /// <summary>
  1274. /// Packs the specified stream dictionary.
  1275. /// </summary>
  1276. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1277. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1278. /// <param name="archiveName">The archive file name.</param>
  1279. /// <param name="password">The archive password.</param>
  1280. public void CompressStreamDictionary(IDictionary<string, Stream> streamDictionary, string archiveName, string password = "")
  1281. {
  1282. _compressingFilesOnDisk = true;
  1283. _archiveName = archiveName;
  1284. using (var fs = GetArchiveFileStream(archiveName))
  1285. {
  1286. if (fs == null && _volumeSize == 0)
  1287. {
  1288. return;
  1289. }
  1290. CompressStreamDictionary(streamDictionary, fs, password);
  1291. }
  1292. FinalizeUpdate();
  1293. }
  1294. /// <summary>
  1295. /// Packs the specified stream dictionary.
  1296. /// </summary>
  1297. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1298. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1299. /// <param name="archiveStream">The archive output stream.
  1300. /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1301. /// <param name="password">The archive password.</param>
  1302. public void CompressStreamDictionary(IDictionary<string, Stream> streamDictionary, Stream archiveStream, string password = "")
  1303. {
  1304. ClearExceptions();
  1305. if (streamDictionary.Count > 1 &&
  1306. (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
  1307. _archiveFormat == OutArchiveFormat.XZ))
  1308. {
  1309. if (!ThrowException(null,
  1310. new CompressionFailedException("Can not compress more than one file/stream in this format.")))
  1311. {
  1312. return;
  1313. }
  1314. }
  1315. if (_volumeSize == 0 || !_compressingFilesOnDisk)
  1316. {
  1317. ValidateStream(archiveStream);
  1318. }
  1319. UpdateCompressorPassword(password);
  1320. if (streamDictionary.Where(
  1321. pair => pair.Value != null && (!pair.Value.CanSeek || !pair.Value.CanRead)).Any(
  1322. pair => !ThrowException(null,
  1323. new ArgumentException(
  1324. $"The specified stream dictionary contains an invalid stream corresponding to the archive entry \"{pair.Key}\".",
  1325. nameof(streamDictionary)))))
  1326. {
  1327. return;
  1328. }
  1329. try
  1330. {
  1331. ISequentialOutStream sequentialArchiveStream;
  1332. using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
  1333. {
  1334. IInStream inArchiveStream;
  1335. using ((inArchiveStream = GetInStream()) as IDisposable)
  1336. {
  1337. IOutArchive outArchive;
  1338. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1339. {
  1340. SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
  1341. outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
  1342. }
  1343. else
  1344. {
  1345. // Create IInArchive, read it and convert to IOutArchive
  1346. SevenZipLibraryManager.LoadLibrary(
  1347. this, Formats.InForOutFormats[_archiveFormat]);
  1348. if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
  1349. {
  1350. return;
  1351. }
  1352. }
  1353. using (var auc = GetArchiveUpdateCallback(streamDictionary, password))
  1354. {
  1355. try
  1356. {
  1357. CheckedExecute(outArchive.UpdateItems(sequentialArchiveStream,
  1358. (uint) streamDictionary.Count + _oldFilesCount, auc),
  1359. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1360. }
  1361. finally
  1362. {
  1363. FreeCompressionCallback(auc);
  1364. }
  1365. }
  1366. }
  1367. }
  1368. }
  1369. finally
  1370. {
  1371. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1372. {
  1373. SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
  1374. }
  1375. else
  1376. {
  1377. SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
  1378. File.Delete(_archiveName);
  1379. }
  1380. _compressingFilesOnDisk = false;
  1381. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1382. }
  1383. ThrowUserException();
  1384. }
  1385. /// <summary>
  1386. /// Packs the specified stream dictionary.
  1387. /// </summary>
  1388. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1389. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1390. /// <param name="archiveStream">The archive output stream.
  1391. /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1392. /// <param name="password">The archive password.</param>
  1393. public void CompressStreamDictionaryWithFileTime(IDictionary<string, CompressStreamEntry> streamDictionary, Stream archiveStream, string password = "")
  1394. {
  1395. ClearExceptions();
  1396. if (streamDictionary.Count > 1 &&
  1397. (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
  1398. _archiveFormat == OutArchiveFormat.XZ))
  1399. {
  1400. if (!ThrowException(null,
  1401. new CompressionFailedException("Can not compress more than one file/stream in this format.")))
  1402. {
  1403. return;
  1404. }
  1405. }
  1406. if (_volumeSize == 0 || !_compressingFilesOnDisk)
  1407. {
  1408. ValidateStream(archiveStream);
  1409. }
  1410. UpdateCompressorPassword(password);
  1411. if (streamDictionary.Where(
  1412. pair => pair.Value.Stream != null && (!pair.Value.Stream.CanSeek || !pair.Value.Stream.CanRead)).Any(
  1413. pair => !ThrowException(null,
  1414. new ArgumentException(
  1415. $"The specified stream dictionary contains an invalid stream corresponding to the archive entry \"{pair.Key}\".",
  1416. nameof(streamDictionary)))))
  1417. {
  1418. return;
  1419. }
  1420. try
  1421. {
  1422. ISequentialOutStream sequentialArchiveStream;
  1423. using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
  1424. {
  1425. IInStream inArchiveStream;
  1426. using ((inArchiveStream = GetInStream()) as IDisposable)
  1427. {
  1428. IOutArchive outArchive;
  1429. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1430. {
  1431. SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
  1432. outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
  1433. }
  1434. else
  1435. {
  1436. // Create IInArchive, read it and convert to IOutArchive
  1437. SevenZipLibraryManager.LoadLibrary(
  1438. this, Formats.InForOutFormats[_archiveFormat]);
  1439. if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
  1440. {
  1441. return;
  1442. }
  1443. }
  1444. using (var auc = GetArchiveUpdateCallbackWithFileTime(streamDictionary, password))
  1445. {
  1446. try
  1447. {
  1448. CheckedExecute(outArchive.UpdateItems(sequentialArchiveStream,
  1449. (uint)streamDictionary.Count + _oldFilesCount, auc),
  1450. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1451. }
  1452. finally
  1453. {
  1454. FreeCompressionCallbackWithFileTime(auc);
  1455. }
  1456. }
  1457. }
  1458. }
  1459. }
  1460. finally
  1461. {
  1462. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1463. {
  1464. SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
  1465. }
  1466. else
  1467. {
  1468. SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
  1469. File.Delete(_archiveName);
  1470. }
  1471. _compressingFilesOnDisk = false;
  1472. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1473. }
  1474. ThrowUserException();
  1475. }
  1476. #endregion
  1477. #region CompressStream overloads
  1478. /// <summary>
  1479. /// Compresses the specified stream.
  1480. /// </summary>
  1481. /// <param name="inStream">The source uncompressed stream.</param>
  1482. /// <param name="outStream">The destination compressed stream.</param>
  1483. /// <param name="password">The archive password.</param>
  1484. /// <exception cref="ArgumentException">ArgumentException: at least one of the specified streams is invalid.</exception>
  1485. public void CompressStream(Stream inStream, Stream outStream, string password = "")
  1486. {
  1487. ClearExceptions();
  1488. if (!inStream.CanSeek || !inStream.CanRead || !outStream.CanWrite)
  1489. {
  1490. if (!ThrowException(null, new ArgumentException("The specified streams are invalid.")))
  1491. {
  1492. return;
  1493. }
  1494. }
  1495. try
  1496. {
  1497. SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
  1498. ISequentialOutStream sequentialArchiveStream;
  1499. using ((sequentialArchiveStream = GetOutStream(outStream)) as IDisposable)
  1500. {
  1501. using (var auc = GetArchiveUpdateCallback(inStream, password))
  1502. {
  1503. try
  1504. {
  1505. CheckedExecute(
  1506. SevenZipLibraryManager.OutArchive(_archiveFormat, this).UpdateItems(
  1507. sequentialArchiveStream, 1, auc),
  1508. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1509. }
  1510. finally
  1511. {
  1512. FreeCompressionCallback(auc);
  1513. }
  1514. }
  1515. }
  1516. }
  1517. finally
  1518. {
  1519. SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
  1520. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1521. }
  1522. ThrowUserException();
  1523. }
  1524. #endregion
  1525. #region ModifyArchive overloads
  1526. /// <summary>
  1527. /// Modifies the existing archive (renames files or deletes them).
  1528. /// </summary>
  1529. /// <param name="archiveName">The archive file name.</param>
  1530. /// <param name="newFileNames">New file names. Null value to delete the corresponding index.</param>
  1531. /// <param name="password">The archive password.</param>
  1532. public void ModifyArchive(string archiveName, IDictionary<int, string> newFileNames, string password = "")
  1533. {
  1534. ClearExceptions();
  1535. if (!SevenZipLibraryManager.ModifyCapable)
  1536. {
  1537. throw new SevenZipLibraryException("The specified 7zip native library does not support this method.");
  1538. }
  1539. if (!File.Exists(archiveName))
  1540. {
  1541. if (!ThrowException(null,
  1542. new ArgumentException("The specified archive does not exist.", nameof(archiveName))))
  1543. {
  1544. return;
  1545. }
  1546. }
  1547. if (newFileNames == null || newFileNames.Count == 0)
  1548. {
  1549. if (!ThrowException(null, new ArgumentException("Invalid new file names.", nameof(newFileNames))))
  1550. {
  1551. return;
  1552. }
  1553. }
  1554. UpdateCompressorPassword(password);
  1555. try
  1556. {
  1557. using (var extractor = new SevenZipExtractor(archiveName, password))
  1558. {
  1559. _updateData = new UpdateData();
  1560. var archiveData = new ArchiveFileInfo[extractor.ArchiveFileData.Count];
  1561. extractor.ArchiveFileData.CopyTo(archiveData, 0);
  1562. _updateData.ArchiveFileData = new List<ArchiveFileInfo>(archiveData);
  1563. }
  1564. _updateData.FileNamesToModify = newFileNames;
  1565. _updateData.Mode = InternalCompressionMode.Modify;
  1566. }
  1567. catch (SevenZipException e)
  1568. {
  1569. if (!ThrowException(null, e))
  1570. {
  1571. return;
  1572. }
  1573. }
  1574. try
  1575. {
  1576. ISequentialOutStream sequentialArchiveStream;
  1577. _compressingFilesOnDisk = true;
  1578. using ((sequentialArchiveStream = GetOutStream(GetArchiveFileStream(archiveName))) as IDisposable)
  1579. {
  1580. IInStream inArchiveStream;
  1581. _archiveName = archiveName;
  1582. using ((inArchiveStream = GetInStream()) as IDisposable)
  1583. {
  1584. IOutArchive outArchive;
  1585. // Create IInArchive, read it and convert to IOutArchive
  1586. SevenZipLibraryManager.LoadLibrary(
  1587. this, Formats.InForOutFormats[_archiveFormat]);
  1588. if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
  1589. {
  1590. return;
  1591. }
  1592. using (var auc = GetArchiveUpdateCallback(null, 0, password))
  1593. {
  1594. uint deleteCount = 0;
  1595. if (_updateData.FileNamesToModify != null)
  1596. {
  1597. deleteCount = (uint) _updateData.FileNamesToModify.Sum(
  1598. pairDeleted => pairDeleted.Value == null ? 1 : 0);
  1599. }
  1600. try
  1601. {
  1602. CheckedExecute(
  1603. outArchive.UpdateItems(
  1604. sequentialArchiveStream, _oldFilesCount - deleteCount, auc),
  1605. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1606. }
  1607. finally
  1608. {
  1609. FreeCompressionCallback(auc);
  1610. }
  1611. }
  1612. }
  1613. }
  1614. }
  1615. finally
  1616. {
  1617. SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
  1618. File.Delete(archiveName);
  1619. FinalizeUpdate();
  1620. _compressingFilesOnDisk = false;
  1621. _updateData.FileNamesToModify = null;
  1622. _updateData.ArchiveFileData = null;
  1623. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1624. }
  1625. ThrowUserException();
  1626. }
  1627. #endregion
  1628. #endregion
  1629. #endif
  1630. /// <summary>
  1631. /// Gets or sets the dictionary size for the managed LZMA algorithm.
  1632. /// </summary>
  1633. public static int LzmaDictionarySize
  1634. {
  1635. get => _lzmaDictionarySize;
  1636. set => _lzmaDictionarySize = value;
  1637. }
  1638. internal static void WriteLzmaProperties(Encoder encoder)
  1639. {
  1640. #region LZMA properties definition
  1641. CoderPropId[] propIDs =
  1642. {
  1643. CoderPropId.DictionarySize,
  1644. CoderPropId.PosStateBits,
  1645. CoderPropId.LitContextBits,
  1646. CoderPropId.LitPosBits,
  1647. CoderPropId.Algorithm,
  1648. CoderPropId.NumFastBytes,
  1649. CoderPropId.MatchFinder,
  1650. CoderPropId.EndMarker
  1651. };
  1652. object[] properties =
  1653. {
  1654. _lzmaDictionarySize,
  1655. 2,
  1656. 3,
  1657. 0,
  1658. 2,
  1659. 256,
  1660. "bt4",
  1661. false
  1662. };
  1663. #endregion
  1664. encoder.SetCoderProperties(propIDs, properties);
  1665. }
  1666. /// <summary>
  1667. /// Compresses the specified stream with LZMA algorithm (C# inside)
  1668. /// </summary>
  1669. /// <param name="inStream">The source uncompressed stream</param>
  1670. /// <param name="outStream">The destination compressed stream</param>
  1671. /// <param name="inLength">The length of uncompressed data (null for inStream.Length)</param>
  1672. /// <param name="codeProgressEvent">The event for handling the code progress</param>
  1673. public static void CompressStream(Stream inStream, Stream outStream, int? inLength,
  1674. EventHandler<ProgressEventArgs> codeProgressEvent)
  1675. {
  1676. if (!inStream.CanRead || !outStream.CanWrite)
  1677. {
  1678. throw new ArgumentException("The specified streams are invalid.");
  1679. }
  1680. var encoder = new Encoder();
  1681. WriteLzmaProperties(encoder);
  1682. encoder.WriteCoderProperties(outStream);
  1683. var streamSize = inLength ?? inStream.Length;
  1684. for (var i = 0; i < 8; i++)
  1685. {
  1686. outStream.WriteByte((byte) (streamSize >> (8 * i)));
  1687. }
  1688. encoder.Code(inStream, outStream, -1, -1, new LzmaProgressCallback(streamSize, codeProgressEvent));
  1689. }
  1690. /// <summary>
  1691. /// Compresses byte array with LZMA algorithm (C# inside)
  1692. /// </summary>
  1693. /// <param name="data">Byte array to compress</param>
  1694. /// <returns>Compressed byte array</returns>
  1695. public static byte[] CompressBytes(byte[] data)
  1696. {
  1697. using (var inStream = new MemoryStream(data))
  1698. {
  1699. using (var outStream = new MemoryStream())
  1700. {
  1701. var encoder = new Encoder();
  1702. WriteLzmaProperties(encoder);
  1703. encoder.WriteCoderProperties(outStream);
  1704. var streamSize = inStream.Length;
  1705. for (var i = 0; i < 8; i++)
  1706. {
  1707. outStream.WriteByte((byte) (streamSize >> (8 * i)));
  1708. }
  1709. encoder.Code(inStream, outStream, -1, -1, null);
  1710. return outStream.ToArray();
  1711. }
  1712. }
  1713. }
  1714. /// <summary>
  1715. /// Ensures an array of file names is the full path to that file.
  1716. /// </summary>
  1717. /// <param name="fileFullNames">Array of file names.</param>
  1718. /// <returns>Array of file names with full paths.</returns>
  1719. private static string[] GetFullFilePaths(IEnumerable<string> fileFullNames)
  1720. {
  1721. return fileFullNames.Select(Path.GetFullPath).ToArray();
  1722. }
  1723. /// <summary>
  1724. /// Check and update password in SevenZipCompressor
  1725. /// </summary>
  1726. /// <param name="password">The password to use.</param>
  1727. private void UpdateCompressorPassword(string password)
  1728. {
  1729. if (!string.IsNullOrEmpty(password) && string.IsNullOrEmpty(Password))
  1730. {
  1731. // When modifying an encrypted archive, Password is not set in the SevenZipCompressor.
  1732. Password = password;
  1733. }
  1734. }
  1735. }
  1736. }