How to decompress a single file from an ZipFile using "SHFILEOPSTRUCT"? - c#

i know this question is asked 1000 times but I don't want to use any external tools like (DotNetZip or SharpZipLib).
I have currently this working piece of code:
string _from = Path.Combine(apk, "*.*") + '\0';
string _to = destination + '\0' + Path.Combine(destination, "*.*") + '\0';
NativeMethods.SHFILEOPSTRUCT fileop = new NativeMethods.SHFILEOPSTRUCT();
fileop.pFrom = _from;
fileop.pTo = _to;
fileop.wFunc = NativeMethods.FO_Type.FO_COPY;
fileop.fFlags = NativeMethods.FOF_Flag.FOF_WANTNUKEWARNING;
NativeMethods.SHFileOperation(ref fileop);
My problem is that I can't extract/copy a single file/folder. If i replace the *.* for example with res\\drawable\\icon.png it will do nothing.
Can someone tell me how to extract a single file/folder using the SHFILEOPSTRUCT? Do I miss something?
The code supports:
Framework Version: .Net Client 4
OS: WinXP, 8, 8.1, 10
EDIT (Notice):
When you use this piece of code in an C# thread you'll always get an 'Path length exceeded' error... In this case you need to use an 'Dispatcher'.

.NET 4.5 has native ZIP support, you should try that
using (ZipArchive archive = ZipFile.OpenRead(zipPath))
{
foreach (ZipArchiveEntry entry in archive.Entries.Where(e => e.FullName.Contains("a")))
{
entry.ExtractToFile(Path.Combine(extractPath, entry.FullName));
}
}

SHFileOperation is not capable of extracting individual items from a ZIP file. You options as I see them are:
Include a 3rd party ZIP library.
Switch to .net 4.5 and use the framework supplied ZIP library.
See if ZipPackage from System.IO.Packaging has enough functionality to meet your needs.
Write your own ZIP code.
Extract the entire ZIP file to a temporary directory and then pick out the parts you need.

pFrom needs to be double null-terminated:
string _from = Path.Combine(apk, "*.*") + "\0\0";
This is because it can contain multiple paths, which should be null-separated.

Related

C#: Programmatically create a split zip file

I want to create a zip file from a folder that it around 1.5GB, and have that zip file be split into chunks of 100MB. I've found quite a few threads on this, but nothing has quite worked out for me.
First I tried System.IO.Compression, but found that it doesn't support splitting the zip files (please correct me if I'm wrong!).
Next I tried Ionic.zip, which looked super easy, but every set of files I create is corrupted in some way (for example, the following code that uses the fonts directory as a test directory creates a set of files that I can't then open or unzip as an archive with either winzip or winrar):
using (var zipFile = new Ionic.Zip.ZipFile(Encoding.UTF8))
{
zipFile.AddDirectory("c:\\windows\\fonts", directoryPathInArchive: string.Empty);
zipFile.MaxOutputSegmentSize = 100 * 1000000;
zipFile.Save("c:\\users\\me\\test.zip");
}
Finally, I've tried the 7z.dll and SharpCompress. Using the Command Line and the 7z.exe file, the following works perfectly:
7z.exe a "c:\users\me\test.zip" "c:\Windows\Fonts" -v100m
But the following code gives the error "Value does not fall within the expected range."
SevenZipCompressor.SetLibraryPath("c:\\program files\\7-zip\\7z.dll");
var compressor = new SevenZipCompressor();
compressor.CompressionMethod = CompressionMethod.Lzma2;
compressor.CustomParameters.Add("v", "100m");
compressor.CompressDirectory("c:\\windows\\fonts\\", "c:\\users\\me\\test.zip");
I've also tried the following (having tried to figure out how the command line switches work in SharpCompress) which does create a zip file, but doesn't split it up:
SevenZipCompressor.SetLibraryPath("c:\\program files\\7-zip\\7z.dll");
var compressor = new SevenZipCompressor();
compressor.CompressionMethod = CompressionMethod.Lzma2;
compressor.CustomParameters.Add("mt", "on");
compressor.CustomParameters.Add("0", "LZMA2:c=100m");
compressor.CompressDirectory("c:\\windows\\fonts\\", "c:\\users\\me\\test.zip");
Does anyone know why any of the above methods aren't working? Or are there any other ways that people have got working that I haven't tried yet?
Thanks!
I am not aware of a library that supports the PKZIP split zip file format.
It's an old question, but Ionic is working. Maybe a little bit tricky, but ok. My first version also creates a set of files that I can't then unzip. But after changing the order of commands, the output can be unzipped.
private static void CreateEncryptedZipFile(string filename, string to, FileInfo fi, string password)
{
using (var zipFile = new Ionic.Zip.ZipFile())
{
zipFile.Password = password;
zipFile.Encryption = Ionic.Zip.EncryptionAlgorithm.WinZipAes256;
zipFile.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zipFile.AddFile(filename, directoryPathInArchive: string.Empty);
zipFile.MaxOutputSegmentSize = 1024*1024*128;
zipFile.Save(to + ".zip");
}
createXMLInfo(fi, to);
}

c# extract zip file with directory

I'm using c# fw4.5.
I have a simple code extracting a zip file.
foreach(ZipArchiveEntry entry in z.entries) //z is a zip file open in ZipArchiveMode.Read
{
entry.ExtractToFile(entry.FullName);
}
The zip file have a directory inside it and all files are inside that directory.
When I look at the z.Entries I see its an array which place [0] is only the directory and [1],[2],[3] are files.
But when its try to do:
entry.ExtractToFile(entry.FullName);
On the first entry, I get an error:
"The filename, directory name or volume label syntax is incorrect".
I can't seems to find out whats wrong. Do I need to anything also for it to open the directory? Maybe because the entry is a directory only the "ExtractToFile(entry.FullName)" can't work?
Thanks in advanced.
According to this MSDN article, the ExtractToFile method expects a path to a file (with an extension) and will throw an ArgumentException if a directory is specified.
Since the first entry in the archive is a directory and you are using its name as the argument, that is why you are having this issue.
Look into the related ExtractToDirectory method, which is used like so:
ZipFile.ExtractToDirectory(#"c:\zip\archive.zip", #"c:\extract\");
In addition to Tonkleton's answer I would suggest that you use a third-party compression library since ZipArchive is not supported for framework versions before the .Net 4.5 framework, might I suggest DotNetZip as mentioned in other questions regarding compression in earlier frameworks on StackOverflow.
Replace your paths:
void Main()
{
var zipPath = #"\\ai-vmdc1\RedirectedFolders\jlambert\Downloads\cscie33chap1and2.zip";
var extractPath = #"c:\Temp\extract";
using (ZipArchive z = ZipFile.OpenRead(zipPath))
{
foreach(ZipArchiveEntry entry in z.Entries) //z is a zip file open in ZipArchiveMode.Read
{
entry.ExtractToFile(Path.Combine(extractPath, entry.FullName), true);
}
}
}

C# SharpZipLib extract .TGZ NotSupportedException

I'm new to programming so please be patient.
I am currently developing a small Program in Visual C# 2010 Express, .NET Framework 4.0, which starts a Script on a Linux Machine (what creates the File /tmp/logs.tgz), downloads the File and then I want to extract it. Running the Script and downloading the File via Renci.SshNet works flawlessly.
But when I want to extract it, it gives me an Error "NotSupportedException" my Filepath Format is incorrect (which is not the case, I think?).
I copy and pasted the Code directly from here (Simple full extract from a TGZ (.tar.gz)) and edited it for my Needs:
using System.IO;
using System.IO.Compression;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
//it executed the Script and created the file on the Linux Machine /tmp/logs.tgz
//now I want to download it
string myTime = DateTime.Now.ToString("yyyyMMdd");
var pathWithEnv = (#"%USERPROFILE%\Desktop\logs" + myTime + ".tgz");
var filePath = Environment.ExpandEnvironmentVariables(pathWithEnv);
string localFile = filePath;
//then downloads /tmp/logs.tgz to ..\Desktop\logs+ myTime +.tgz
//everything great until now. here I want to extract .TGZ:
var pathWithEnv2 = (#"%USERPROFILE%\Desktop\logs" + myTime);
var fileDir = Environment.ExpandEnvironmentVariables(pathWithEnv2);
string localDir = fileDir;
Stream inStream = File.OpenRead(localFile);
Stream gzipStream = new GZipInputStream(inStream);
TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream);
//ERROR OCCURS HERE:
tarArchive.ExtractContents(localDir);
tarArchive.Close();
gzipStream.Close();
inStream.Close();
I even tried to set the localFile and localDir string without the EnviromentVariable, but that didnt help. I tried:
- download and extract it directly on C:\ (or on a mapped Network Drive U:) to prevent too long filenames (which should not be the case as it should never get longer than 86 characters).
- string = #"C:..\logs", string = "C:\..\logs", string = #"C:..\logs\", etc.
- tried it without myTime
- using ICSharpCode.SharpZipLib.Core;
I did a MessageBox.Show(localDir); before the tarArchive.ExtractContents(localDir); and it showed "C:\Users\Baumann\Desktop\logs20140530" which is correct, thats the Directory I want to extract it to. Even creating the Directory before executing it doesn't help.
I also created a new Project with just one button which should start the Extraction and the same error occurs.
I tried, doing it separately, first extract the GZip and then the .tar, but it also gives me the same Error when extracting the GZip (using ICSharpCode.SharpZipLib.Core; of course).
What drives me even more crazy about it, is, that it starts to extract it, but not everything, before it fails. And always the same Files, whats not clear for me why these and why not the others.
I'm on Windows 8.1, using SharpZipLib 0.86.0.518, downloaded directly from the Website.
Thanks in advance.
well, I finally fixed the Problem. The Linux machine is creating a file which includes the MAC-Adress and since Windows can't handle ":" in a Filename, it crashes.
I am now extracting file by file and checking each file for ":" and replacing it with "_", works flawlessly.

Extracting an ISO to a folder using WinZip/WinRar

I'm trying to extract an ISO using C#, I found a Winzip library, DotNetZip, and used that but when I run the project it says that it cannot extract the ISO.
string activeDir = copyTo = this.folderBD.SelectedPath;;
folderName = toExtract.Remove(toExtract.Length - 4, 4);
Path.Combine(activeDir, Path.GetFileNameWithoutExtension(folderName));
string zipToUnpack = toExtract;
string unpackDirectory = folderName;
using (ZipFile zip1 = ZipFile.Read(zipToUnpack))
{
// here, we extract every entry, but we could extract conditionally
// based on entry name, size, date, checkbox status, etc.
foreach (ZipEntry file in zip1)
{
file.Extract(unpackDirectory, ExtractExistingFileAction.OverwriteSilently);
}
}
That is the code I am working with. copyTo and folderName are sent in from other methods in the program.
Any libraries that let me use Winzip or Winrar on a ISO would be a great help, but so far my searches have thrown up nothing.
Thanks in advance
EDIT:
Can you only extract .rar or .zip using winrar with C# or can you pass the file to be extracted as a arguement and how? I've tried
ProcessStartInfo startInfo = new ProcessStartInfo("winrar.exe");
Process.Start("winrar.exe",#"C:\file\to\be\extracted");
The ISO location, but that returns an exception that there is nothing to extract there.
You can execute winrar from c# using Process.Start and pass in the arguments you need to extract the iso.

Unzip files programmatically in .net

I am trying to programatically unzip a zipped file.
I have tried using the System.IO.Compression.GZipStream class in .NET, but when my app runs (actually a unit test) I get this exception:
System.IO.InvalidDataException: The magic number in GZip header is not correct. Make sure you are passing in a GZip stream..
I now realize that a .zip file is not the same as a .gz file, and that GZip is not the same as Zip.
However, since I'm able to extract the file by manually double clicking the zipped file and then clicking the "Extract all files"-button, I think there should be a way of doing that in code as well.
Therefore I've tried to use Process.Start() with the path to the zipped file as input. This causes my app to open a Window showing the contents in the zipped file. That's all fine, but the app will be installed on a server with none around to click the "Extract all files"-button.
So, how do I get my app to extract the files in the zipped files?
Or is there another way to do it? I prefer doing it in code, without downloading any third party libraries or apps; the security department ain't too fancy about that...
With .NET 4.5 you can now unzip files using the .NET framework:
using System;
using System.IO;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
string extractPath = #"c:\example\extract";
System.IO.Compression.ZipFile.CreateFromDirectory(startPath, zipPath);
System.IO.Compression.ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}
The above code was taken directly from Microsoft's documentation: http://msdn.microsoft.com/en-us/library/ms404280(v=vs.110).aspx
ZipFile is contained in the assembly System.IO.Compression.FileSystem. (Thanks nateirvin...see comment below). You need to add a DLL reference to the framework assembly System.IO.Compression.FileSystem.dll
For .Net 4.5+
It is not always desired to write the uncompressed file to disk. As an ASP.Net developer, I would have to fiddle with permissions to grant rights for my application to write to the filesystem. By working with streams in memory, I can sidestep all that and read the files directly:
using (ZipArchive archive = new ZipArchive(postedZipStream))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
var stream = entry.Open();
//Do awesome stream stuff!!
}
}
Alternatively, you can still write the decompressed file out to disk by calling ExtractToFile():
using (ZipArchive archive = ZipFile.OpenRead(pathToZip))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
entry.ExtractToFile(Path.Combine(destination, entry.FullName));
}
}
To use the ZipArchive class, you will need to add a reference to the System.IO.Compression namespace and to System.IO.Compression.FileSystem.
We have used SharpZipLib successfully on many projects. I know it's a third party tool, but source code is included and could provide some insight if you chose to reinvent the wheel here.
Free, and no external DLL files. Everything is in one CS file. One download is just the CS file, another download is a very easy to understand example. Just tried it today and I can't believe how simple the setup was. It worked on first try, no errors, no nothing.
https://github.com/jaime-olivares/zipstorer
Use the DotNetZip library at http://www.codeplex.com/DotNetZip
class library and toolset for manipulating zip files. Use VB, C# or any .NET language to easily create, extract, or update zip files...
DotNetZip works on PCs with the full .NET Framework, and also runs on mobile devices that use the .NET Compact Framework. Create and read zip files in VB, C#, or any .NET language, or any scripting environment...
If all you want is a better DeflateStream or GZipStream class to replace the one that is built-into the .NET BCL, DotNetZip has that, too. DotNetZip's DeflateStream and GZipStream are available in a standalone assembly, based on a .NET port of Zlib. These streams support compression levels and deliver much better performance than the built-in classes. There is also a ZlibStream to complete the set (RFC 1950, 1951, 1952)...
String ZipPath = #"c:\my\data.zip";
String extractPath = #"d:\\myunzips";
ZipFile.ExtractToDirectory(ZipPath, extractPath);
To use the ZipFile class, you must add a reference to the System.IO.Compression.FileSystem assembly in your project
This will do it System.IO.Compression.ZipFile.ExtractToDirectory(ZipName, ExtractToPath)
Standard zip files normally use the deflate algorithm.
To extract files without using third party libraries use DeflateStream. You'll need a bit more information about the zip file archive format as Microsoft only provides the compression algorithm.
You may also try using zipfldr.dll. It is Microsoft's compression library (compressed folders from the Send to menu). It appears to be a com library but it's undocumented. You may be able to get it working for you through experimentation.
I use this to either zip or unzip multiple files. The Regex stuff is not required, but I use it to change the date stamp and remove unwanted underscores. I use the empty string in the Compress >> zipPath string to prefix something to all files if required. Also, I usually comment out either Compress() or Decompress() based on what I am doing.
using System;
using System.IO.Compression;
using System.IO;
using System.Text.RegularExpressions;
namespace ZipAndUnzip
{
class Program
{
static void Main(string[] args)
{
var directoryPath = new DirectoryInfo(#"C:\your_path\");
Compress(directoryPath);
Decompress(directoryPath);
}
public static void Compress(DirectoryInfo directoryPath)
{
foreach (DirectoryInfo directory in directoryPath.GetDirectories())
{
var path = directoryPath.FullName;
var newArchiveName = Regex.Replace(directory.Name, "[0-9]{8}", "20130913");
newArchiveName = Regex.Replace(newArchiveName, "[_]+", "_");
string startPath = path + directory.Name;
string zipPath = path + "" + newArchiveName + ".zip";
ZipFile.CreateFromDirectory(startPath, zipPath);
}
}
public static void Decompress(DirectoryInfo directoryPath)
{
foreach (FileInfo file in directoryPath.GetFiles())
{
var path = directoryPath.FullName;
string zipPath = path + file.Name;
string extractPath = Regex.Replace(path + file.Name, ".zip", "");
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}
}
You can do it all within .NET 3.5 using DeflateStream. The thing lacking in .NET 3.5 is the ability to process the file header sections that are used to organize the zipped files. PKWare has published this information, which you can use to process the zip file after you create the structures that are used. It is not particularly onerous, and it a good practice in tool building without using 3rd party code.
It isn't a one line answer, but it is completely doable if you are willing and able to take the time yourself. I wrote a class to do this in a couple of hours and what I got from that is the ability to zip and unzip files using .NET 3.5 only.
From here :
Compressed GZipStream objects written
to a file with an extension of .gz can
be decompressed using many common
compression tools; however, this class
does not inherently provide
functionality for adding files to or
extracting files from .zip archives.
I found out about this one (Unzip package on NuGet) today, since I ran into a hard bug in DotNetZip, and I realized there hasn't been really that much work done on DotNetZip for the last two years.
The Unzip package is lean, and it did the job for me - it didn't have the bug that DotNetZip had. Also, it was a reasonably small file, relying upon the Microsoft BCL for the actual decompression. I could easily make adjustments which I needed (to be able to keep track of the progress while decompressing). I recommend it.
From Embed Ressources:
using (Stream _pluginZipResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(programName + "." + "filename.zip"))
{
using (ZipArchive zip = new ZipArchive(_pluginZipResourceStream))
{
zip.ExtractToDirectory(Application.StartupPath);
}
}
Until now, I was using cmd processes in order to extract an .iso file, copy it into a temporary path from server and extracted on a usb stick. Recently I've found that this is working perfectly with .iso's that are less than 10Gb. For a iso like 29Gb this method gets stuck somehow.
public void ExtractArchive()
{
try
{
try
{
Directory.Delete(copyISOLocation.OutputPath, true);
}
catch (Exception e) when (e is IOException || e is UnauthorizedAccessException)
{
}
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
//stackoverflow
cmd.StartInfo.Arguments = "-R";
cmd.Disposed += (sender, args) => {
Console.WriteLine("CMD Process disposed");
};
cmd.Exited += (sender, args) => {
Console.WriteLine("CMD Process exited");
};
cmd.ErrorDataReceived += (sender, args) => {
Console.WriteLine("CMD Process error data received");
Console.WriteLine(args.Data);
};
cmd.OutputDataReceived += (sender, args) => {
Console.WriteLine("CMD Process Output data received");
Console.WriteLine(args.Data);
};
//stackoverflow
cmd.Start();
cmd.StandardInput.WriteLine("C:");
//Console.WriteLine(cmd.StandardOutput.Read());
cmd.StandardInput.Flush();
cmd.StandardInput.WriteLine("cd C:\\\"Program Files (x86)\"\\7-Zip\\");
//Console.WriteLine(cmd.StandardOutput.ReadToEnd());
cmd.StandardInput.Flush();
cmd.StandardInput.WriteLine(string.Format("7z.exe x -o{0} {1}", copyISOLocation.OutputPath, copyISOLocation.TempIsoPath));
//Console.WriteLine(cmd.StandardOutput.ReadToEnd());
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
Console.WriteLine(cmd.StandardOutput.ReadToEnd());
Console.WriteLine(cmd.StandardError.ReadToEnd());
you can use Info-unzip command line cod.you only need to download unzip.exe from Info-unzip official website.
internal static void Unzip(string sorcefile)
{
try
{
AFolderFiles.AFolderFilesDelete.DeleteFolder(TempBackupFolder); // delete old folder
AFolderFiles.AFolderFilesCreate.CreateIfNotExist(TempBackupFolder); // delete old folder
//need to Command command also to export attributes to a excel file
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; // window type
startInfo.FileName = UnzipExe;
startInfo.Arguments = sorcefile + " -d " + TempBackupFolder;
process.StartInfo = startInfo;
process.Start();
//string result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
process.Dispose();
process.Close();
}
catch (Exception ex){ throw ex; }
}

Categories