本文介绍了提取在C#中的.cab文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在开发C#应用程序,我需要解压CAB文件。

I am developing a c# application and I a need to extract a cab file.

我找不到,做,在C#)我不能使用的库Microsoft.Deployment.Compression.Cab.dll因为一个发牌的问题。

I couldn't find a library that does that in C# ) I cannot use Microsoft.Deployment.Compression.Cab.dll because of a licensing issue.

我发现的代码,但问题是,当我用它我能发现并提取只能在机柜中的第一个文件。

I found this code, but the problem is that when I use it I am able to find and extract only the first file in the cabinet.

OutputFileClose被称为只有OutputFileOpen或者返回的东西,然后IntPtr.Zero。
,但如果OutputFileClose是卡列斯,然后枚举停止。
因此,对于这个代码OutputFileClose可以只为一个文件名为

OutputFileClose is called only if OutputFileOpen returns something either then IntPtr.Zero.but if OutputFileClose is calles, then the enumeration is stopped.So for this code OutputFileClose can be called only for one file

有人可以帮我找出如何编写代码,将提取所有的文件吗?

Can someone please help me figuring out how to write a code that will extract all the files?

推荐答案

我发现Microsoft.Deployment.Compression.cab DLL也可以从的

I found out that Microsoft.Deployment.Compression.cab DLL can also be obtained from here

如果你看看以前的版本,如的你会看到,他们与通用公共许可证1.0版本(CPL)许可。

if you look at previous versions such as version 3.5 you will see that they were licensed with Common Public License Version 1.0 (CPL).

看来只有在以后的版本牌照是改变MS-RL。

It seems that only in later versions the license was changes to MS-RL.

我也能够创建我自己的解决方案,但不是最优的(我停止做这个工作,因为我发现我可以使用Microsoft.Deployment.Compression.cab)。

I was also able to create a solution of my own, but it is not optimal( I stopped working on it since I found that I can use Microsoft.Deployment.Compression.cab).

这是代码:

public class CabExtractor : IDisposable
{
    private static class NativeMethods
    {
        [StructLayout(LayoutKind.Sequential)]
        internal class CabError //Cabinet API: "ERF"
        {
            public int erfOper;
            public int erfType;
            public int fError;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal class FdiNotification //Cabinet API: "FDINOTIFICATION"
        {
            internal int cb;
            //not sure if this should be a IntPtr or a strong
            internal IntPtr psz1;
            internal IntPtr psz2;
            internal IntPtr psz3;
            internal IntPtr pv;
            internal IntPtr hf;
            internal short date;
            internal short time;
            internal short attribs;
            internal short setID;
            internal short iCabinet;
            internal short iFolder;
            internal int fdie;

        }

        internal enum FdiNotificationType
        {
            CabinetInfo,
            PartialFile,
            CopyFile,
            CloseFileInfo,
            NextCabinet,
            Enumerate
        }


        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate IntPtr FdiMemAllocDelegate(int numBytes);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate void FdiMemFreeDelegate(IntPtr mem);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate IntPtr FdiFileOpenDelegate(string fileName, int oflag, int pmode);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate Int32 FdiFileReadDelegate(IntPtr hf,
                                                   [In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2,
                                                       ArraySubType = UnmanagedType.U1)] byte[] buffer, int cb);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate Int32 FdiFileWriteDelegate(IntPtr hf,
                                                    [In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2,
                                                        ArraySubType = UnmanagedType.U1)] byte[] buffer, int cb);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate Int32 FdiFileCloseDelegate(IntPtr hf);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate Int32 FdiFileSeekDelegate(IntPtr hf, int dist, int seektype);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate IntPtr FdiNotifyDelegate(
            FdiNotificationType fdint, [In] [MarshalAs(UnmanagedType.LPStruct)] FdiNotification fdin);

        [DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDICreate", CharSet = CharSet.Ansi)]
        internal static extern IntPtr FdiCreate(
            FdiMemAllocDelegate fnMemAlloc,
            FdiMemFreeDelegate fnMemFree,
            FdiFileOpenDelegate fnFileOpen,
            FdiFileReadDelegate fnFileRead,
            FdiFileWriteDelegate fnFileWrite,
            FdiFileCloseDelegate fnFileClose,
            FdiFileSeekDelegate fnFileSeek,
            int cpuType,
            [MarshalAs(UnmanagedType.LPStruct)] CabError erf);

        [DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDIDestroy", CharSet = CharSet.Ansi)]
        internal static extern bool FdiDestroy(IntPtr hfdi);

        [DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDICopy", CharSet = CharSet.Ansi)]
        internal static extern bool FdiCopy(
            IntPtr hfdi,
            string cabinetName,
            string cabinetPath,
            int flags,
            FdiNotifyDelegate fnNotify,
            IntPtr fnDecrypt,
            IntPtr userData);
    }

    internal class ArchiveFile
    {
        public IntPtr Handle { get; set; }
        public string Name { get; set; }
        public bool Found { get; set; }
        public int Length { get; set; }
        public byte[] Data { get; set; }
    }

    #region fields and properties

    /// Very important!
    /// Do not try to call directly to this methods, instead use the delegates. if you use them directly it may cause application crashes, corruption and data loss.
    /// Using fields to save the delegate so that the delegate won't be garbage collected  !
    /// When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
    private readonly NativeMethods.FdiMemAllocDelegate _fdiAllocMemHandler;
    private readonly NativeMethods.FdiMemFreeDelegate _fdiFreeMemHandler;
    private readonly NativeMethods.FdiFileOpenDelegate _fdiOpenStreamHandler;
    private readonly NativeMethods.FdiFileReadDelegate _fdiReadStreamHandler;
    private readonly NativeMethods.FdiFileWriteDelegate _fdiWriteStreamHandler;
    private readonly NativeMethods.FdiFileCloseDelegate _fdiCloseStreamHandler;
    private readonly NativeMethods.FdiFileSeekDelegate _fdiSeekStreamHandler;

    private ArchiveFile _currentFileToDecompress;
    readonly List<string> _fileNames = new List<string>();
    private readonly NativeMethods.CabError _erf;
    private const int CpuTypeUnknown = -1;
    private readonly byte[] _inputData;
    private bool _disposed;
    /// <summary>
    /// 
    /// </summary>
    private readonly List<string> _subDirectoryToIgnore = new List<string>();
    /// <summary>
    /// Path to the folder where the files will be extracted to
    /// </summary>
    private readonly string _extractionFolderPath;
    /// <summary>
    /// The name of the folder where the files will be extracted to
    /// </summary>
    public const string ExtractedFolderName = "ExtractedFiles";

    public const string CabFileName = "setup.cab";

    #endregion

    public CabExtractor(string cabFilePath, IEnumerable<string> subDirectoryToUnpack)
        : this(cabFilePath)
    {
        if (subDirectoryToUnpack != null)
            _subDirectoryToIgnore.AddRange(subDirectoryToUnpack);
    }
    public CabExtractor(string cabFilePath)
    {
        var cabBytes =
           File.ReadAllBytes(cabFilePath);
        _inputData = cabBytes;
        var cabFileLocation = Path.GetDirectoryName(cabFilePath) ?? "";
        _extractionFolderPath = Path.Combine(cabFileLocation, ExtractedFolderName);
        _erf = new NativeMethods.CabError();
        FdiContext = IntPtr.Zero;

        _fdiAllocMemHandler = MemAlloc;
        _fdiFreeMemHandler = MemFree;
        _fdiOpenStreamHandler = InputFileOpen;
        _fdiReadStreamHandler = FileRead;
        _fdiWriteStreamHandler = FileWrite;
        _fdiCloseStreamHandler = InputFileClose;
        _fdiSeekStreamHandler = FileSeek;

        FdiContext = FdiCreate(_fdiAllocMemHandler, _fdiFreeMemHandler, _fdiOpenStreamHandler, _fdiReadStreamHandler, _fdiWriteStreamHandler, _fdiCloseStreamHandler, _fdiSeekStreamHandler, _erf);


    }







    public bool ExtractCabFiles()
    {
        if (!FdiIterate())
        {
            throw new Exception("Failed to iterate cab files");
        }

        foreach (var file in _fileNames)
        {
            ExtractFile(file);
        }
        return true;
    }

    private void ExtractFile(string fileName)
    {
        try
        {
            _currentFileToDecompress = new ArchiveFile { Name = fileName };
            FdiCopy();
            CreateAllRelevantDirectories(fileName);
            if (_currentFileToDecompress.Data != null)
            {
                File.WriteAllBytes(Path.Combine(_extractionFolderPath, _currentFileToDecompress.Name), _currentFileToDecompress.Data);
            }
        }
        catch (Exception ex)
        {

            SbaLogger.Instance.Error(ex);
            SbaLogger.Instance.Error(string.Format("Failed to cextract file file {0}", fileName));
        }

    }

    private void CreateAllRelevantDirectories(string filePath)
    {
        try
        {
            if (!Directory.Exists(_extractionFolderPath))
            {
                Directory.CreateDirectory(_extractionFolderPath);
            }
            var fullPathToFile = Path.GetDirectoryName(filePath);
            if (fullPathToFile != null &&
                !Directory.Exists(Path.Combine(_extractionFolderPath, fullPathToFile)))
            {
                Directory.CreateDirectory(Path.Combine(_extractionFolderPath, fullPathToFile));
            }
        }
        catch (Exception ex)
        {
            SbaLogger.Instance.Error(ex);
            SbaLogger.Instance.Error(string.Format("Failed to create directories for the file {0}",filePath));
        }

    }



    private static string GetFileName(NativeMethods.FdiNotification notification)
    {
        var encoding = ((int)notification.attribs & 128) != 0 ? Encoding.UTF8 : Encoding.Default;
        int length = 0;
        while (Marshal.ReadByte(notification.psz1, length) != 0)
            checked { ++length; }
        var numArray = new byte[length];
        Marshal.Copy(notification.psz1, numArray, 0, length);
        string path = encoding.GetString(numArray);
        if (Path.IsPathRooted(path))
            path = path.Replace(String.Concat(Path.VolumeSeparatorChar), "");
        return path;
    }
    private IntPtr ExtractCallback(NativeMethods.FdiNotificationType fdint, NativeMethods.FdiNotification fdin)
    {
        switch (fdint)
        {
            case NativeMethods.FdiNotificationType.CopyFile:
                return CopyFiles(fdin);
            case NativeMethods.FdiNotificationType.CloseFileInfo:
                return OutputFileClose(fdin);
            default:
                return IntPtr.Zero;
        }
    }

    private IntPtr IterateCallback(NativeMethods.FdiNotificationType fdint, NativeMethods.FdiNotification fdin)
    {
        switch (fdint)
        {
            case NativeMethods.FdiNotificationType.CopyFile:
                return OutputFileOpen(fdin);
            default:
                return IntPtr.Zero;
        }
    }

    private IntPtr InputFileOpen(string fileName, int oflag, int pmode)
    {
        var stream = new MemoryStream(_inputData);
        GCHandle gch = GCHandle.Alloc(stream);
        return (IntPtr)gch;
    }

    private int InputFileClose(IntPtr hf)
    {
        var stream = StreamFromHandle(hf);
        stream.Close();
        ((GCHandle)(hf)).Free();
        return 0;
    }
    /// <summary>
    /// Copies the contents of input to output. Doesn't close either stream.
    /// </summary>
    public static void CopyStream(Stream input, Stream output)
    {
        var buffer = new byte[8 * 1024];
        int len;
        while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            output.Write(buffer, 0, len);
        }
    }


    private IntPtr CopyFiles(NativeMethods.FdiNotification fdin)
    {
        var fileName = GetFileName(fdin);
        var extractFile = _currentFileToDecompress.Name == fileName ? _currentFileToDecompress : null;
        if (extractFile != null)
        {
            var stream = new MemoryStream();
            GCHandle gch = GCHandle.Alloc(stream);
            extractFile.Handle = (IntPtr)gch;
            return extractFile.Handle;
        }

        //Do not extract this file
        return IntPtr.Zero;
    }
    private IntPtr OutputFileOpen(NativeMethods.FdiNotification fdin)
    {
        try
        {
            var extractFile = new ArchiveFile { Name = GetFileName(fdin) };
            if (ShouldIgnoreFile(extractFile))
            {
                //ignore this file.
                return IntPtr.Zero;
            }
            var stream = new MemoryStream();
            GCHandle gch = GCHandle.Alloc(stream);
            extractFile.Handle = (IntPtr)gch;

            AddToListOfFiles(extractFile);


        }
        catch (Exception ex)
        {
           SbaLogger.Instance.Verbose(ex);
        }
        //return IntPtr.Zero so that the iteration will keep on going
        return IntPtr.Zero;

    }

    private bool ShouldIgnoreFile(ArchiveFile extractFile)
    {
        var rootFolder = GetFileRootFolder(extractFile.Name);
        return _subDirectoryToIgnore.Any(dir => dir.Equals(rootFolder, StringComparison.InvariantCultureIgnoreCase));
    }

    private string GetFileRootFolder(string path)
    {
        try
        {
            return path.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
        }
        catch (Exception)
        {

            return string.Empty;
        }

    }

    private void AddToListOfFiles(ArchiveFile extractFile)
    {
        if (!_fileNames.Any(file => file.Equals(extractFile.Name)))
        {
            _fileNames.Add(extractFile.Name);
        }
    }

    private IntPtr OutputFileClose(NativeMethods.FdiNotification fdin)
    {
        var extractFile = _currentFileToDecompress.Handle == fdin.hf ? _currentFileToDecompress : null;
        var stream = StreamFromHandle(fdin.hf);

        if (extractFile != null)
        {
            extractFile.Found = true;
            extractFile.Length = (int)stream.Length;

            if (stream.Length > 0)
            {
                extractFile.Data = new byte[stream.Length];
                stream.Position = 0;
                stream.Read(extractFile.Data, 0, (int)stream.Length);
            }
        }

        stream.Close();
        return IntPtr.Zero;
    }

    private static IntPtr FdiCreate(
    NativeMethods.FdiMemAllocDelegate fnMemAlloc,
    NativeMethods.FdiMemFreeDelegate fnMemFree,
    NativeMethods.FdiFileOpenDelegate fnFileOpen,
    NativeMethods.FdiFileReadDelegate fnFileRead,
    NativeMethods.FdiFileWriteDelegate fnFileWrite,
    NativeMethods.FdiFileCloseDelegate fnFileClose,
    NativeMethods.FdiFileSeekDelegate fnFileSeek,
    NativeMethods.CabError erf)
    {
        return NativeMethods.FdiCreate(fnMemAlloc, fnMemFree, fnFileOpen, fnFileRead, fnFileWrite,
                         fnFileClose, fnFileSeek, CpuTypeUnknown, erf);
    }

    private static int FileRead(IntPtr hf, byte[] buffer, int cb)
    {
        var stream = StreamFromHandle(hf);
        return stream.Read(buffer, 0, cb);
    }

    private static int FileWrite(IntPtr hf, byte[] buffer, int cb)
    {
        var stream = StreamFromHandle(hf);
        stream.Write(buffer, 0, cb);
        return cb;
    }

    private static Stream StreamFromHandle(IntPtr hf)
    {
        return (Stream)((GCHandle)hf).Target;
    }

    private IntPtr MemAlloc(int cb)
    {
        return Marshal.AllocHGlobal(cb);
    }

    private void MemFree(IntPtr mem)
    {
        Marshal.FreeHGlobal(mem);
    }

    private int FileSeek(IntPtr hf, int dist, int seektype)
    {
        var stream = StreamFromHandle(hf);
        return (int)stream.Seek(dist, (SeekOrigin)seektype);
    }

    private bool FdiCopy()
    {
        try
        {
            return NativeMethods.FdiCopy(FdiContext, "<notused>", "<notused>", 0, ExtractCallback, IntPtr.Zero, IntPtr.Zero);
        }
        catch (Exception)
        {

            return false;
        }

    }

    private bool FdiIterate()
    {
        return NativeMethods.FdiCopy(FdiContext, "<notused>", "<notused>", 0, IterateCallback, IntPtr.Zero, IntPtr.Zero);
    }



    private IntPtr FdiContext { get; set; }

    public void Dispose()
    {
        Dispose(true);
    }
    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (!_disposed)
            {
                if (FdiContext != IntPtr.Zero)
                {
                    NativeMethods.FdiDestroy(FdiContext);
                    FdiContext = IntPtr.Zero;
                }
                _disposed = true;
            }
        }
    }
}

这篇关于提取在C#中的.cab文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-15 00:03