Click here to Skip to main content
Email Password   helpLost your password?

About 7-Zip

7-Zip is an open-source archive program with plug-in interface. New archive formats and/or archive codecs can be added by DLLs. 7-Zip ships with several archive formats preinstalled:

The project is written in C++ language.

You can find more on the official 7-Zip site — here.

About this Contribution

This contribution allows you to use 7-zip archive format DLLs in your programs written in .NET languages.

I created this module for my own project to have the ability to work with archives. Currently my project only has extract capabilities, so only this part of 7-Zip interface translated to C#. Later I plan to translate compress capability as well. For now if you need such functionality you can implement it by yourself, with this code, and the 7-Zip source code.

This translation is tested and already working in my own project.

Implementation Details

All communication with archive DLLs is done with COM-like interfaces (why COM-like, and not COM can be see in the known issues section). Callbacks are also implemented as interfaces.

Every DLL contains a class that can implement one or more interface. Some formats allows only extracting, some also provide compress abilities. Public interfaces translated to C#:

Every DLL export function is for creating archive class handlers and functions in order to get archive format properties. These functions are translated as .NET delegates:

Update (1.3): In 7-Zip 4.45 there are some changes in the DLL interface. Now all archive formats and compression codecs are implemented as one big DLL. So several new exported functions (and delegates for these functions in translation) are added to handle several archive handler classes in one DLL.

Points of Interest

7-Zip interfaces uses variants (PropVariant) for property values. C# does not support such variants as classes and all such parameters are implemented in C# as IntPtr. This is done for compatibility and because I prefer not to use unsafe code in my projects.

Fortunately a managed class System.Runtime.InteropServices.Marshal has a method GetObjectForNativeVariant that you can use for converting such "pointers" to objects. However this method does not handle all PropVariant types (for example VT_FILETIME), so for these cases I added my GetObjectForNativeVariant method to this translation.

7-Zip works with files through its own interfaces, so if you want to open file on disk, or in memory you need to provide class implement one or more necessary interfaces. Several such wrapper classes are also present in this translation (they are wrap around standard .NET Stream class).

Update (1.2): Most of the complexity related to PropVariant processing is now hidden in special PropVariant structure. And interface methods now return PropVariant instead of IntPtr.

Known Issues

The first and most disappointing issue is that you cannot use 7-Zip DLLs directly. This means that you cannot simply take such DLLs from 7-Zip distribution and use them in your projects. This is because of the incomplete COM interfaces implementations in 7-Zip code. All issues are related to IUnknown.QueryInterface implementation. 7-Zip's QueryInterface does not return IUnknown interface if prompted (this part is most critical for working with COM-interfaces in .NET), and some classes do not return any interface at all!

This is done because 7-Zip code is C++ code and works with pointers, and most functions returns direct pointers to interface implementation. That means that 7-Zip code not use QueryInterface at all. Sad, but .NET works in a different way, and the first access to any interface always goes though QueryInterface and IUnknown.

So if we use DLLs directly we have constant InvalidCastException. So we need to make several changes in 7-Zip code and rebuild DLLs. Or ask Igor Pavlov to include such changes to the 7-Zip code itself :)

Important Update: Starting from 7-Zip 4.46 alpha Igor did necessary changes in the code. So, from this version forward, you can use format DLLs directly, without applying any patch. Superb!

The second issue is much smaller one. It is related to multi-threading. If you plan to use 7-Zip interfaces only in one stream you have no problem. The problem comes when you try to use one interface in several threads. In this case all threads except the main one (threads where interfaces are created) throw exceptions on any interface method calls. This is because of RCW behavior. RCW is an object that wraps COM-interface in .NET. When you try to use interface in different thread RCW tries to marshal interface and fails (because this implementation does not support ITypeInfo).

Fortunately I've found simple solutions for this. Main interface (IInArchive) returns as IntPtr, and not as RCW object. When you need to access this interface, call System.Runtime.InteropServices.Marshal.GetTypedObjectForIUnknown or any other related method and get RCW object. If you need to use this interface in another thread simple call System.Runtime.InteropServices.Marshal.FinalReleaseComObject (or ReleaseComObject), and create another RCW wrapper around returned IntPtr pointer. Of course in this case you can use interface only in one thread in time, but this is better than using interface only in one thread. And any logic can be easily implemented with correct thread locking.

And the third issue is a well known issue but I think it must be noted here. It appears that .NET runtime does not support COM interfaces inheritance (interfaces marked with the ComImport attribute). This is definitely a .NET bug, but I don't know when Microsoft fixes this bug, or fix if they fix it at all.

There is simple solution to avoid this bug. Inherited interface must be declared as standalone one and first methods must be methods of inherited interfaces in the order of appearance. You can see sample of such "inheritance" in this translation source.

Demo

Due to many requests, I have spend some time and written a little demo program. The demo program lacks proper error checking, lacks different archives support (zip format is hardcoded in source, but can be easily changed), it lacks almost everything, but it has two advantages: it's simple, and it works.

The demo has only two modes, the first to list all files in archive, and the second is to extract a single file from the archive. I think that this is enough to understand how to use 7-zip interfaces and how to create something more complex.

If you want to run demo, don't forget to put 7z.dll (can be found on official 7-zip site) to the executable folder with executable.

Version History

1.5 - Small demo added
1.3 - Added two new delegate for features added in 7-Zip 4.45
1.2 - Variant type changed from IntPtr to newly created PropVariant structure
1.1 - Stream wrappers added, minor interface translation changes for better usability
1.0 - initial release

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralHow Inherits...Implements...in VB
he_young
21:28 23 Jan '10  
hi,..
im doing some task that convert c#.net into vb.net code ...so there having some method..like...

..........................................................

public class InStreamWrapper : StreamWrapper, ISequentialInStream, IInStream
{
public InStreamWrapper(Stream baseStream) : base(baseStream) { }

public uint Read(byte[] data, uint size)
{
return (uint)BaseStream.Read(data, 0, (int)size);
}
}

public interface IInStream
{
uint Read([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data,uint size);

void Seek(long offset,uint seekOrigin,IntPtr newPosition); // ref long newPosition
}

public interface ISequentialInStream
{
uint Read(
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data,
uint size);
}

public class StreamWrapper : IDisposable
{
protected Stream BaseStream;
protected StreamWrapper(Stream baseStream)
{BaseStream = baseStream; }

public void Dispose()
{ BaseStream.Close(); }

public virtual void Seek(long offset, uint seekOrigin, IntPtr newPosition)
{
long Position = (uint)BaseStream.Seek(offset, (SeekOrigin)seekOrigin);
if (newPosition != IntPtr.Zero)
Marshal.WriteInt64(newPosition, Position);
}
}
..........................................................
convert into
..........................................................

Public Class InStreamWrapper
Inherits StreamWrapper
Implements ISequentialInStream
Implements IInStream

Public Sub New(ByVal baseStream As Stream)
MyBase.New(baseStream)
End Sub

Public Function Read(ByVal data As Byte(), ByVal size As UInteger) As UInteger Implements ISequentialInStream.Read, IInStream.Read
Return CUInt(BaseStream.Read(data, 0, CInt(size)))
End Function

Public Overridable Sub Seek(ByVal offset As Long, ByVal seekOrigin As UInteger, ByVal newPosition As IntPtr) Implements IInStream.Seek
Dim Position As Long
'Position = CUInt(BaseStream.Seek(offset, DirectCast(seekOrigin, SeekOrigin)))
Position = CUInt(BaseStream.Seek(offset, seekOrigin))
If newPosition <> IntPtr.Zero Then
Marshal.WriteInt64(newPosition, Position)
End If
End Sub
End Class

<ComImport(), Guid("23170F69-40C1-278A-0000-000300030000"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IInStream
'Inherits ISequentialInStream
' <PreserveSig()> _
' Function Read(<Out(), MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal data As Byte(), ByVal size As UInteger, ByVal processedSize As IntPtr) As Integer

Function Read(<Out(), MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal data As Byte(), ByVal size As UInteger) As UInteger

<PreserveSig()> _
Sub Seek(ByVal offset As Long, ByVal seekOrigin As UInteger, ByVal newPosition As IntPtr)
' ref long newPosition
End Interface

<ComImport(), Guid("23170F69-40C1-278A-0000-000300010000"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface ISequentialInStream
' <PreserveSig()> _
' Function Read(<Out(), MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal data As Byte(), ByVal size As UInteger, ByVal processedSize As IntPtr) As Integer

Function Read(<Out(), MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal data As Byte(), ByVal size As UInteger) As UInteger

End Interface

Public Class StreamWrapper
Implements IDisposable

Protected BaseStream As Stream

Protected Sub New(ByVal baseStream__1 As Stream)
BaseStream = baseStream__1
End Sub

Public Sub Dispose() Implements IDisposable.Dispose
BaseStream.Close()
End Sub

Public Overridable Sub Seek(ByVal offset As Long, ByVal seekOrigin As UInteger, ByVal newPosition As IntPtr)
Dim Position As Long
'Position = CUInt(BaseStream.Seek(offset, DirectCast(seekOrigin, SeekOrigin)))
Position = CUInt(BaseStream.Seek(offset, seekOrigin))
If newPosition <> IntPtr.Zero Then
Marshal.WriteInt64(newPosition, Position)
End If
End Sub
End Class

..........................................................

but it does not work. can you help me convert C# code into vb code ?
thanks in advance....
NewsSharp IMG Viewer 2008 - alternative 100% managed solution
MikeGratsas
23:24 23 Sep '09  
Please read information about available product features and download the trial version from Sharp IMG Viewer 2008 page.
GeneralWhere's the Demo??
nybelle
5:36 31 Aug '09  
Did you remove the demo from the zip file? I can't find it anywhere.
Thanks!
GeneralCan't make it work on TGZ
pym
23:01 2 Jun '09  
Hello,

First, I'd like to say thanks, I work with this interface for extracting ZIP and Z files and it works great.

The thing is, I now have to support TGZ files as well, but can't figure out the way to do it.
I use GZIP as the archive zip format (tried all others as well), but no luck...

Anyone tried it?

Thanks.
GeneralRe: Can't make it work on TGZ
markhor
12:12 3 Jun '09  
Do you mean any specific errors? Note that TGZ is a double archive - first data is packed with tar, second with GZIP, which means to extract GZIP and then Tar.
GeneralRe: Can't make it work on TGZ
pym
20:47 3 Jun '09  
Hi, thanks for your reply.
I know about the GZIP and TAR.
I used your sample application, changed this line:
IInArchive Archive = Format.CreateInArchive(SevenZipFormat.GetClassIdFromKnownFormat(KnownSevenZipFormat.GZip));

and used this cmd: "e C:\Temp\Zipped\tgz1.tgz 1"

No errors really, just extraction produces nothing.
FileName argument is set to null after these lines:
PropVariant Name = new PropVariant();
Archive.GetProperty(FileNumber, ItemPropId.kpidPath, ref Name);
string FileName = (string)Name.GetObject();

AnswerRe: Can't make it work on TGZ
Eugene Sichkar
0:41 4 Jun '09  
You can also try ItemPropId.kpidName to get name. But in simple archive formats like bzip, z and partially gzip, file name is not stored inside archive, so you must reconstruct stored name from the archive name itself (in your case tgz1.tgz => tgz1.tar).

Liberavi animam meam!

GeneralRe: Can't make it work on TGZ
pym
20:47 6 Jun '09  
Thank you for your replies.

Still, no luck.
Is there a chance anyone posts a sample code for extracting a simple TGZ file?

Thank you.
GeneralRe: Can't make it work on TGZ
markhor
22:10 20 Jul '09  
Well,


using (SevenZipExtractor extr = new SevenZipExtractor("your tgz archive path"))
{
extr.ExtractFiles("tar path", 0);
}
using (SevenZipExtractor extr = new SevenZipExtractor("tar path"))
{
extr.ExtractArchive("directory");
}


You must have SevenZipSharp though Big Grin
GeneralPlease check http://www.codeplex.com/sevenzipsharp
markhor
9:43 21 Feb '09  
I would be glad to your test results and hints.
GeneralPassword protected 7z archive [modified]
one_eddie
8:23 18 Feb '09  
Hi,

Could someone give me a hint or provide a sample code showing how a password protected archive can be extracted using this library?

Thanks

-- edit --

I found the solution. ICrypto... interface need to be implemented inside ArchiveExtractCallback class. I was doing it exactly the same and it didn't work. Finally found I got another bug which prevented me to decompress.

modified on Wednesday, February 18, 2009 2:18 PM

GeneralISetProperties - "Store" files in archive, and encrypt them
thunder7553
22:42 16 Feb '09  
Hello!

First, thanks for that piece of work, it was very helpful! I am using the 2.0 version from the nomad homepage.

I understand basic extracting and creation of archive, that works wonderful. But now I am trying to do two things, which i just do not know how to do correctly:

1.) "Store" files in archive, so that they are not compressed, while creating a ZIP Archive

2.) Encrypt the files with a password

As far as i understand, both things are possible using the ISetProperties. I looked into the 7zip source code, and as far as i could understand, there input of this is an array of names, and array of propvalues, and a count.

The current ISetProperties takes as second parameter an IntPtr - and i do not know how to convert that to an array. I tried to change it to an array of AsAny, which does not work either (Exception when you call the function), and then i tried an Array of I4. This would call, but do nothing.

My call was something like this:
(outArchive as ISetProperties).SetProperties(new string[]{"x"}, new int[]{0},1);

But that does not change anything. Maybe I misunderstood the SetProperties as well. The 7z.exe call i wanted to simulate was "7za a -tZIP -mx=0 test.zip directorywithfiles".

Has someone done this already and could give me a hint?

Markus
GeneralRe: ISetProperties - "Store" files in archive, and encrypt them
Eugene Sichkar
23:28 16 Feb '09  
Second parameter in ISetProperties.SetProperties is PropVariant array. You can use the following snippet to call it:

// string[] Names
// PropVariant[] Values

GCHandle ValuesHandle = GCHandle.Alloc(Values, GCHandleType.Pinned);
try
{
setProperties.SetProperties(Names, ValuesHandle.AddrOfPinnedObject(), Count);
}
finally
{
ValuesHandle.Free();
}


And don't forget that 7z.dll is very sensitive to variant type. And different format handlers have different set of available parameters.

For zip:
- Change compression method: m=Copy (Deflate, Deflate64, etc)
- Change encryption method: em=ZipCrypto (AES256)

Liberavi animam meam!

QuestionError: Insufficient memory to continue the execution of the program.
ThisIsARandomNameArgh
5:50 10 Feb '09  
I made a sample *.7z archive (with LZMA compression) using the installed 7Zip tools, then I built your 7Zip interface library and copied 7z.dll from the installation folder into the project Debug output directory and ran the program.

I get these results:

C:\Documents and Settings\David\Desktop\Compression\7zIntf15\bin\Debug>SevenZip.exe e Files.7z 1
SevenZip
SevenZip l {ArchiveName}
SevenZip e {ArchiveName} {FileNumber}
Archive: Files.7z
Error: Insufficient memory to continue the execution of the program.

C:\Documents and Settings\David\Desktop\Compression\7zIntf15\bin\Debug>


Any idea?
AnswerRe: Error: Insufficient memory to continue the execution of the program.
ThisIsARandomNameArgh
5:52 10 Feb '09  
Oh, silly me. I didn't change the line from this:

IInArchive Archive = Format.CreateInArchive(SevenZipFormat.GetClassIdFromKnownFormat(KnownSevenZipFormat.Zip));


to this:

IInArchive Archive = Format.CreateInArchive(SevenZipFormat.GetClassIdFromKnownFormat(KnownSevenZipFormat.SevenZip));

Questionproblem...
ghiboz
23:05 1 Feb '09  
hi, how can I extract all the files in the 7zip file??
i tried to use the sample, but extract only the "0" file, I checekd a compressed file with 4 files, if I read the list, (using the l parameter in the sample ) reads correctly the 4 files, but when I extract, if I use 0 as filenumber extracts correctly the 0 file, but if I use 1 or any other filenumber ( in my case 0, 1, 2, 3 ) nothing will be extracted... the function archive.extract( ... returns 0 if is ok, and a negative number if fails...
does anybody help me?

thanks in advance!
AnswerRe: problem...
thunder7553
22:46 16 Feb '09  
I am using the version 2.0 from the authors homepage, but i did not experience that problem. I am using a "for" loop to extract all files, and this worked without problems. Could it be that you have an error in the filename you are extracting to? the source code in the sample did not include the folder to extract to, so the files would go the the programs current directory, whatever that is.
GeneralWhat is filenumber in this code?
sobanbabu1
5:42 28 Jan '09  
What value do I need to pass for 3rd argument?
GeneralRe: What is filenumber in this code?
xashlocke
9:04 28 Jan '09  
It's the number of the file you wish to extract.

If you want to extract all, use 0xFFFFFFFF
GeneralRecognizing file types for input archives
Viram
3:04 20 Jan '09  
Hi,

I am using c# wrapper of 7Zip.dll.
I want to recognize the file types of the provided archive as an input. Suppose if i wish to extract a zip file, i want to identify it through code that its type is zip. How could i do that in code.

Thanks.

Viram Pandey

GeneralRe: Recognizing file types for input archives
xashlocke
8:56 27 Jan '09  
You could do something like this:


private KnownSevenZipFormat GetArchiveFormat(string archivePath)
{
FileInfo archFile = new FileInfo(archivePath);
switch(archFile.Extension.ToUpper().Remove(archFile.Extension.IndexOf('.'), 1))
{
case "RAR":
return KnownSevenZipFormat.Rar;
case "ZIP":
return KnownSevenZipFormat.Zip;
case "7Z":
return KnownSevenZipFormat.SevenZip;
case "LZH":
return KnownSevenZipFormat.Lzh;
case "Z":
return KnownSevenZipFormat.Z;
case "LZMA":
return KnownSevenZipFormat.Lzma;
case "CBR":
return KnownSevenZipFormat.Rar;
case "CBZ":
return KnownSevenZipFormat.Zip;
default:
return KnownSevenZipFormat.Zip;
}
}

GeneralSpecifying Destination folder for extracted files
Viram
3:03 20 Jan '09  
Hi,

I have one more problem in using c# wrapper of 7Zip.dll.
I need to specify my custom destination folder for the extracted files of archive.
Currently it decompresses all the files in the \bin\debug\ folder and i am not able to specify its custom destination path.

Thanks

Viram Pandey

GeneralRe: Specifying Destination folder for extracted files
sprice86
4:25 7 Feb '09  
Hi

I have the same problem as Viram regarding specifying the destination directory for unpacking operations(I don't know how to do it).

I wondered if any kind soul knows the answer?

Confused

Bertram

Generalhow to use the interface
linuxsuperfans
4:21 28 Dec '08  
I went through the source code, but I still have no idea how to use it in my project(sorry, because I am a C# beginer). can somebody tell me how to use it? e.g. in the winform, I just need to specify a zip filename(.zip or .rar) and click a button, then the file can be decompression.
QuestionMulit Volume archives
NullAcht_15
7:54 22 Dec '08  
Hi

Does anyone have an example how to handle MultiVolume archives on extraction?

Thx for this great piece of code Smile


Last Updated 20 Jun 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010