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

C# CAB 壓縮/解壓縮工具類:呼叫 makecab/expand 指令實作

     編輯:關於C#

簡介

CAB (Cabinet) 是微軟常用的壓縮格式,適用於驅動程式套件、系統更新等場景。.NET Framework 未內建 CAB 操作 API,常見做法是呼叫系統內建的 makecab.exe 與 expand.exe 命令列工具。本文提供一個封裝良好的 Cab 類別,支援多檔案壓縮、解壓縮、臨時目錄管理與錯誤處理。

類別架構概覽

  • 屬性:CabFileName(輸出/來源 .cab 路徑)、FileList(待壓縮或解壓出的檔案清單)
  • 建構子:自動建立臨時工作目錄(%Templates%\CabTemp\)
  • 公有方法:MakeCab() 壓縮、ExpandCab() 解壓縮
  • 私有輔助方法:產生 list.txt、執行 CMD 指令、移動產出的 .cab、解析指令輸出取得檔案清單

核心程式碼

Cab.cs 完整實作

using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Text;

namespace Com.ImYan.CabHelper
{
    public class Cab
    {
        #region 屬性
        private string _cabFileName;
        public string CabFileName
        {
            get { return _cabFileName; }
            set { _cabFileName = value; }
        }

        private List _fileList = new List();
        public List FileList
        {
            get { return _fileList; }
            set { _fileList = value; }
        }

        private string TempDir;
        #endregion

        #region 建構子
        public Cab()
        {
            // 指定並建立臨時目錄
            this.TempDir = Environment.GetFolderPath(Environment.SpecialFolder.Templates);
            this.TempDir = string.Format("{0}\\CabTemp\\", this.TempDir);
            if (Directory.Exists(this.TempDir)) Directory.Delete(this.TempDir, true);
            Directory.CreateDirectory(this.TempDir);
        }
        #endregion

        #region 私有方法
        // 產生 makecab 所需的 list.txt
        private string CreateListFile()
        {
            try
            {
                string listFilePath = Path.Combine(this.TempDir, "list.txt");
                string listContent = string.Empty;
                for (int i = 0; i < this.FileList.Count; i++)
                {
                    listContent += string.Format("\"{0}\"\r\n", this.FileList[i]);
                }
                using (FileStream fs = new FileStream(listFilePath, FileMode.Create, FileAccess.Write))
                using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
                {
                    sw.Write(listContent);
                }
                return listFilePath;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        // 執行命令列指令並回傳輸出
        private string RunCommand(string cmdString)
        {
            Process p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.CreateNoWindow = true;
            p.Start();
            p.StandardInput.WriteLine(cmdString);
            p.StandardInput.WriteLine("exit");
            p.WaitForExit(60000);
            string output = p.StandardOutput.ReadToEnd();
            p.Close();
            return output;
        }

        // 從臨時目錄尋找 .cab 並移動到指定輸出路徑
        private void MoveCabFile()
        {
            string cabFilePath = string.Empty;
            List allFiles = GetFilesFromDir(this.TempDir);
            foreach (string file in allFiles)
            {
                if (Path.GetExtension(file).ToLower() == ".cab")
                {
                    cabFilePath = file;
                    break;
                }
                else
                {
                    File.Delete(file); // 清理非 cab 檔
                }
            }
            File.Move(cabFilePath, this.CabFileName);
        }

        // 遞迴取得目錄下所有檔案
        private List GetFilesFromDir(string dir)
        {
            List files = new List(Directory.GetFiles(dir));
            foreach (string childDir in Directory.GetDirectories(dir))
            {
                files.AddRange(GetFilesFromDir(childDir));
            }
            return files;
        }

        // 解析 expand 輸出,取得解壓出的檔案路徑
        private void GetExpandedFileByCmdResult(string cmdResult)
        {
            this._fileList = new List();
            string[] lines = cmdResult.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            string key = string.Format("將 {0} 展開成", this.CabFileName.ToLower());
            foreach (string line in lines)
            {
                if (line.Trim().StartsWith(key))
                {
                    string filePath = line.Replace(key, "").Trim().Replace("。", "");
                    if (File.Exists(filePath))
                        this._fileList.Add(filePath);
                }
            }
        }
        #endregion

        #region 公有方法
        // 壓縮建立 CAB
        public bool MakeCab(out string errorInfo)
        {
            errorInfo = string.Empty;
            try
            {
                string listFile = this.CreateListFile();
                if (!File.Exists(listFile)) throw new Exception("生成 list.txt 失敗!");

                string cmd = "makecab /F " + listFile;
                string result = this.RunCommand(cmd);
                this.MoveCabFile();

                if (File.Exists(this.CabFileName)) return true;
                errorInfo = string.Format("檔案 {0} 生成失敗!", this.CabFileName);
                return false;
            }
            catch (Exception ex)
            {
                errorInfo = ex.Message;
                return false;
            }
        }

        // 解壓縮 CAB
        public bool ExpandCab(out string errorInfo)
        {
            errorInfo = string.Empty;
            try
            {
                if (!File.Exists(this.CabFileName)) throw new Exception("CAB 檔不存在!");
                if (!Directory.Exists(this.TempDir)) Directory.CreateDirectory(this.TempDir);

                string tempPath = this.TempDir.TrimEnd('\\');
                string cmd = string.Format("expand \"{0}\" \"{1}\" -f:*", this.CabFileName, tempPath);
                string result = this.RunCommand(cmd);
                this.GetExpandedFileByCmdResult(cmdResult);
                return true;
            }
            catch (Exception ex)
            {
                errorInfo = ex.Message;
                return false;
            }
        }
        #endregion
    }
}

使用範例

壓縮檔案

Cab cab = new Cab();
cab.CabFileName = @"D:\Output\MyFiles.cab";
cab.FileList.Add(@"D:\Source\File1.txt");
cab.FileList.Add(@"D:\Source\File2.doc");
string err;
if (cab.MakeCab(out err))
    Console.WriteLine("壓縮成功!");
else
    Console.WriteLine("失敗:" + err);

解壓縮檔案

Cab cab = new Cab();
cab.CabFileName = @"D:\Output\MyFiles.cab";
string err;
if (cab.ExpandCab(out err))
{
    Console.WriteLine("解壓縮成功,檔案清單:");
    foreach (string f in cab.FileList) Console.WriteLine(f);
}
else
    Console.WriteLine("失敗:" + err);

注意事項與限制

  • 依賴系統內建 makecab.exe 與 expand.exe,需確保執行環境為 Windows。
  • 臨時目錄固定在 %Templates%\CabTemp\,多執行緒同時使用時可能衝突,建議改用 Path.GetTempPath() + 隨機子目錄。
  • 解析 expand 輸出的關鍵字「將 {cab} 展開成」為中文系統預設訊息,英文系統需調整關鍵字。
  • 大型檔案壓縮/解壓縮時,WaitForExit(60000) 可能不足,建議改為非同步讀取輸出流或增加逾時。

結語

透過封裝命令列工具,我們在不引入第三方函式庫的情況下,快速獲得 CAB 壓縮/解壓縮功能。若專案需跨平台或追求更高效能,可考慮使用 SharpCompress 或 Microsoft.Deployment.Compression.Cab (Dism API) 等純 managed 實作。

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