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, orARM64) must match the architecture of7z.dll. A mismatch will result in aBadImageFormatExceptionor failure to load the module. - Extraction Callback: To extract files, implement
IArchiveExtractCallback. InGetStream(uint index, out ISequentialOutStream outStream, int askExtractMode), instantiate aFileStreampointing to the target extraction path, wrap it in yourOutStreamWrapper, and assign it tooutStream. - Resource Cleanup: Always call
IInArchive.Close()when done to ensure file handles and unmanaged memory allocated by7z.dllare released properly.