首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 調用 WinRAR 壓縮/解壓完整方案:命令列參數、路徑含空格處理與進程同步

     編輯:關於C語言

前言

在 .NET 生態中,SharpZipLib / System.IO.Compression 僅支援 ZIP 格式,若需原生支援 RAR、7z、分卷壓縮、加密密碼等高級功能,調用已安裝的 WinRAR(或 rar.exe/unrar.exe)是最穩健、相容性最佳的方案。本文提供兩套完整實作:

  1. 純 .NET 方案:ZipOutputStream/ZipInputStream 處理 ZIP,無外部依賴。
  2. WinRAR 命令列方案:支援 RAR/ZIP/7Z,處理路徑空格、隱藏視窗、同步等待、註冊表自動定位。

方案一:純 .NET ZIP 操作(Zero Dependency)

using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip; // NuGet: SharpZipLib

public static class ZipHelper
{
    /// 壓縮整個目錄為 ZIP(保留相對路徑)
    public static void CreateZip(string sourceDir, string zipPath, int level = 9, string password = null)
    {
        if (!Directory.Exists(sourceDir)) throw new DirectoryNotFoundException(sourceDir);

        using (var fs = File.Create(zipPath))
        using (var zip = new ZipOutputStream(fs))
        {
            zip.SetLevel(level);
            if (!string.IsNullOrEmpty(password)) zip.Password = password;

            byte[] buffer = new byte[4096];
            foreach (string file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories))
            {
                string entryName = file.Substring(sourceDir.Length + 1).Replace('\\', '/');
                var entry = new ZipEntry(entryName) { DateTime = DateTime.Now, Size = new FileInfo(file).Length };
                zip.PutNextEntry(entry);

                using (var fsIn = File.OpenRead(file))
                {
                    int read;
                    while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                        zip.Write(buffer, 0, read);
                }
            }
            zip.Finish();
        }
    }

    /// 解壓 ZIP 到指定目錄(自動建立子目錄)
    public static void ExtractZip(string zipPath, string destDir, string password = null)
    {
        if (!File.Exists(zipPath)) throw new FileNotFoundException(zipPath);
        Directory.CreateDirectory(destDir);

        using (var fs = File.OpenRead(zipPath))
        using (var zip = new ZipInputStream(fs))
        {
            if (!string.IsNullOrEmpty(password)) zip.Password = password;

            ZipEntry entry;
            while ((entry = zip.GetNextEntry()) != null)
            {
                string fullPath = Path.Combine(destDir, entry.Name);
                if (entry.IsDirectory)
                {
                    Directory.CreateDirectory(fullPath);
                    continue;
                }
                Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
                using (var outFs = File.Create(fullPath))
                {
                    byte[] buffer = new byte[4096];
                    int read;
                    while ((read = zip.Read(buffer, 0, buffer.Length)) > 0)
                        outFs.Write(buffer, 0, read);
                }
            }
        }
    }
}

方案二:WinRAR 命令列完整封裝(支援 RAR/ZIP/7Z、空格路徑、同步等待)

using System;
using System.Diagnostics;
using System.IO;
using Microsoft.Win32;

public static class WinRarHelper
{
    /// 自動從註冊表獲取 WinRAR.exe 路徑,失敗回退常見安裝目錄
    private static string GetWinRarPath()
    {
        using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe"))
        {
            if (key?.GetValue("") is string path && File.Exists(path)) return path;
        }
        // 常見回退路徑
        string[] candidates = {
            @"C:\Program Files\WinRAR\WinRAR.exe",
            @"C:\Program Files (x86)\WinRAR\WinRAR.exe",
            Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rar.exe") // 攜帶 rar.exe 部署
        };
        foreach (var p in candidates) if (File.Exists(p)) return p;
        throw new FileNotFoundException("找不到 WinRAR.exe,請安裝或將 rar.exe 放在程式目錄。");
    }

    /// 通用執行器:隱藏視窗、同步等待、拋出異常
    private static void Run(string arguments, string workingDir = null)
    {
        var exe = GetWinRarPath();
        var psi = new ProcessStartInfo(exe, arguments)
        {
            UseShellExecute = false,
            CreateNoWindow = true,
            WindowStyle = ProcessWindowStyle.Hidden,
            WorkingDirectory = workingDir ?? Path.GetDirectoryName(exe)
        };
        using (var proc = Process.Start(psi))
        {
            proc.WaitForExit();
            if (proc.ExitCode != 0)
                throw new InvalidOperationException($"WinRAR 退出碼 {proc.ExitCode},參數:{arguments}");
        }
    }

    // ========== 壓縮 ==========
    /// 壓縮目錄/文件為 RAR(支援空格路徑)
    /// 要壓縮的來源路徑(文件或目錄)
    /// 輸出目錄
    /// 輸出檔名(含副檔名 .rar/.zip)
    public static void Compress(string inputPath, string outputDir, string archiveName)
    {
        if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir);
        if (!File.Exists(inputPath) && !Directory.Exists(inputPath))
            throw new ArgumentException("來源路徑不存在:" + inputPath);

        // -r 遞歸 -ep1 排除基礎路徑 -ibck 後台模式 -y 覆蓋確認
        string args = $"a -r -ep1 -ibck -y \"{Path.Combine(outputDir, archiveName)}\" \"{inputPath}\"";
        Run(args);
    }

    /// 壓縮為 ZIP(WinRAR 也支援 ZIP 格式)
    public static void CompressZip(string inputPath, string outputDir, string archiveName)
    {
        string args = $"a -afzip -r -ep1 -ibck -y \"{Path.Combine(outputDir, archiveName)}\" \"{inputPath}\"";
        Run(args);
    }

    // ========== 解壓 ==========
    /// 解壓縮包到指定目錄(支援 .rar/.zip/.7z 等 WinRAR 支援格式)
    /// 壓縮包完整路徑
    /// 解壓目標目錄(需預先存在)
    public static void Extract(string archivePath, string destDir)
    {
        if (!File.Exists(archivePath)) throw new FileNotFoundException(archivePath);
        if (!Directory.Exists(destDir)) Directory.CreateDirectory(destDir);

        // x 保留路徑解壓 -o+ 覆蓋 -y 全是 -inul 禁用錯誤對話框
        string args = $"x -inul -o+ -y \"{archivePath}\" \"{destDir}\"";
        Run(args, Path.GetDirectoryName(archivePath));
    }

    /// 解壓到當前目錄(同名資料夾)
    public static void ExtractHere(string archivePath) =>
        Extract(archivePath, Path.GetDirectoryName(archivePath));
}

調用範例

class Program
{
    static void Main()
    {
        string work = @"D:\Projects\Demo";
        string rarOut = @"D:\Output";

        // 1. 壓縮整個資料夾為 RAR
        WinRarHelper.Compress(work, rarOut, "Demo.rar");

        // 2. 壓縮為 ZIP(相容性更好)
        WinRarHelper.CompressZip(work, rarOut, "Demo.zip");

        // 3. 解壓
        WinRarHelper.Extract(@"D:\Output\Demo.rar", @"D:\Extract\Demo");

        Console.WriteLine("完成");
    }
}

WinRAR 命令列參數速查表

參數說明
a添加文件到壓縮包
x解壓並保留完整路徑
e解壓但忽略路徑(所有文件平鋪)
-r遞歸子目錄
-ep1排除基礎目錄名(只存相對路徑)
-ep2存完整路徑(含磁碟代號)
-afzip建立 ZIP 格式(預設 RAR)
-hp[pwd]加密並設定密碼(如 -hp123)
-ibck後台模式(無進度條)
-y所有提示預設 Yes
-o+覆蓋現有文件不提示
-inul禁用錯誤對話框(靜默模式)
-v分卷大小(如 -v100m)
-rr[N]添加恢復記錄

避坑指南

  • 路徑含空格:全部用雙引號包裹 "path with space",代碼中已處理。
  • 工作目錄:解壓時 WorkingDirectory 設為壓縮包所在目錄,相對路徑才正確。
  • 權限問題:寫入 Program Files 需管理員權限,建議輸出到用戶目錄。
  • 非同步需求:若不想阻塞 UI,移除 WaitForExit() 改用 proc.Exited 事件或 Task.Run 包裝。
  • 攜帶部署:隨程式附帶 rar.exe + unrar.dll(約 1MB),免安裝 WinRAR 也能用,注意授權條款。

總結

純 .NET 方案適合純 ZIP、跨平台、無外部依賴場景;WinRAR 方案勝在格式支援全(RAR/7Z/分卷/加密/恢復記錄)、參數豐富、企業級穩定。本文提供的 WinRarHelper 封裝了註冊表定位、空格路徑、同步等待、錯誤碼檢查,開箱即用。根據專案需求選擇:單純 ZIP 用 SharpZipLib,需 RAR/高級功能用 WinRAR,兩者互補,構建完整壓縮體系。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved