tar
tar

Reputation: 326

C#: How to use unrar.dll & unrar.cs from rarlab to extract a split archive?

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

  1. how to handle unrar.ReadHeader() to proceed with the next file of a split archive
  2. how to determine the first file of a split archive, in general
  3. (optional) how to determine the progress of the overall extraction process (to output the progress in my program during runtime)

Upvotes: 2

Views: 2256

Answers (3)

user13587121
user13587121

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.

  1. 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

jjwr
jjwr

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

tar
tar

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

Related Questions