using System; using System.Runtime.InteropServices; using System.IO; namespace NaGet.InteropServices { /// /// CommonArchiverDllの設定 /// public struct CommonArchiverDllConfig { public CommonArchiverDllConfig(string dllName, string cmdName, string cmdLineFmt, int versionreq) { DllName = dllName; CmdName = cmdName; CmdLineFmt = cmdLineFmt; VersionMoreThan = versionreq; } /// /// DLLの名称。LoadLibraryに渡される /// public string DllName; /// /// コマンド名称。 /// public string CmdName; /// /// コマンドライン形式"{0}"がアーカイブ名、"{1}"が展開先 /// public string CmdLineFmt; /// /// 最低バージョン要求 /// public int VersionMoreThan; } /// /// アーカイバDLLを使った書庫展開器 /// public class CommonArchiverExtracter { #region DLL関数のdelegate /// /// DLL の版の取得 /// private delegate ushort ArcGetVersion(); /// /// DLL の実行状況の取得 /// private delegate bool ArcGetRunning(); /// /// 書庫のチェック /// private delegate bool ArcCheckArchive(string szFileName, ArchCheckFlag iMode); /// /// 書庫のチェックの精度 /// private enum ArchCheckFlag { /// /// 簡易モード。先頭の数個のヘッダのみ確認 /// RAPID = 0, /// /// 通常モード。すべてのヘッダを確認 /// BASIC = 1, /// /// 厳密モード。CRCも含めすべて確認 /// FULLCRC = 2, //RECOVERY = 4, //SFX = 8, //ALL = 16, } /// /// 格納ファイル数の取得 /// private delegate int ArcGetFileCount(string szFileName); /// /// 書庫操作一般 /// private delegate int ArcMain(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string szCmdLine, [MarshalAs(UnmanagedType.LPStr)] System.Text.StringBuilder szOutput, uint dwSize); #endregion /// /// アーカイバDLLの設定 /// public static readonly CommonArchiverDllConfig[] Configs = { new CommonArchiverDllConfig("7-ZIP32", "SevenZip", "x -y \"{0}\" \"-o{1}\"", 423), new CommonArchiverDllConfig("UNZIP32", "UnZip", "-x -o \"{0}\" \"{1}\" *", 541), new CommonArchiverDllConfig("UNLHA32", "Unlha", "x \"{0}\" \"{1}\" *", 240), new CommonArchiverDllConfig("CAB32", "Cab", "-x \"{0}\" \"{1}\" *", 98), new CommonArchiverDllConfig("TAR32", "Tar", "-x \"{0}\" -o \"{1}\"", 218), new CommonArchiverDllConfig("UNGCA32", "UnGCA", "e \"{0}\" \"{1}\"", 10), // いるかな? //new CommonArchiverDllConfig("UNRAR32", "Unrar", "x -y \"{0}\" \"{1}\" *", -1), }; /// /// アーカイブを展開する。適切なDLLで自動的に展開する。 /// /// アーカイブのパス /// 展開先ディレクトリ /// アーカイバDLLの展開時の標準出力を格納する /// 親フレーム /// アーカイバDLLの展開時のエラーコード(0なら正常終了) public static int ExtractArchive(string arcFile, string targetDir, System.Text.StringBuilder output, IntPtr hWnd) { foreach (CommonArchiverDllConfig config in Configs) { try { return ExtractArchiveWith(arcFile, targetDir, config, output, hWnd); } catch (DllNotFoundException) { } catch (ApplicationException) { } } throw new DllNotFoundException("Not found dll matched for " + arcFile); } /// /// 指定したDLLの設定を使ってアーカイブを展開する /// /// アーカイブのパス /// 展開先ディレクトリ /// 使用するアーカイバDLLの設定 /// アーカイバDLLの展開時の標準出力を格納する /// 親フレーム /// アーカイバDLLの展開時のエラーコード(0なら正常終了) public static int ExtractArchiveWith(string arcFile, string targetDir, CommonArchiverDllConfig config, System.Text.StringBuilder output, IntPtr hWnd) { if (! File.Exists(arcFile) ) { throw new FileNotFoundException("File not found: ", arcFile); } if (! Directory.Exists(targetDir)) { throw new DirectoryNotFoundException("Directory not found: " + targetDir); } // 統合アーカイバDLLの解凍先ディレクトリは\で終わらないといけない(重要) if (! targetDir.EndsWith("\\")) { targetDir += "\\"; } // DLLバージョンチェック using (DllAccess dll = new DllAccess(config.DllName)) { ArcGetVersion GetVersion = (ArcGetVersion) dll.GetFunction(config.CmdName+"GetVersion", typeof(ArcGetVersion)); ushort version = GetVersion(); if (version < config.VersionMoreThan) { throw new DllNotFoundException("Dll version is too old."); } } string cmdLine = string.Format(config.CmdLineFmt, arcFile, targetDir); return ExtractArchiveWith(config.DllName, config.CmdName, cmdLine, arcFile, output, hWnd); } protected static int ExtractArchiveWith(string dllName, string cmdName, string cmdLine, string arcFile, System.Text.StringBuilder output, IntPtr hWnd) { using (DllAccess dll = new DllAccess(dllName)) { ArcGetRunning GetRunning = (ArcGetRunning) dll.GetFunction(cmdName+"GetRunning", typeof(ArcGetRunning)); if ( GetRunning() ) { // マルチスレッド対応でないのでビジー時には例外 throw new SystemException(dllName + " is busy"); } ArcCheckArchive CheckArchive = (ArcCheckArchive) dll.GetFunction(cmdName+"CheckArchive", typeof(ArcCheckArchive)); if (! CheckArchive(arcFile, ArchCheckFlag.BASIC)) { throw new ApplicationException(dllName + " could not treat the archive file"); } ArcMain Exec = (ArcMain) dll.GetFunction(cmdName, typeof(ArcMain)); int retVal = Exec(hWnd, cmdLine, output, (uint)((output != null)? output.Capacity : 0) ); return retVal; } } } }