567 lines
23 KiB
C#
567 lines
23 KiB
C#
/* This file is part of SevenZipSharp.
|
|
|
|
SevenZipSharp is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SevenZipSharp is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with SevenZipSharp. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
#if !WINCE && !MONO
|
|
using System.Configuration;
|
|
using System.Diagnostics;
|
|
using System.Security.Permissions;
|
|
#endif
|
|
#if WINCE
|
|
using OpenNETCF.Diagnostics;
|
|
#endif
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
#if MONO
|
|
using SevenZip.Mono.COM;
|
|
#endif
|
|
|
|
namespace SevenZip
|
|
{
|
|
#if UNMANAGED
|
|
/// <summary>
|
|
/// 7-zip library low-level wrapper.
|
|
/// </summary>
|
|
internal static class SevenZipLibraryManager
|
|
{
|
|
#if !WINCE && !MONO
|
|
/// <summary>
|
|
/// Path to the 7-zip dll.
|
|
/// </summary>
|
|
/// <remarks>7zxa.dll supports only decoding from .7z archives.
|
|
/// Features of 7za.dll:
|
|
/// - Supporting 7z format;
|
|
/// - Built encoders: LZMA, PPMD, BCJ, BCJ2, COPY, AES-256 Encryption.
|
|
/// - Built decoders: LZMA, PPMD, BCJ, BCJ2, COPY, AES-256 Encryption, BZip2, Deflate.
|
|
/// 7z.dll (from the 7-zip distribution) supports every InArchiveFormat for encoding and decoding.
|
|
/// </remarks>
|
|
//private static string _libraryFileName = ConfigurationManager.AppSettings["7zLocation"] ?? Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "7z.dll");
|
|
private static string _libraryFileName = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "7z.dll");
|
|
#endif
|
|
#if WINCE
|
|
private static string _libraryFileName =
|
|
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase), "7z.dll");
|
|
#endif
|
|
/// <summary>
|
|
/// 7-zip library handle.
|
|
/// </summary>
|
|
private static IntPtr _modulePtr;
|
|
|
|
/// <summary>
|
|
/// 7-zip library features.
|
|
/// </summary>
|
|
private static LibraryFeature? _features;
|
|
|
|
private static Dictionary<object, Dictionary<InArchiveFormat, IInArchive>> _inArchives;
|
|
#if COMPRESS
|
|
private static Dictionary<object, Dictionary<OutArchiveFormat, IOutArchive>> _outArchives;
|
|
#endif
|
|
private static int _totalUsers;
|
|
|
|
// private static string _LibraryVersion;
|
|
private static bool? _modifyCapabale;
|
|
|
|
private static void InitUserInFormat(object user, InArchiveFormat format)
|
|
{
|
|
if (!_inArchives.ContainsKey(user))
|
|
{
|
|
_inArchives.Add(user, new Dictionary<InArchiveFormat, IInArchive>());
|
|
}
|
|
if (!_inArchives[user].ContainsKey(format))
|
|
{
|
|
_inArchives[user].Add(format, null);
|
|
_totalUsers++;
|
|
}
|
|
}
|
|
|
|
#if COMPRESS
|
|
private static void InitUserOutFormat(object user, OutArchiveFormat format)
|
|
{
|
|
if (!_outArchives.ContainsKey(user))
|
|
{
|
|
_outArchives.Add(user, new Dictionary<OutArchiveFormat, IOutArchive>());
|
|
}
|
|
if (!_outArchives[user].ContainsKey(format))
|
|
{
|
|
_outArchives[user].Add(format, null);
|
|
_totalUsers++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
private static void Init()
|
|
{
|
|
_inArchives = new Dictionary<object, Dictionary<InArchiveFormat, IInArchive>>();
|
|
#if COMPRESS
|
|
_outArchives = new Dictionary<object, Dictionary<OutArchiveFormat, IOutArchive>>();
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads the 7-zip library if necessary and adds user to the reference list
|
|
/// </summary>
|
|
/// <param name="user">Caller of the function</param>
|
|
/// <param name="format">Archive format</param>
|
|
public static void LoadLibrary(object user, Enum format)
|
|
{
|
|
if (_inArchives == null
|
|
#if COMPRESS
|
|
|| _outArchives == null
|
|
#endif
|
|
)
|
|
{
|
|
Init();
|
|
}
|
|
#if !WINCE && !MONO
|
|
if (_modulePtr == IntPtr.Zero)
|
|
{
|
|
//zero 29-oct-2012 - this check isnt useful since LoadLibrary can pretty much check for the same thing. and it wrecks our dll relocation scheme
|
|
//if (!File.Exists(_libraryFileName))
|
|
//{
|
|
// throw new SevenZipLibraryException("DLL file does not exist.");
|
|
//}
|
|
if ((_modulePtr = NativeMethods.LoadLibrary(_libraryFileName)) == IntPtr.Zero)
|
|
{
|
|
//try a different directory
|
|
string alternateFilename = Path.Combine(Path.Combine(Path.GetDirectoryName(_libraryFileName),"dll"),"7z.dll");
|
|
if ((_modulePtr = NativeMethods.LoadLibrary(alternateFilename)) == IntPtr.Zero)
|
|
throw new SevenZipLibraryException("failed to load library.");
|
|
}
|
|
if (NativeMethods.GetProcAddress(_modulePtr, "GetHandlerProperty") == IntPtr.Zero)
|
|
{
|
|
NativeMethods.FreeLibrary(_modulePtr);
|
|
throw new SevenZipLibraryException("library is invalid.");
|
|
}
|
|
}
|
|
#endif
|
|
if (format is InArchiveFormat)
|
|
{
|
|
InitUserInFormat(user, (InArchiveFormat) format);
|
|
return;
|
|
}
|
|
#if COMPRESS
|
|
if (format is OutArchiveFormat)
|
|
{
|
|
InitUserOutFormat(user, (OutArchiveFormat) format);
|
|
return;
|
|
}
|
|
#endif
|
|
throw new ArgumentException(
|
|
"Enum " + format + " is not a valid archive format attribute!");
|
|
}
|
|
|
|
/*/// <summary>
|
|
/// Gets the native 7zip library version string.
|
|
/// </summary>
|
|
public static string LibraryVersion
|
|
{
|
|
get
|
|
{
|
|
if (String.IsNullOrEmpty(_LibraryVersion))
|
|
{
|
|
FileVersionInfo dllVersionInfo = FileVersionInfo.GetVersionInfo(_libraryFileName);
|
|
_LibraryVersion = String.Format(
|
|
System.Globalization.CultureInfo.CurrentCulture,
|
|
"{0}.{1}",
|
|
dllVersionInfo.FileMajorPart, dllVersionInfo.FileMinorPart);
|
|
}
|
|
return _LibraryVersion;
|
|
}
|
|
}*/
|
|
|
|
/// <summary>
|
|
/// Gets the value indicating whether the library supports modifying archives.
|
|
/// </summary>
|
|
public static bool ModifyCapable
|
|
{
|
|
get
|
|
{
|
|
if (!_modifyCapabale.HasValue)
|
|
{
|
|
#if !WINCE && !MONO
|
|
FileVersionInfo dllVersionInfo = FileVersionInfo.GetVersionInfo(_libraryFileName);
|
|
_modifyCapabale = dllVersionInfo.FileMajorPart >= 9;
|
|
#else
|
|
_modifyCapabale = true;
|
|
#endif
|
|
}
|
|
return _modifyCapabale.Value;
|
|
}
|
|
}
|
|
|
|
static readonly string Namespace = Assembly.GetExecutingAssembly().GetManifestResourceNames()[0].Split('.')[0];
|
|
|
|
private static string GetResourceString(string str)
|
|
{
|
|
return Namespace + ".arch." + str;
|
|
}
|
|
|
|
private static bool ExtractionBenchmark(string archiveFileName, Stream outStream,
|
|
ref LibraryFeature? features, LibraryFeature testedFeature)
|
|
{
|
|
var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(
|
|
GetResourceString(archiveFileName));
|
|
try
|
|
{
|
|
using (var extr = new SevenZipExtractor(stream))
|
|
{
|
|
extr.ExtractFile(0, outStream);
|
|
}
|
|
}
|
|
catch(Exception)
|
|
{
|
|
return false;
|
|
}
|
|
features |= testedFeature;
|
|
return true;
|
|
}
|
|
|
|
private static bool CompressionBenchmark(Stream inStream, Stream outStream,
|
|
OutArchiveFormat format, CompressionMethod method,
|
|
ref LibraryFeature? features, LibraryFeature testedFeature)
|
|
{
|
|
try
|
|
{
|
|
var compr = new SevenZipCompressor {ArchiveFormat = format, CompressionMethod = method};
|
|
compr.CompressStream(inStream, outStream);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return false;
|
|
}
|
|
features |= testedFeature;
|
|
return true;
|
|
}
|
|
|
|
public static LibraryFeature CurrentLibraryFeatures
|
|
{
|
|
get
|
|
{
|
|
if (_features != null && _features.HasValue)
|
|
{
|
|
return _features.Value;
|
|
}
|
|
_features = LibraryFeature.None;
|
|
#region Benchmark
|
|
#region Extraction features
|
|
using (var outStream = new MemoryStream())
|
|
{
|
|
ExtractionBenchmark("Test.lzma.7z", outStream, ref _features, LibraryFeature.Extract7z);
|
|
ExtractionBenchmark("Test.lzma2.7z", outStream, ref _features, LibraryFeature.Extract7zLZMA2);
|
|
int i = 0;
|
|
if (ExtractionBenchmark("Test.bzip2.7z", outStream, ref _features, _features.Value))
|
|
{
|
|
i++;
|
|
}
|
|
if (ExtractionBenchmark("Test.ppmd.7z", outStream, ref _features, _features.Value))
|
|
{
|
|
i++;
|
|
if (i == 2 && (_features & LibraryFeature.Extract7z) != 0 &&
|
|
(_features & LibraryFeature.Extract7zLZMA2) != 0)
|
|
{
|
|
_features |= LibraryFeature.Extract7zAll;
|
|
}
|
|
}
|
|
ExtractionBenchmark("Test.rar", outStream, ref _features, LibraryFeature.ExtractRar);
|
|
ExtractionBenchmark("Test.tar", outStream, ref _features, LibraryFeature.ExtractTar);
|
|
ExtractionBenchmark("Test.txt.bz2", outStream, ref _features, LibraryFeature.ExtractBzip2);
|
|
ExtractionBenchmark("Test.txt.gz", outStream, ref _features, LibraryFeature.ExtractGzip);
|
|
ExtractionBenchmark("Test.txt.xz", outStream, ref _features, LibraryFeature.ExtractXz);
|
|
ExtractionBenchmark("Test.zip", outStream, ref _features, LibraryFeature.ExtractZip);
|
|
}
|
|
#endregion
|
|
#region Compression features
|
|
using (var inStream = new MemoryStream())
|
|
{
|
|
inStream.Write(Encoding.UTF8.GetBytes("Test"), 0, 4);
|
|
using (var outStream = new MemoryStream())
|
|
{
|
|
CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.SevenZip, CompressionMethod.Lzma,
|
|
ref _features, LibraryFeature.Compress7z);
|
|
CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.SevenZip, CompressionMethod.Lzma2,
|
|
ref _features, LibraryFeature.Compress7zLZMA2);
|
|
int i = 0;
|
|
if (CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.SevenZip, CompressionMethod.BZip2,
|
|
ref _features, _features.Value))
|
|
{
|
|
i++;
|
|
}
|
|
if (CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.SevenZip, CompressionMethod.Ppmd,
|
|
ref _features, _features.Value))
|
|
{
|
|
i++;
|
|
if (i == 2 && (_features & LibraryFeature.Compress7z) != 0 &&
|
|
(_features & LibraryFeature.Compress7zLZMA2) != 0)
|
|
{
|
|
_features |= LibraryFeature.Compress7zAll;
|
|
}
|
|
}
|
|
CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.Zip, CompressionMethod.Default,
|
|
ref _features, LibraryFeature.CompressZip);
|
|
CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.BZip2, CompressionMethod.Default,
|
|
ref _features, LibraryFeature.CompressBzip2);
|
|
CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.GZip, CompressionMethod.Default,
|
|
ref _features, LibraryFeature.CompressGzip);
|
|
CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.Tar, CompressionMethod.Default,
|
|
ref _features, LibraryFeature.CompressTar);
|
|
CompressionBenchmark(inStream, outStream,
|
|
OutArchiveFormat.XZ, CompressionMethod.Default,
|
|
ref _features, LibraryFeature.CompressXz);
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
if (ModifyCapable && (_features.Value & LibraryFeature.Compress7z) != 0)
|
|
{
|
|
_features |= LibraryFeature.Modify;
|
|
}
|
|
return _features.Value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes user from reference list and frees the 7-zip library if it becomes empty
|
|
/// </summary>
|
|
/// <param name="user">Caller of the function</param>
|
|
/// <param name="format">Archive format</param>
|
|
public static void FreeLibrary(object user, Enum format)
|
|
{
|
|
#if !WINCE && !MONO
|
|
var sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
|
|
sp.Demand();
|
|
#endif
|
|
if (_modulePtr != IntPtr.Zero)
|
|
{
|
|
if (format is InArchiveFormat)
|
|
{
|
|
if (_inArchives != null && _inArchives.ContainsKey(user) &&
|
|
_inArchives[user].ContainsKey((InArchiveFormat) format) &&
|
|
_inArchives[user][(InArchiveFormat) format] != null)
|
|
{
|
|
try
|
|
{
|
|
Marshal.ReleaseComObject(_inArchives[user][(InArchiveFormat) format]);
|
|
}
|
|
catch (InvalidComObjectException) {}
|
|
_inArchives[user].Remove((InArchiveFormat) format);
|
|
_totalUsers--;
|
|
if (_inArchives[user].Count == 0)
|
|
{
|
|
_inArchives.Remove(user);
|
|
}
|
|
}
|
|
}
|
|
#if COMPRESS
|
|
if (format is OutArchiveFormat)
|
|
{
|
|
if (_outArchives != null && _outArchives.ContainsKey(user) &&
|
|
_outArchives[user].ContainsKey((OutArchiveFormat) format) &&
|
|
_outArchives[user][(OutArchiveFormat) format] != null)
|
|
{
|
|
try
|
|
{
|
|
Marshal.ReleaseComObject(_outArchives[user][(OutArchiveFormat) format]);
|
|
}
|
|
catch (InvalidComObjectException) {}
|
|
_outArchives[user].Remove((OutArchiveFormat) format);
|
|
_totalUsers--;
|
|
if (_outArchives[user].Count == 0)
|
|
{
|
|
_outArchives.Remove(user);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if ((_inArchives == null || _inArchives.Count == 0)
|
|
#if COMPRESS
|
|
&& (_outArchives == null || _outArchives.Count == 0)
|
|
#endif
|
|
)
|
|
{
|
|
_inArchives = null;
|
|
#if COMPRESS
|
|
_outArchives = null;
|
|
#endif
|
|
if (_totalUsers == 0)
|
|
{
|
|
#if !WINCE && !MONO
|
|
NativeMethods.FreeLibrary(_modulePtr);
|
|
|
|
#endif
|
|
_modulePtr = IntPtr.Zero;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets IInArchive interface to extract 7-zip archives.
|
|
/// </summary>
|
|
/// <param name="format">Archive format.</param>
|
|
/// <param name="user">Archive format user.</param>
|
|
public static IInArchive InArchive(InArchiveFormat format, object user)
|
|
{
|
|
#if !WINCE && !MONO
|
|
lock (_libraryFileName)
|
|
{
|
|
#endif
|
|
if (_inArchives[user][format] == null)
|
|
{
|
|
#if !WINCE && !MONO
|
|
var sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
|
|
sp.Demand();
|
|
|
|
if (_modulePtr == IntPtr.Zero)
|
|
{
|
|
LoadLibrary(user, format);
|
|
if (_modulePtr == IntPtr.Zero)
|
|
{
|
|
throw new SevenZipLibraryException();
|
|
}
|
|
}
|
|
var createObject = (NativeMethods.CreateObjectDelegate)
|
|
Marshal.GetDelegateForFunctionPointer(
|
|
NativeMethods.GetProcAddress(_modulePtr, "CreateObject"),
|
|
typeof(NativeMethods.CreateObjectDelegate));
|
|
if (createObject == null)
|
|
{
|
|
throw new SevenZipLibraryException();
|
|
}
|
|
#endif
|
|
object result;
|
|
Guid interfaceId =
|
|
#if !WINCE && !MONO
|
|
typeof(IInArchive).GUID;
|
|
#else
|
|
new Guid(((GuidAttribute)typeof(IInArchive).GetCustomAttributes(typeof(GuidAttribute), false)[0]).Value);
|
|
#endif
|
|
Guid classID = Formats.InFormatGuids[format];
|
|
try
|
|
{
|
|
#if !WINCE && !MONO
|
|
createObject(ref classID, ref interfaceId, out result);
|
|
#elif !MONO
|
|
NativeMethods.CreateCOMObject(ref classID, ref interfaceId, out result);
|
|
#else
|
|
result = SevenZip.Mono.Factory.CreateInterface<IInArchive>(user, classID, interfaceId);
|
|
#endif
|
|
}
|
|
catch (Exception)
|
|
{
|
|
throw new SevenZipLibraryException("Your 7-zip library does not support this archive type.");
|
|
}
|
|
InitUserInFormat(user, format);
|
|
_inArchives[user][format] = result as IInArchive;
|
|
}
|
|
#if !WINCE && !MONO
|
|
}
|
|
#endif
|
|
return _inArchives[user][format];
|
|
}
|
|
|
|
#if COMPRESS
|
|
/// <summary>
|
|
/// Gets IOutArchive interface to pack 7-zip archives.
|
|
/// </summary>
|
|
/// <param name="format">Archive format.</param>
|
|
/// <param name="user">Archive format user.</param>
|
|
public static IOutArchive OutArchive(OutArchiveFormat format, object user)
|
|
{
|
|
#if !WINCE && !MONO
|
|
lock (_libraryFileName)
|
|
{
|
|
#endif
|
|
if (_outArchives[user][format] == null)
|
|
{
|
|
#if !WINCE && !MONO
|
|
var sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
|
|
sp.Demand();
|
|
if (_modulePtr == IntPtr.Zero)
|
|
{
|
|
throw new SevenZipLibraryException();
|
|
}
|
|
var createObject = (NativeMethods.CreateObjectDelegate)
|
|
Marshal.GetDelegateForFunctionPointer(
|
|
NativeMethods.GetProcAddress(_modulePtr, "CreateObject"),
|
|
typeof(NativeMethods.CreateObjectDelegate));
|
|
if (createObject == null)
|
|
{
|
|
throw new SevenZipLibraryException();
|
|
}
|
|
#endif
|
|
object result;
|
|
Guid interfaceId =
|
|
#if !WINCE && !MONO
|
|
typeof(IOutArchive).GUID;
|
|
#else
|
|
new Guid(((GuidAttribute)typeof(IOutArchive).GetCustomAttributes(typeof(GuidAttribute), false)[0]).Value);
|
|
#endif
|
|
Guid classID = Formats.OutFormatGuids[format];
|
|
try
|
|
{
|
|
#if !WINCE && !MONO
|
|
createObject(ref classID, ref interfaceId, out result);
|
|
#elif !MONO
|
|
NativeMethods.CreateCOMObject(ref classID, ref interfaceId, out result);
|
|
#else
|
|
result = SevenZip.Mono.Factory.CreateInterface<IOutArchive>(classID, interfaceId, user);
|
|
#endif
|
|
}
|
|
catch (Exception)
|
|
{
|
|
throw new SevenZipLibraryException("Your 7-zip library does not support this archive type.");
|
|
}
|
|
InitUserOutFormat(user, format);
|
|
_outArchives[user][format] = result as IOutArchive;
|
|
}
|
|
#if !WINCE && !MONO
|
|
}
|
|
#endif
|
|
return _outArchives[user][format];
|
|
}
|
|
#endif
|
|
#if !WINCE && !MONO
|
|
public static void SetLibraryPath(string libraryPath)
|
|
{
|
|
if (_modulePtr != IntPtr.Zero && !Path.GetFullPath(libraryPath).Equals(
|
|
Path.GetFullPath(_libraryFileName), StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new SevenZipLibraryException(
|
|
"can not change the library path while the library \"" + _libraryFileName + "\" is being used.");
|
|
}
|
|
if (!File.Exists(libraryPath))
|
|
{
|
|
throw new SevenZipLibraryException(
|
|
"can not change the library path because the file \"" + libraryPath + "\" does not exist.");
|
|
}
|
|
_libraryFileName = libraryPath;
|
|
_features = null;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
} |