4

An LZH archive is embedded within a file. The file was read into a byte[], and the LZH part is identified as a smaller byte[].

How can the embedded LZH bytes be decompressed into another byte[] using .NET Framework 4.6 (C#)? I have only see http://www.infoq.com/news/2008/06/7-Zip-from-.NET which doesn't exactly do what I need.

Thanks.

Snowy
  • 5,942
  • 19
  • 65
  • 119
  • I assume you've already looked at system.io.compression and that's not able to read bytes in LZH format? I dont have a byte array to play with or I'd try it before asking ;) – bri Jan 12 '16 at 20:57
  • The compression libraries in .NET do not have LZH support. I am looking for a managed code solution that I can run safely in a hosted web environment for a special application. – Snowy Jan 13 '16 at 19:52
  • Yeah. I figured system.io.compression was too obvious ;-). Never hurts to ask I suppose. – bri Jan 13 '16 at 20:18

1 Answers1

1

the code snippet that follows is taken from the sample program from this article http://www.codeproject.com/Articles/27148/C-NET-Interface-for-Zip-Archive-DLLs

There are no significant changes: instead of reading and writing files, it reads and write byte arrays. Changes are marked by comments Run with sevenzip.exe e "C:\temp\gwo0.11-sample-win32.lzh" 3 for example

https://dl.dropboxusercontent.com/u/71459360/7z.zip

using System;
using System.Collections.Generic;
using System.Text;
using Nomad.Archive.SevenZip;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;

namespace SevenZip
{
    class Program
    {
        private static void ShowHelp()
        {
            Console.WriteLine("SevenZip");
            Console.WriteLine("SevenZip l {ArchiveName}");
            Console.WriteLine("SevenZip e {ArchiveName} {FileNumber}");
        }

        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                ShowHelp();
                return;
            }

            try
            {
                string ArchiveName;
                uint FileNumber = 0xFFFFFFFF;
                bool Extract;

                switch (args[0])
                {
                    case "l":
                        ArchiveName = args[1];
                        Extract = false;
                        break;
                    case "e":
                        ArchiveName = args[1];
                        Extract = true;
                        if ((args.Length < 3) || !uint.TryParse(args[2], out FileNumber))
                        {
                            ShowHelp();
                            return;
                        }
                        break;
                    default:
                        ShowHelp();
                        return;
                }

                using (SevenZipFormat Format = new SevenZipFormat(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "7z.dll")))
                {
                    IInArchive Archive = Format.CreateInArchive(SevenZipFormat.GetClassIdFromKnownFormat(KnownSevenZipFormat.Lzh));
                    if (Archive == null)
                    {
                        ShowHelp();
                        return;
                    }

                    try
                    {
                        //the byte array is provided by you. here it's coming from a file
                        byte[] data;
                        using (var stream = File.OpenRead(ArchiveName))
                        {
                            data = new byte[stream.Length];
                            stream.Read(data, 0, data.Length);
                        }

                        using (InStreamWrapper ArchiveStream = new InStreamWrapper(new MemoryStream(data))) //modified here
                        {
                            ulong CheckPos = 32 * 1024;
                            if (Archive.Open(ArchiveStream, ref CheckPos, null) != 0)
                                ShowHelp();

                            Console.Write("Archive: ");
                            Console.WriteLine(ArchiveName);

                            if (Extract)
                            {
                                PropVariant Name = new PropVariant();
                                Archive.GetProperty(FileNumber, ItemPropId.kpidPath, ref Name);
                                string FileName = (string) Name.GetObject();

                                Console.Write("Extracting: ");
                                Console.Write(FileName);
                                Console.Write(' ');

                                MemoryStream ms = new MemoryStream();
                                Archive.Extract(new uint[] { FileNumber }, 1, 0, new ArchiveMemoryCallback(FileNumber, ms)); //modified here
                                byte[] output = ms.ToArray(); //here you have the output byte array
                                output.ToString();
                            }
                            else
                            {
                                Console.WriteLine("List:");
                                uint Count = Archive.GetNumberOfItems();
                                for (uint I = 0; I < Count; I++)
                                {
                                    PropVariant Name = new PropVariant();
                                    Archive.GetProperty(I, ItemPropId.kpidPath, ref Name);
                                    Console.Write(I);
                                    Console.Write(' ');
                                    Console.WriteLine(Name.GetObject());
                                }
                            }
                        }
                    }
                    finally
                    {
                        Marshal.ReleaseComObject(Archive);
                    }
                }
            }
            catch (Exception e)
            {
                Console.Write("Error: ");
                Console.WriteLine(e.Message);
            }
        }
    }

    class ArchiveCallback : IArchiveExtractCallback
    {
        private uint FileNumber;
        private string FileName;
        private OutStreamWrapper FileStream;

        public ArchiveCallback(uint fileNumber, string fileName)
        {
            this.FileNumber = fileNumber;
            this.FileName = fileName;
        }

        #region IArchiveExtractCallback Members

        public void SetTotal(ulong total)
        {
        }

        public void SetCompleted(ref ulong completeValue)
        {
        }

        public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode)
        {
            if ((index == FileNumber) && (askExtractMode == AskMode.kExtract))
            {
                string FileDir = Path.GetDirectoryName(FileName);
                if (!string.IsNullOrEmpty(FileDir))
                    Directory.CreateDirectory(FileDir);
                FileStream = new OutStreamWrapper(File.Create(FileName));

                outStream = FileStream;
            }
            else
                outStream = null;

            return 0;
        }

        public void PrepareOperation(AskMode askExtractMode)
        {
        }

        public void SetOperationResult(OperationResult resultEOperationResult)
        {
            FileStream.Dispose();
            Console.WriteLine(resultEOperationResult);
        }

        #endregion
    }


    //new
    class ArchiveMemoryCallback : IArchiveExtractCallback
    {
        private uint FileNumber;
        private Stream stream;
        private OutStreamWrapper FileStream;

        public ArchiveMemoryCallback(uint fileNumber, Stream stream)
        {
            this.FileNumber = fileNumber;
            this.stream = stream;
        }

        #region IArchiveExtractCallback Members

        public void SetTotal(ulong total)
        {
        }

        public void SetCompleted(ref ulong completeValue)
        {
        }

        public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode)
        {
            if ((index == FileNumber) && (askExtractMode == AskMode.kExtract))
            {
                FileStream = new OutStreamWrapper(stream);

                outStream = FileStream;
            }
            else
                outStream = null;

            return 0;
        }

        public void PrepareOperation(AskMode askExtractMode)
        {
        }

        public void SetOperationResult(OperationResult resultEOperationResult)
        {
            FileStream.Dispose();
            Console.WriteLine(resultEOperationResult);
        }

        #endregion
    }
}
Claudio B
  • 564
  • 5
  • 12
  • The Marshal to release a COM latch on a 3rd party DLL makes this unusable for my purpose, sorry. I am looking for an all-in-memory solution that runs within managed .NET. – Snowy Jan 13 '16 at 19:56
  • Looks like you are out of luck: LZH isn't such a popular compression format, especially on windows (and so on .net). I've never found an already made fully managed library that could handle it. May I ask why is interop out of the picture? – Claudio B Jan 13 '16 at 22:45
  • Interop is out of the picture because I need to run in a hosted environment where everything has to be in the memory space of the web application. – Snowy Jan 15 '16 at 18:07