// Version 1.5 using System; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Threading; namespace SevenZipExtractor { [StructLayout(LayoutKind.Sequential)] internal struct PropArray { uint length; IntPtr pointerValues; } [StructLayout(LayoutKind.Explicit)] internal struct PropVariant { [DllImport("ole32.dll")] private static extern int PropVariantClear(ref PropVariant pvar); [FieldOffset(0)] public ushort vt; [FieldOffset(8)] public IntPtr pointerValue; [FieldOffset(8)] public byte byteValue; [FieldOffset(8)] public long longValue; [FieldOffset(8)] public System.Runtime.InteropServices.ComTypes.FILETIME filetime; [FieldOffset(8)] public PropArray propArray; public VarEnum VarType { get { return (VarEnum) this.vt; } } public void Clear() { switch (this.VarType) { case VarEnum.VT_EMPTY: break; case VarEnum.VT_NULL: case VarEnum.VT_I2: case VarEnum.VT_I4: case VarEnum.VT_R4: case VarEnum.VT_R8: case VarEnum.VT_CY: case VarEnum.VT_DATE: case VarEnum.VT_ERROR: case VarEnum.VT_BOOL: //case VarEnum.VT_DECIMAL: case VarEnum.VT_I1: case VarEnum.VT_UI1: case VarEnum.VT_UI2: case VarEnum.VT_UI4: case VarEnum.VT_I8: case VarEnum.VT_UI8: case VarEnum.VT_INT: case VarEnum.VT_UINT: case VarEnum.VT_HRESULT: case VarEnum.VT_FILETIME: this.vt = 0; break; default: PropVariantClear(ref this); break; } } public object GetObject() { switch (this.VarType) { case VarEnum.VT_EMPTY: return null; case VarEnum.VT_FILETIME: return DateTime.FromFileTime(this.longValue); default: GCHandle PropHandle = GCHandle.Alloc(this, GCHandleType.Pinned); try { return Marshal.GetObjectForNativeVariant(PropHandle.AddrOfPinnedObject()); } finally { PropHandle.Free(); } } } } [ComImport] [Guid("23170F69-40C1-278A-0000-000000050000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IProgress { void SetTotal(ulong total); void SetCompleted([In] ref ulong completeValue); } [ComImport] [Guid("23170F69-40C1-278A-0000-000600100000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IArchiveOpenCallback { // ref ulong replaced with IntPtr because handlers ofter pass null value // read actual value with Marshal.ReadInt64 void SetTotal( IntPtr files, // [In] ref ulong files, can use 'ulong* files' but it is unsafe IntPtr bytes); // [In] ref ulong bytes void SetCompleted( IntPtr files, // [In] ref ulong files IntPtr bytes); // [In] ref ulong bytes } [ComImport] [Guid("23170F69-40C1-278A-0000-000500100000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface ICryptoGetTextPassword { [PreserveSig] int CryptoGetTextPassword( [MarshalAs(UnmanagedType.BStr)] out string password); //[return : MarshalAs(UnmanagedType.BStr)] //string CryptoGetTextPassword(); } internal enum AskMode : int { kExtract = 0, kTest, kSkip } internal enum OperationResult : int { kOK = 0, kUnSupportedMethod, kDataError, kCRCError } [ComImport] [Guid("23170F69-40C1-278A-0000-000600300000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IArchiveOpenVolumeCallback { void GetProperty( ItemPropId propID, // PROPID IntPtr value); // PROPVARIANT [PreserveSig] int GetStream( [MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.Interface)] out IInStream inStream); } [ComImport] [Guid("23170F69-40C1-278A-0000-000600400000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IInArchiveGetStream { [return: MarshalAs(UnmanagedType.Interface)] ISequentialInStream GetStream(uint index); } [ComImport] [Guid("23170F69-40C1-278A-0000-000300010000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface ISequentialInStream { //[PreserveSig] //int Read( // [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, // uint size, // IntPtr processedSize); // ref uint processedSize uint Read( [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, uint size); /* Out: if size != 0, return_value = S_OK and (*processedSize == 0), then there are no more bytes in stream. if (size > 0) && there are bytes in stream, this function must read at least 1 byte. This function is allowed to read less than number of remaining bytes in stream. You must call Read function in loop, if you need exact amount of data */ } [ComImport] [Guid("23170F69-40C1-278A-0000-000300020000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface ISequentialOutStream { [PreserveSig] int Write( [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, uint size, IntPtr processedSize); // ref uint processedSize /* if (size > 0) this function must write at least 1 byte. This function is allowed to write less than "size". You must call Write function in loop, if you need to write exact amount of data */ } [ComImport] [Guid("23170F69-40C1-278A-0000-000300030000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IInStream //: ISequentialInStream { //[PreserveSig] //int Read( // [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, // uint size, // IntPtr processedSize); // ref uint processedSize uint Read( [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, uint size); //[PreserveSig] void Seek( long offset, uint seekOrigin, IntPtr newPosition); // ref long newPosition } [ComImport] [Guid("23170F69-40C1-278A-0000-000300040000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IOutStream //: ISequentialOutStream { [PreserveSig] int Write( [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, uint size, IntPtr processedSize); // ref uint processedSize //[PreserveSig] void Seek( long offset, uint seekOrigin, IntPtr newPosition); // ref long newPosition [PreserveSig] int SetSize(long newSize); } internal enum ItemPropId : uint { kpidNoProperty = 0, kpidHandlerItemIndex = 2, kpidPath, kpidName, kpidExtension, kpidIsFolder, kpidSize, kpidPackedSize, kpidAttributes, kpidCreationTime, kpidLastAccessTime, kpidLastWriteTime, kpidSolid, kpidCommented, kpidEncrypted, kpidSplitBefore, kpidSplitAfter, kpidDictionarySize, kpidCRC, kpidType, kpidIsAnti, kpidMethod, kpidHostOS, kpidFileSystem, kpidUser, kpidGroup, kpidBlock, kpidComment, kpidPosition, kpidPrefix, kpidTotalSize = 0x1100, kpidFreeSpace, kpidClusterSize, kpidVolumeName, kpidLocalName = 0x1200, kpidProvider, kpidUserDefined = 0x10000 } [ComImport] [Guid("23170F69-40C1-278A-0000-000600600000")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] //[AutomationProxy(true)] internal interface IInArchive { [PreserveSig] int Open( IInStream stream, /*[MarshalAs(UnmanagedType.U8)]*/ [In] ref ulong maxCheckStartPosition, [MarshalAs(UnmanagedType.Interface)] IArchiveOpenCallback openArchiveCallback); void Close(); //void GetNumberOfItems([In] ref uint numItem); uint GetNumberOfItems(); void GetProperty( uint index, ItemPropId propID, // PROPID ref PropVariant value); // PROPVARIANT [PreserveSig] int Extract( [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] uint[] indices, //[In] ref uint indices, uint numItems, int testMode, [MarshalAs(UnmanagedType.Interface)] IArchiveExtractCallback extractCallback); // indices must be sorted // numItems = 0xFFFFFFFF means all files // testMode != 0 means "test files operation" void GetArchiveProperty( uint propID, // PROPID ref PropVariant value); // PROPVARIANT //void GetNumberOfProperties([In] ref uint numProperties); uint GetNumberOfProperties(); void GetPropertyInfo( uint index, [MarshalAs(UnmanagedType.BStr)] out string name, out ItemPropId propID, // PROPID out ushort varType); //VARTYPE //void GetNumberOfArchiveProperties([In] ref uint numProperties); uint GetNumberOfArchiveProperties(); void GetArchivePropertyInfo( uint index, [MarshalAs(UnmanagedType.BStr)] string name, ref uint propID, // PROPID ref ushort varType); //VARTYPE } internal enum ArchivePropId : uint { kName = 0, kClassID, kExtension, kAddExtension, kUpdate, kKeepName, kStartSignature, kFinishSignature, kAssociate } [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate int CreateObjectDelegate( [In] ref Guid classID, [In] ref Guid interfaceID, //out IntPtr outObject); [MarshalAs(UnmanagedType.Interface)] out object outObject); [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate int GetHandlerPropertyDelegate( ArchivePropId propID, ref PropVariant value); // PROPVARIANT [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate int GetNumberOfFormatsDelegate(out uint numFormats); [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate int GetHandlerProperty2Delegate( uint formatIndex, ArchivePropId propID, ref PropVariant value); // PROPVARIANT internal class StreamWrapper : IDisposable { protected Stream BaseStream; protected StreamWrapper(Stream baseStream) { this.BaseStream = baseStream; } public void Dispose() { this.BaseStream.Close(); } public virtual void Seek(long offset, uint seekOrigin, IntPtr newPosition) { long Position = (uint) this.BaseStream.Seek(offset, (SeekOrigin) seekOrigin); if (newPosition != IntPtr.Zero) { Marshal.WriteInt64(newPosition, Position); } } } internal class InStreamWrapper : StreamWrapper, ISequentialInStream, IInStream { public InStreamWrapper(Stream baseStream) : base(baseStream) { } public uint Read(byte[] data, uint size) { return (uint) this.BaseStream.Read(data, 0, (int) size); } } // Can close base stream after period of inactivity and reopen it when needed. // Useful for long opened archives (prevent locking archive file on disk). internal class InStreamTimedWrapper : StreamWrapper, ISequentialInStream, IInStream { private string BaseStreamFileName; private long BaseStreamLastPosition; private Timer CloseTimer; private const int KeepAliveInterval = 10 * 1000; // 10 sec public InStreamTimedWrapper(Stream baseStream) : base(baseStream) { if ((this.BaseStream is FileStream) && !this.BaseStream.CanWrite && this.BaseStream.CanSeek) { this.BaseStreamFileName = ((FileStream) this.BaseStream).Name; this.CloseTimer = new Timer(new TimerCallback(this.CloseStream), null, KeepAliveInterval, Timeout.Infinite); } } private void CloseStream(object state) { if (this.CloseTimer != null) { this.CloseTimer.Dispose(); this.CloseTimer = null; } if (this.BaseStream != null) { if (this.BaseStream.CanSeek) { this.BaseStreamLastPosition = this.BaseStream.Position; } this.BaseStream.Close(); this.BaseStream = null; } } protected void ReopenStream() { if (this.BaseStream == null) { if (this.BaseStreamFileName != null) { this.BaseStream = new FileStream(this.BaseStreamFileName, FileMode.Open, FileAccess.Read, FileShare.Read); this.BaseStream.Position = this.BaseStreamLastPosition; this.CloseTimer = new Timer(new TimerCallback(this.CloseStream), null, KeepAliveInterval, Timeout.Infinite); } else { throw new ObjectDisposedException("StreamWrapper"); } } else if (this.CloseTimer != null) { this.CloseTimer.Change(KeepAliveInterval, Timeout.Infinite); } } public uint Read(byte[] data, uint size) { this.ReopenStream(); return (uint) this.BaseStream.Read(data, 0, (int) size); } public override void Seek(long offset, uint seekOrigin, IntPtr newPosition) { this.ReopenStream(); base.Seek(offset, seekOrigin, newPosition); } } internal class OutStreamWrapper : StreamWrapper, ISequentialOutStream, IOutStream { public OutStreamWrapper(Stream baseStream) : base(baseStream) { } public int SetSize(long newSize) { this.BaseStream.SetLength(newSize); return 0; } public int Write(byte[] data, uint size, IntPtr processedSize) { this.BaseStream.Write(data, 0, (int) size); if (processedSize != IntPtr.Zero) { Marshal.WriteInt32(processedSize, (int) size); } return 0; } } }