[C#]如何使用Windows Shell做Zip檔的壓縮與解壓縮
在.NET程式中開發人員要做壓縮與解壓縮有很多種方法,可以用BCL內建的壓縮類別,或是用 DotNetZip與SharpZipLib之類的第三方元件庫,抑或是使用Windows Shell來做。內建的壓縮方式與第三方元件很多人都做過了介紹,使用上也大概都不是問題,但對於Windows Shell的使用方式可能就比較沒有那麼熟悉,所以這邊筆者稍微簡介一下怎樣用Windows Shell來做Zip檔的壓縮與解壓縮。
要用Windows Shell來做Zip檔的壓縮與解壓縮,因為我們要使用Shell的功能,因此首先必須將Microsoft Shell Controls and Automation組件加入參考。
組件參考加入後,還要記得引用Shell32命名空間,引用後我們可以開始進行Zip檔的壓縮與解壓縮。程式部份很簡單,Windows Shell將一般檔案系統的目錄與壓縮檔皆視為一個Folder物件。因此壓縮時是將檔案目錄裡面的FolderItems拷貝到壓縮檔的Folder物件,解壓縮就是將壓縮檔的FolderItems拷貝到目的目錄的Folder物件。這邊特別要注意的是要是壓縮時壓縮檔不存在的話,需要建立一個空的檔案,這樣程式才能正常的取得壓縮檔Folder物件。
private static void ShellCopyTo(string from, string to)
{
Shell sc = new Shell();
Folder SrcFolder = sc.NameSpace(from);
Folder DestFolder = sc.NameSpace(to);
FolderItems items = SrcFolder.Items();
DestFolder.CopyHere(items, 20);
}
public static void Compress(string sourceFolderPath, string zipFile)
{
if (!Directory.Exists(sourceFolderPath))
throw new DirectoryNotFoundException();
if (!File.Exists(zipFile))
File.Create(zipFile).Dispose();
ShellCopyTo(sourceFolderPath, zipFile);
}
public static void DeCompress(string zipFile, string destinationFolderPath)
{
if (!File.Exists(zipFile))
throw new FileNotFoundException();
if (!Directory.Exists(destinationFolderPath))
Directory.CreateDirectory(destinationFolderPath);
ShellCopyTo(zipFile, destinationFolderPath);
}
整個壓縮與解壓縮動作就是那麼簡單,若是不想要引用額外的參考,這邊也可以改成透過反射去做,替換下方的ShellCopyTo方法就可以了。
private static void ShellCopyTo(string from, string to)
{
Type shellType = Type.GetTypeFromProgID("Shell.Application");
object shellObject = System.Activator.CreateInstance(shellType);
object objSrcFile = shellType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shellObject, new object[] { from });
object objDestFolder = shellType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shellObject, new object[] { to });
Type FolderType = Type.GetTypeFromCLSID(new Guid("BBCBDE60-C3FF-11CE-8350-444553540000"));
object items = FolderType.InvokeMember("Items", System.Reflection.BindingFlags.InvokeMethod, null, objSrcFile, null);
FolderType.InvokeMember("CopyHere", System.Reflection.BindingFlags.InvokeMethod, null, objDestFolder, new object[] { items, 20 });
}
最後這邊筆者隨手試著整理了一個簡易的類別,可以做到基本的壓縮、解壓縮、以及查驗壓縮檔內含有什麼內容,有興趣的可以自行參閱使用。
public enum ZipEntryType
{
File,
Folder
}
public class ZipEntry
{
#region Private Property
private FolderItem m_ShellItem { get; set; }
private IEnumerable<ZipEntry> _entrys;
#endregion
#region Public Property
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
public string Name
{
get
{
return m_ShellItem.Name;
}
}
/// <summary>
/// Gets the type.
/// </summary>
/// <value>The type.</value>
public ZipEntryType Type
{
get
{
return m_ShellItem.IsFolder ? ZipEntryType.Folder : ZipEntryType.File;
}
}
/// <summary>
/// Gets the modify date.
/// </summary>
/// <value>The modify date.</value>
public DateTime ModifyDate
{
get
{
return m_ShellItem.ModifyDate;
}
}
/// <summary>
/// Gets the size.
/// </summary>
/// <value>The size.</value>
public int Size
{
get
{
return m_ShellItem.Size;
}
}
public IEnumerable<ZipEntry> Entrys
{
get
{
if (_entrys == null)
{
if (!m_ShellItem.IsFolder)
{
_entrys = new ZipEntry[0];
}
else
{
var folder = m_ShellItem.GetFolder as Folder;
var items = new List<ZipEntry>();
foreach (FolderItem shellItem in folder.Items())
{
items.Add(new ZipEntry(shellItem));
}
_entrys = items;
}
}
return _entrys;
}
}
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="ZipEntry"/> struct.
/// </summary>
/// <param name="shellItem">The shell item.</param>
public ZipEntry(FolderItem shellItem)
{
m_ShellItem = shellItem;
}
#endregion
}
public class ShellZip
{
#region Var
private FolderItems _shellItems;
private IEnumerable<ZipEntry> _items;
#endregion
#region Private Property
/// <summary>
/// Gets the m_ shell items.
/// </summary>
/// <value>The m_ shell items.</value>
private FolderItems m_ShellItems
{
get
{
return _shellItems??(_shellItems = (new Shell()).NameSpace(FilePath).Items());
}
}
#endregion
#region Property
/// <summary>
/// Gets or sets the file.
/// </summary>
/// <value>The file.</value>
public string FilePath { get; private set; }
/// <summary>
/// Gets the count.
/// </summary>
/// <value>The count.</value>
public int Count
{
get
{
return m_ShellItems.Count;
}
}
public IEnumerable<ZipEntry> Items
{
get
{
if (_items == null)
{
var items = new List<ZipEntry>();
foreach (FolderItem shellItem in m_ShellItems)
{
items.Add(new ZipEntry(shellItem));
}
_items = items;
}
return _items;
}
}
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="ShellZip"/> class.
/// </summary>
/// <param name="zipFile">The zip file.</param>
public ShellZip(string zipFile)
{
this.FilePath = zipFile;
}
#endregion
#region Private Static Method
/// <summary>
/// Shells the copy to.
/// </summary>
/// <param name="from">From.</param>
/// <param name="to">To.</param>
private static void ShellCopyTo(string from, string to)
{
Shell sc = new Shell();
Folder SrcFolder = sc.NameSpace(from);
Folder DestFolder = sc.NameSpace(to);
FolderItems items = SrcFolder.Items();
DestFolder.CopyHere(items, 20);
}
#endregion
#region Public Static Method
/// <summary>
/// Compresses the specified source folder path.
/// </summary>
/// <param name="sourceFolderPath">The source folder path.</param>
/// <param name="zipFile">The zip file.</param>
public static void Compress(string sourceFolderPath, string zipFile)
{
if (!Directory.Exists(sourceFolderPath))
throw new DirectoryNotFoundException();
if (!File.Exists(zipFile))
File.Create(zipFile).Dispose();
ShellCopyTo(sourceFolderPath, zipFile);
}
/// <summary>
/// Des the compress.
/// </summary>
/// <param name="zipFile">The zip file.</param>
/// <param name="destinationFolderPath">The destination folder path.</param>
public static void DeCompress(string zipFile, string destinationFolderPath)
{
if (!File.Exists(zipFile))
throw new FileNotFoundException();
if (!Directory.Exists(destinationFolderPath))
Directory.CreateDirectory(destinationFolderPath);
ShellCopyTo(zipFile, destinationFolderPath);
}
#endregion
#region Public Method
/// <summary>
/// Des the compress.
/// </summary>
/// <param name="destinationFolderPath">The destination folder path.</param>
public void DeCompress(string destinationFolderPath)
{
DeCompress(this.FilePath, destinationFolderPath);
}
#endregion
}