C#: 如何使用 rarlab 的 unrar.dll 和 unrar.cs 从分割存档中提取数据?

huangapple go评论73阅读模式
英文:

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

问题

I've translated the code-related portion of your text:

我已经从[rarlab.com][2]下载了[Windows软件开发人员的unrar.dll][1],并将unrar.dll和unrar.cs类包含在我的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: "文件CRC错误"

由于没有相关文档,我想知道:

 1. 如何处理`unrar.ReadHeader()`以继续处理分割归档的下一个文件
 2. 如何通常确定分割归档的第一个文件
 3. (可选)如何确定总体提取过程的进度(以在运行时在我的程序中输出进度)

  [1]: https://www.rarlab.com/rar/UnRARDLL.exe
  [2]: https://www.rarlab.com/rar_add.htm

Is there anything else you need help with?

英文:

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)

答案1

得分: 3

以下是翻译好的部分:

unrar.dll中包含的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;
}

请注意,这是提供的代码段的翻译部分。如果您需要更多帮助或有其他问题,请随时提出。

英文:

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

答案2

得分: 1

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. 如何处理 unrar.ReadHeader() 以继续处理分割存档的下一个文件

如果你正在使用Unrar的C#示例,应该在ProcessFileError()中移除IOException(&quot;File CRC Error&quot;)或者抛出该错误。对于文件打开错误,如果你以读模式打开了它,也是同样的处理方式,如果continuedOnNext == false

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

下一个文件将具有完全相同的文件名,成功解压(假设第2部分的文件没有损坏)。请注意,如果你列出了新文件,你需要修改ReadHeader()中的这个语句:

// 确定是否为新文件
if (((header.Flags &amp; 0x01) != 0) &amp;&amp; currentFile != null)
    	currentFile.ContinuedFromPrevious = true;

修改为:

// 确定是否为新文件
if ((header.Flags &amp; 0x01) != 0) return true;

如果你不想停止读取循环,你需要返回true。

如何确定分割存档的第一个文件,一般来说

如果你想确定存档是否是第一个卷,你应该更改Unrar.cs中的私有属性archiveFlags的可访问修饰符(例如:internal或public)。

要检查它是否为卷/分割存档:

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

要检查它是否为第一个卷/分割存档:

bool isFirstVolume = (RARObj.archiveFlags &amp; RAR.ArchiveFlags.FirstVolume) == RAR.ArchiveFlags.FirstVolume

如何确定整体提取过程的进度(在运行时输出进度到我的程序中)

你可以使用ListFiles获取存档文件的数量,然后将其作为主要的存档提取过程。然后提取并创建RARObj.ExtractionProgress的事件来更新你的进度条。

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

还有一件事:

RAR RARObj = new Unrar(archive.path);
英文:

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(&quot;File CRC Error&quot;) 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(&quot;File CRC Error&quot;);
    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 &amp; 0x01) != 0) &amp;&amp; currentFile != null)
    	currentFile.ContinuedFromPrevious = true;

To this:

// Determine if new file
if ((header.Flags &amp; 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 &amp; RAR.ArchiveFlags.Volume) == RAR.ArchiveFlags.Volume

To check if it's the first volume/split archive:

bool isFirstVolume = (RARObj.archiveFlags &amp; 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);

答案3

得分: 0

解决方案:

基本上,它使用SharpCompress,如上面的评论中由@Viezevingertjes建议的。但不幸的是,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;
    }
}

请注意,以上是代码部分的翻译。

英文:

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 = &quot;\&quot;c:\\Program Files\\WinRAR\\Rar.exe\&quot;&quot;;
string sourceRar = &quot;\&quot;c:\\sourceFolder\\myfile.part001.rar\&quot;&quot;;
string targetPath = &quot;\&quot;c:\\destinationFolder\&quot;&quot;;
string password = &quot;topsecret&quot;;

using (System.Diagnostics.Process process = new System.Diagnostics.Process())
{
	process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
	process.StartInfo.FileName = rarExec;
	if (password != &quot;&quot;)
		process.StartInfo.Arguments = &quot;/c x -cfg- -inul -y &quot; + &quot;-p&quot; + password + sourceRar + &quot; &quot; + targetPath;
	else
		process.StartInfo.Arguments = &quot;/c x -cfg- -inul -y &quot; + sourceRar + &quot; &quot; + targetPath;

	process.Start();
	process.WaitForExit();

	switch (process.ExitCode)
	{
		case 0:
			// &quot;OK: extracted&quot;;
			break;
		case 10:
			// &quot;Error: wrong password&quot;;
			break;
		default:
			// &quot;Error: unknown&quot;;
			break;
	}
}

huangapple
  • 本文由 发表于 2020年1月3日 17:36:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/59576125.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定