Reputation: 326
I've downloaded the unrar.dll for Windows software developers from rarlab.com, included the unrar.dll and the class of unrar.cs into my C# WPF project.
It extracts single archives (even with password set):
string source = "d:\\unrartest\\compressed\\myarchive.part1.rar";
string dest = "d:\\unrartest\\extracted";
Unrar unrar = new Unrar();
unrar.Password = "password_of_myarchive";
unrar.Open(@source, Unrar.OpenMode.Extract);
while (unrar.ReadHeader())
{
unrar.ExtractToDirectory(@dest);
}
unrar.Close();
... but not split ones:
System.IO.IOException: "File CRC Error"
As there is no documentation around, I would like to know
unrar.ReadHeader()
to proceed with the next
file of a split archiveUpvotes: 2
Views: 2256
Reputation:
I came across this issue hoping for an answer to all my problems. But I just found my solution and am willing to share.
- how to handle unrar.ReadHeader() to proceed with the next file of a split archive
If you are using the C# example from Unrar, you should remove the IOException("File CRC Error")
in ProcessFileError()
or throw the error. Same thing for File Open Error if you opened in read mode.
if continuedOnNext == false
.
case RARError.BadData:
if (!currentFile.continuedOnNext) throw new IOException("File CRC Error");
else break;
The next file will be the exact same filename and will extract successfully (assuming the file in part 2 isn't corrupted). Do note that if you list the new files, you need to modify this statement in ReadHeader()
:
// Determine if new file
if (((header.Flags & 0x01) != 0) && currentFile != null)
currentFile.ContinuedFromPrevious = true;
To this:
// Determine if new file
if ((header.Flags & 0x01) != 0) return true;
You want to return true if you don't want to stop a while read loop.
how to determine the first file of a split archive, in general
If you mean how to determine if the archive is the first volume, you should change the private property in Unrar.cs, archiveFlags
, to a more accessible modifier (ex: internal or public).
To check if it's a volume/split archive:
bool isVolume = (RARObj.archiveFlags & RAR.ArchiveFlags.Volume) == RAR.ArchiveFlags.Volume
To check if it's the first volume/split archive:
bool isFirstVolume = (RARObj.archiveFlags & RAR.ArchiveFlags.FirstVolume) == RAR.ArchiveFlags.FirstVolume
how to determine the progress of the overall extraction process (to output the progress in my program during runtime)
You can use ListFiles
to get the archive file count and use that as the main archive extraction process. Then extract and create an event for RARObj.ExtractionProgress
to update whatever ProgressBar you have.
ExtractionProgressEventArgs e
has the property PercentComplete
for you to update your progress bar. I suggest you analyze how the WinRAR app does it so you can get a better idea of how this works.
oh one more thing.
RAR RARObj = new Unrar(archive.path);
Upvotes: 1
Reputation: 56
The c# wrapper and example included in the unrardll is outdated and does not work well with multivolume rar files because there are new callback messaged that are not handled. To fix it change:
private enum CallbackMessages : uint
{
VolumeChange = 0,
ProcessData = 1,
NeedPassword = 2
}
to
private enum CallbackMessages : uint
{
VolumeChange=0,
ProcessData=1,
NeedPassword=2,
VolumeChangeW = 3,
NeedPasswordW = 4,
}
And handle the new callback message for VolumeChangeW:
private int RARCallback(uint msg, int UserData, IntPtr p1, int p2)
{
string volume=string.Empty;
string newVolume=string.Empty;
int result=-1;
switch((CallbackMessages)msg)
{
case CallbackMessages.VolumeChange:
case CallbackMessages.VolumeChangeW:
if ((CallbackMessages)msg == CallbackMessages.VolumeChange)
volume =Marshal.PtrToStringAnsi(p1);
else
volume = Marshal.PtrToStringAuto(p1);
if ((VolumeMessage)p2==VolumeMessage.Notify)
result=OnNewVolume(volume);
else if((VolumeMessage)p2==VolumeMessage.Ask)
{
newVolume=OnMissingVolume(volume);
if(newVolume.Length==0)
result=-1;
else
{
if(newVolume!=volume)
{
for(int i=0; i<newVolume.Length; i++)
{
Marshal.WriteByte(p1, i, (byte)newVolume[i]);
}
Marshal.WriteByte(p1, newVolume.Length, (byte)0);
}
result=1;
}
}
break;
case CallbackMessages.ProcessData:
result=OnDataAvailable(p1, p2);
break;
case CallbackMessages.NeedPassword:
result=OnPasswordRequired(p1, p2);
break;
default:
break;
}
return result;
}
Upvotes: 3
Reputation: 326
Solution:
Basically, it works with SharpCompress as suggested by @Viezevingertjes in the comments above. But unfortunately, SharpCompress does not yet support RAR5 decryption, which means that RAR5 password protected files are not extracted, at all and therefore SharpCompress has a limited usage.
As workaround, you can only use rar.exe, unrar.exe or winrar.exe directly by a process call like this:
string rarExec = "\"c:\\Program Files\\WinRAR\\Rar.exe\"";
string sourceRar = "\"c:\\sourceFolder\\myfile.part001.rar\"";
string targetPath = "\"c:\\destinationFolder\"";
string password = "topsecret";
using (System.Diagnostics.Process process = new System.Diagnostics.Process())
{
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo.FileName = rarExec;
if (password != "")
process.StartInfo.Arguments = "/c x -cfg- -inul -y " + "-p" + password + sourceRar + " " + targetPath;
else
process.StartInfo.Arguments = "/c x -cfg- -inul -y " + sourceRar + " " + targetPath;
process.Start();
process.WaitForExit();
switch (process.ExitCode)
{
case 0:
// "OK: extracted";
break;
case 10:
// "Error: wrong password";
break;
default:
// "Error: unknown";
break;
}
}
Upvotes: 0