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