C#:如何使用 rarlab 中的 unrar.dll & unrar.cs 来提取拆分存档?

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

我已经下载了 unrar.dll for Windows software developers from rarlab.com,将 unrar.cs 的 unrar.dll 和 class 包含到我的 C# WPF 项目中。

它提取单个档案(即使设置了密码):

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();

...但不是拆分的:

System.IO.IOException: "File CRC Error"

周围没有资料,想知道

  1. 如何处理unrar.ReadHeader()进行下一步 拆分存档的文件
  2. 一般情况下如何确定拆分存档的第一个文件
  3. (optional)如何判断整体提取进度 进程(在运行时输出我的程序的进度)

解决方案:

基本上,它与上面评论中@Viezevingertjes 所建议的 SharpCompress 一起使用。但不幸的是,SharpCompress 还不支持 RAR5 解密,这意味着 RAR5 密码保护的文件根本不会被提取,因此 SharpCompress 的使用受到限制。

作为解决方法,您只能通过这样的进程调用直接使用 rar.exe、unrar.exe 或 winrar.exe:

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;
    }
}

unrardll 中包含的 c# 包装器和示例已过时,并且不能很好地处理多卷 rar 文件,因为有未处理的新回调消息。要修复它,请更改:

private enum CallbackMessages : uint
{
    VolumeChange = 0,
    ProcessData = 1,
    NeedPassword = 2
}

private enum CallbackMessages : uint
{
    VolumeChange=0,
    ProcessData=1,
    NeedPassword=2,
    VolumeChangeW = 3,
    NeedPasswordW = 4,
}

并处理 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;
}

我遇到了这个问题,希望能解决我所有的问题。但我刚刚找到我的解决方案并愿意分享。

  1. how to handle unrar.ReadHeader() to proceed with the next file of a split archive

如果您使用的是 Unrar 中的 C# 示例,则应删除 ProcessFileError() 中的 IOException("File CRC Error") 或抛出错误。如果您以读取模式打开,文件打开错误也是如此。 如果 continuedOnNext == false.

case RARError.BadData:
    if (!currentFile.continuedOnNext) throw new IOException("File CRC Error");
    else break;

下一个文件将是完全相同的文件名并将成功提取(假设第 2 部分中的文件未损坏)。请注意,如果列出新文件,则需要修改 ReadHeader():

中的此语句
// Determine if new file
if (((header.Flags & 0x01) != 0) && currentFile != null)
        currentFile.ContinuedFromPrevious = true;

为此:

// Determine if new file
if ((header.Flags & 0x01) != 0) return true;

如果不想停止 while read 循环,则需要 return true。

how to determine the first file of a split archive, in general

如果您的意思是如何确定存档是否是第一卷,您应该将 Unrar.cs、archiveFlags 中的私有 属性 更改为更易于访问的修饰符(例如:内部或 public).

检查它是否是 volume/split 存档:

bool isVolume = (RARObj.archiveFlags & RAR.ArchiveFlags.Volume) == RAR.ArchiveFlags.Volume

检查它是否是第一个 volume/split 存档:

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)

您可以使用 ListFiles 获取存档文件计数并将其用作主要的存档提取过程。然后为 RARObj.ExtractionProgress 提取并创建一个事件以更新您拥有的任何 ProgressBar。

ExtractionProgressEventArgs e 有 属性 PercentComplete 供您更新进度条。我建议您分析 WinRAR 应用程序是如何工作的,这样您就可以更好地了解它是如何工作的。

哦还有一件事。

RAR RARObj = new Unrar(archive.path);