How to Use 7z.dll Directly in C# .NET

This guide explains how to integrate and call the native 7z.dll dynamic link library directly within C# .NET applications. Rather than launching 7z.exe as an external command-line process, this article covers how to interact with the library's native COM-like architecture using Platform Invoke (P/Invoke), define the required COM interfaces, and extract archives directly through unmanaged function pointers.


Understanding the 7z.dll Interface

The 7z.dll binary included with 7-Zip does not export a large collection of standard C functions. Instead, it behaves similarly to a COM component, exporting only a few entry points, primarily:

  • CreateObject(const GUID *clsid, const GUID *iid, void **outObject)
  • GetHandlerProperty(PROPID propID, PROPVariant *value)
  • GetNumberOfFormats(unsigned int *numFormats)

To decompress or compress an archive directly, you must load 7z.dll, invoke CreateObject, instantiate an archive handler, and supply managed implementations of callbacks (such as stream reading and progress tracking) that conform to 7-Zip's unmanaged interface contracts.


Step 1: Declare the Native API and CLSIDs

The first step is importing CreateObject from 7z.dll via P/Invoke and defining the appropriate Class IDs (CLSIDs) and Interface IDs (IIDs).

using System;
using System.Runtime.InteropServices;

namespace SevenZipDirect
{
    public static class NativeMethods
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr LoadLibrary(string libname);

        [DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate int CreateObjectDelegate(
            [In] ref Guid clsid,
            [In] ref Guid iid,
            [MarshalAs(UnmanagedType.Interface)] out object outObject);

        // CLSIDs for archive format handlers
        public static readonly Guid CLSID_Format7z = new Guid("23170F69-40C1-278A-1000-000110070000");
        public static readonly Guid CLSID_FormatZip = new Guid("23170F69-40C1-278A-1000-000110010000");

        // IIDs for archive interfaces
        public static readonly Guid IID_IInArchive = new Guid("23170F69-40C1-278A-0000-000600600000");
    }
}

Step 2: Define Core COM Interfaces

7-Zip requires client code to implement and consume interfaces that mirror its internal C++ definitions. The most critical interface for reading archives is IInArchive.

using System;
using System.Runtime.InteropServices;

namespace SevenZipDirect
{
    [ComImport]
    [Guid("23170F69-40C1-278A-0000-000600600000")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IInArchive
    {
        [PreserveSig]
        int Open(
            IInStream stream, 
            [In] ref ulong maxCheckStartPosition, 
            IArchiveOpenCallback openArchiveCallback);

        [PreserveSig]
        int Close();

        [PreserveSig]
        int GetNumberOfItems(out uint numItems);

        [PreserveSig]
        int GetProperty(uint index, uint propID, ref PropVariant value);

        [PreserveSig]
        int Extract(
            [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] uint[] indices,
            uint numItems,
            int testMode,
            IArchiveExtractCallback extractCallback);

        [PreserveSig]
        int GetArchiveProperty(uint propID, ref PropVariant value);

        [PreserveSig]
        int GetNumberOfProperties(out uint numProperties);

        [PreserveSig]
        int GetPropertyInfo(uint index, [MarshalAs(UnmanagedType.BStr)] out string name, out uint propID, out ushort varType);

        [PreserveSig]
        int GetNumberOfArchiveProperties(out uint numProperties);

        [PreserveSig]
        int GetArchivePropertyInfo(uint index, [MarshalAs(UnmanagedType.BStr)] out string name, out uint propID, out ushort varType);
    }

    [StructLayout(LayoutKind.Explicit, Size = 16)]
    public struct PropVariant
    {
        [FieldOffset(0)] public ushort vt;
        [FieldOffset(8)] public IntPtr ptrVal;
        [FieldOffset(8)] public ulong uhVal;
    }
}

You also need basic stream interfaces to feed byte streams into 7-Zip:

[ComImport]
[Guid("23170F69-40C1-278A-0000-000300030000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInStream
{
    [PreserveSig]
    int Read([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, uint size, out uint processedSize);

    [PreserveSig]
    int Seek(long offset, uint seekOrigin, out ulong newPosition);
}

[ComImport]
[Guid("23170F69-40C1-278A-0000-000600100000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IArchiveOpenCallback
{
    [PreserveSig]
    int SetTotal(ulong files, ulong bytes);

    [PreserveSig]
    int SetCompleted(ulong files, ulong bytes);
}

[ComImport]
[Guid("23170F69-40C1-278A-0000-000600200000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IArchiveExtractCallback
{
    [PreserveSig]
    int GetStream(uint index, out ISequentialOutStream outStream, int askExtractMode);

    [PreserveSig]
    int PrepareOperation(int askExtractMode);

    [PreserveSig]
    int SetOperationResult(int resultEOperationResult);

    [PreserveSig]
    int SetTotal(ulong total);

    [PreserveSig]
    int SetCompleted(ulong completeValue);
}

[ComImport]
[Guid("23170F69-40C1-278A-0000-000300020000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISequentialOutStream
{
    [PreserveSig]
    int Write([In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, uint size, out uint processedSize);
}

Step 3: Implement Managed Stream Wrappers

7-Zip requires instances of IInStream and ISequentialOutStream. You must provide managed classes that wrap standard .NET System.IO.Stream objects:

using System;
using System.IO;

namespace SevenZipDirect
{
    public class InStreamWrapper : IInStream
    {
        private readonly Stream _baseStream;

        public InStreamWrapper(Stream baseStream)
        {
            _baseStream = baseStream;
        }

        public int Read(byte[] data, uint size, out uint processedSize)
        {
            processedSize = (uint)_baseStream.Read(data, 0, (int)size);
            return 0; // S_OK
        }

        public int Seek(long offset, uint seekOrigin, out ulong newPosition)
        {
            newPosition = (ulong)_baseStream.Seek(offset, (SeekOrigin)seekOrigin);
            return 0; // S_OK
        }
    }

    public class OutStreamWrapper : ISequentialOutStream
    {
        private readonly Stream _baseStream;

        public OutStreamWrapper(Stream baseStream)
        {
            _baseStream = baseStream;
        }

        public int Write(byte[] data, uint size, out uint processedSize)
        {
            _baseStream.Write(data, 0, (int)size);
            processedSize = size;
            return 0; // S_OK
        }
    }
}

Step 4: Loading and Invoking 7z.dll

With the interfaces and wrappers in place, load the DLL and call CreateObject dynamically:

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace SevenZipDirect
{
    public class SevenZipArchiveReader
    {
        public static IInArchive CreateArchiveReader(string dllPath, Guid formatGuid)
        {
            IntPtr libHandle = NativeMethods.LoadLibrary(dllPath);
            if (libHandle == IntPtr.Zero)
            {
                throw new FileNotFoundException("Unable to load native 7z.dll.", dllPath);
            }

            IntPtr procAddress = NativeMethods.GetProcAddress(libHandle, "CreateObject");
            if (procAddress == IntPtr.Zero)
            {
                throw new EntryPointNotFoundException("Failed to find CreateObject export in 7z.dll.");
            }

            var createObject = Marshal.GetDelegateForFunctionPointer<NativeMethods.CreateObjectDelegate>(procAddress);

            Guid interfaceId = NativeMethods.IID_IInArchive;
            int result = createObject(ref formatGuid, ref interfaceId, out object archiveObj);

            if (result != 0 || archiveObj == null)
            {
                throw new COMException("Failed to instantiate the archive handler.", result);
            }

            return (IInArchive)archiveObj;
        }
    }
}

Step 5: Reading an Archive

Use the initialized IInArchive instance to open an archive stream and inspect its item count:

using System;
using System.IO;

namespace SevenZipDirect
{
    class Program
    {
        static void Main()
        {
            string dllPath = @"C:\Program Files\7-Zip\7z.dll";
            string archivePath = @"C:\Path\To\archive.7z";

            IInArchive archive = SevenZipArchiveReader.CreateArchiveReader(dllPath, NativeMethods.CLSID_Format7z);

            using (FileStream fileStream = File.OpenRead(archivePath))
            {
                var managedStream = new InStreamWrapper(fileStream);
                ulong maxCheckPos = 1 << 23; // 8 MB

                int openResult = archive.Open(managedStream, ref maxCheckPos, null);
                if (openResult != 0)
                {
                    Console.WriteLine($"Failed to open archive. Error code: {openResult}");
                    return;
                }

                archive.GetNumberOfItems(out uint count);
                Console.WriteLine($"Total items in archive: {count}");

                archive.Close();
            }
        }
    }
}

Practical Considerations

  • Architecture Matching: The bitness of your .NET application (x86, x64, or ARM64) must match the architecture of 7z.dll. A mismatch will result in a BadImageFormatException or failure to load the module.
  • Extraction Callback: To extract files, implement IArchiveExtractCallback. In GetStream(uint index, out ISequentialOutStream outStream, int askExtractMode), instantiate a FileStream pointing to the target extraction path, wrap it in your OutStreamWrapper, and assign it to outStream.
  • Resource Cleanup: Always call IInArchive.Close() when done to ensure file handles and unmanaged memory allocated by 7z.dll are released properly.