「Unlha32.dll」を使用した書庫の作成
次は、圧縮の方法です。ここでは、新規に書庫ファイルを作成するものとします。
手順
- 作成する書庫と同じ名前のファイルが存在しないことを確認する。
- 「Unlha32.dll」の存在をWin32 APIのSearchPath関数で確認する。
- 「Unlha32.dll」が実際にインストールされているか確認するために、UnlhaGetVersion関数を呼び出す。
- UnlhaGetRunning関数により、「Unlha32.dll」が実行されていないことを確認する。
- Unlha関数に渡すコマンドラインを作成する。
Unlha関数により書庫を作成する場合は、コマンドとして「a」または「u」を使います。両者の違いは、書庫にファイルを追加する時に強制的に上書きするか、更新日時を確認するかです。よって、新規作成する場合は、どちらでもかまいません。下のコードではそれ以外に、パス情報を記録し、フォルダも書庫に格納するための「-d1」スイッチと、読み込み許可で開けなかったファイルをエラーとして処理する「-jso1」スイッチを付けています(コマンドやスイッチについて詳しくは、DLLのドキュメントをご覧ください)。- Unlha関数を呼び出して書庫を作成する。
- 正しく書庫が作成されたか確認する。
コード
以下に「Unlha32.dll」を使って書庫を作成するコードを紹介します。このコードも簡略化されていますので、サンプルの「LhaCompressor_Console」プロジェクトも併せてご覧ください。
using System; using System.Text; using System.IO; using System.Runtime.InteropServices; class Program { //DLL の版の取得 [DllImport("unlha32", EntryPoint = "UnlhaGetVersion", ExactSpelling = true)] private extern static ushort UnlhaGetVersion(); //DLL の実行状況の取得 [DllImport("unlha32", EntryPoint = "UnlhaGetRunning", ExactSpelling = true)] private extern static bool UnlhaGetRunning(); //書庫操作一般 [DllImport("unlha32", EntryPoint = "Unlha", CharSet = CharSet.Ansi, ExactSpelling = true)] private extern static int Unlha( IntPtr _hwnd, [MarshalAs(UnmanagedType.LPStr)]string _szCmdLine, [MarshalAs(UnmanagedType.LPStr)]StringBuilder _szOutput, uint _dwSize); //ファイルを検索 [DllImport("kernel32", EntryPoint = "SearchPath", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = false)] private static extern uint SearchPath( [MarshalAs(UnmanagedType.LPTStr)]string lpPath, [MarshalAs(UnmanagedType.LPTStr)]string lpFileName, [MarshalAs(UnmanagedType.LPTStr)]string lpExtension, uint nBufferLength, [MarshalAs(UnmanagedType.LPTStr)]StringBuilder lpBuffer, out IntPtr lpFilePart); /// <summary> /// UNLHA32.DLLで書庫を作成する /// </summary> /// <param name="compressFiles">圧縮するファイルのパス</param> /// <param name="createArchive">作成するファイル名</param> /// <param name="baseDir">基準ディレクトリ</param> public static void LhaCompressFiles( string[] compressFiles, string createArchive, string baseDir) { //作成する書庫と同じ名前のファイルが無いか確認 if (File.Exists(createArchive)) throw new ApplicationException( "ファイル" + createArchive + "が既に存在しています。"); //DLLの存在を確認 IntPtr pathPtr; if (SearchPath(null, "unlha32.dll", null, 0, null, out pathPtr) == 0) throw new ApplicationException( "UNLHA32.DLLが見つかりません。"); //DLLのチェック try { ushort ver = UnlhaGetVersion(); Console.WriteLine("UNLHA32.DLLのバージョン:{0:0.00}", ver / 100f); } catch { throw new ApplicationException( "UNLHA32.DLLがインストールされていません。"); } //動作中かチェック if (UnlhaGetRunning()) throw new ApplicationException( "UNLHA32.DLLが現在動作中です。"); //基準ディレクトリを修正 if (!string.IsNullOrEmpty(baseDir) && !baseDir.EndsWith("\\")) baseDir += "\\"; //コマンドラインとする圧縮するファイル名のリストを取得 string compressFilePaths = ""; foreach (string f in compressFiles) { string filePath = f; //相対パスとする if (!string.IsNullOrEmpty(baseDir)) { if (f.StartsWith(baseDir)) { filePath = f.Substring(baseDir.Length); } else { throw new ApplicationException( "指定されたファイル'" + f + "'は基準ディレクトリ'" + baseDir + "'の下にありません。"); } } if (File.Exists(f)) { //ファイルのとき compressFilePaths += "\"" + filePath + "\" "; } else if (Directory.Exists(f)) { //フォルダのときは、 //すべてのファイルを表すワイルドカードを追加 compressFilePaths += "\"" + Path.Combine(filePath, "*") + "\" "; } else { //ファイルが存在しないとき throw new ApplicationException( "指定されたファイル'" + f + "'が存在しません。"); } } compressFilePaths = compressFilePaths.Trim(); //基準ディレクトリを付加する if (!string.IsNullOrEmpty(baseDir)) compressFilePaths = "\"" + baseDir + "\" " + compressFilePaths; compressFilePaths = "\"" + baseDir + "\" " + compressFilePaths; //ファイル名を修正 createArchive = "\"" + createArchive + "\""; StringBuilder output = new StringBuilder(1024); //圧縮する int ret = Unlha(IntPtr.Zero, string.Format("a -d1 -jso1 {0} {1}", createArchive, compressFilePaths), output, (uint)output.Capacity); //結果 if (ret != 0) throw new ApplicationException( "書庫の作成に失敗しました。"); Console.WriteLine(output.ToString()); } static void Main(string[] args) { //圧縮 //作成する書庫名を決定 string archive = Path.Combine( System.Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), Path.GetFileNameWithoutExtension(args[0]) + ".lzh"); //基準ディレクトリを決定 string baseDir = Path.GetDirectoryName(args[0]); //圧縮 try { LhaCompressFiles(args, archive, baseDir); Console.WriteLine("書庫'{0}'を作成しました。", archive); } catch (Exception ex) { Console.WriteLine("書庫の作成に失敗しました。"); Console.WriteLine("ERROR:" + ex.Message); } Console.ReadLine(); } }
Imports System Imports System.Text Imports System.IO Imports System.Runtime.InteropServices Class Program 'DLL の版の取得 <DllImport("unlha32", _ EntryPoint:="UnlhaGetVersion", _ ExactSpelling:=True)> _ Private Shared Function UnlhaGetVersion() As UShort End Function 'DLL の実行状況の取得 <DllImport("unlha32", _ EntryPoint:="UnlhaGetRunning", _ ExactSpelling:=True)> _ Private Shared Function UnlhaGetRunning() As Boolean End Function '書庫操作一般 <DllImport("unlha32", _ EntryPoint:="Unlha", _ CharSet:=CharSet.Ansi, _ ExactSpelling:=True)> _ Private Shared Function Unlha( _ ByVal _hwnd As IntPtr, _ <MarshalAs(UnmanagedType.LPStr)> ByVal _szCmdLine As String, _ <MarshalAs(UnmanagedType.LPStr)> ByVal _szOutput As StringBuilder, _ ByVal _dwSize As UInteger) As Integer End Function 'ファイルを検索 <DllImport("kernel32", _ EntryPoint:="SearchPath", _ SetLastError:=True, _ CharSet:=CharSet.Auto, _ ExactSpelling:=False)> _ Private Shared Function SearchPath( _ <MarshalAs(UnmanagedType.LPTStr)> ByVal lpPath As String, _ <MarshalAs(UnmanagedType.LPTStr)> ByVal lpFileName As String, _ <MarshalAs(UnmanagedType.LPTStr)> ByVal lpExtension As String, _ ByVal nBufferLength As UInteger, _ <MarshalAs(UnmanagedType.LPTStr)> ByVal lpBuffer As StringBuilder, _ ByRef lpFilePart As IntPtr) As UInteger End Function ''' <summary> ''' UNLHA32.DLLで書庫を作成する ''' </summary> ''' <param name="compressFiles">圧縮するファイルのパス</param> ''' <param name="createArchive">作成するファイル名</param> ''' <param name="baseDir">基準ディレクトリ</param> Public Shared Sub LhaCompressFiles( _ ByVal compressFiles() As String, _ ByVal createArchive As String, _ ByVal baseDir As String) '作成する書庫と同じ名前のファイルが無いか確認 If File.Exists(createArchive) Then Throw New ApplicationException( _ "ファイル" + createArchive + "が既に存在しています。") End If 'DLLの存在を確認 Dim pathPtr As IntPtr If SearchPath(Nothing, "unlha32.dll", Nothing, 0, _ Nothing, pathPtr) = 0 Then Throw New ApplicationException("UNLHA32.DLLが見つかりません。") End If 'DLLのチェック Try Dim ver As UShort = UnlhaGetVersion() Console.WriteLine("UNLHA32.DLLのバージョン:{0}", ver) Catch Throw New ApplicationException( _ "UNLHA32.DLLがインストールされていません。") End Try '動作中かチェック If UnlhaGetRunning() Then Throw New ApplicationException("UNLHA32.DLLが現在動作中です。") End If '基準ディレクトリを修正 If Not String.IsNullOrEmpty(baseDir) AndAlso _ Not baseDir.EndsWith("\") Then baseDir += "\" End If 'コマンドラインとする圧縮するファイル名のリストを取得 Dim compressFilePaths As String = "" For Each f As String In compressFiles Dim filePath As String '相対パスとする If Not String.IsNullOrEmpty(baseDir) Then If f.StartsWith(baseDir) Then filePath = f.Substring(baseDir.Length) Else Throw New ApplicationException( _ "指定されたファイル'" + f + "'は基準ディレクトリ'" + _ baseDir + "'の下にありません。") End If End If If File.Exists(f) Then 'ファイルのとき compressFilePaths += """" + filePath + """ " Else If Directory.Exists(f) Then 'フォルダのときは、 'すべてのファイルを表すワイルドカードを追加 compressFilePaths += _ """" + Path.Combine(filePath, "*") + """ " Else 'ファイルが存在しないとき Throw New ApplicationException( _ "指定されたファイル'" + f + "'が存在しません。") End If End If Next f compressFilePaths = compressFilePaths.Trim() '基準ディレクトリを付加する If Not String.IsNullOrEmpty(baseDir) Then compressFilePaths = """" + baseDir + """ " + compressFilePaths End If 'ファイル名を修正 createArchive = """" + createArchive + """" Dim output As New StringBuilder(1024) '圧縮する Dim ret As Integer = Unlha( _ IntPtr.Zero, _ String.Format("a -d1 -jso1 {0} {1}", _ createArchive, compressFilePaths), _ output, _ CType(output.Capacity, UInteger)) '結果 If ret <> 0 Then Throw New ApplicationException("書庫の作成に失敗しました。") End If Console.WriteLine(output.ToString()) End Sub Shared Sub Main(ByVal args() As String) '圧縮 '作成する書庫名を決定 Dim archive As String = Path.Combine( _ System.Environment.GetFolderPath( _ Environment.SpecialFolder.DesktopDirectory), _ Path.GetFileNameWithoutExtension(args(0)) + ".lzh") '基準ディレクトリを決定 Dim baseDir As String = Path.GetDirectoryName(args(0)) '圧縮 Try LhaCompressFiles(args, archive, baseDir) Console.WriteLine("書庫'{0}'を作成しました。", archive) Catch ex As Exception Console.WriteLine("書庫の作成に失敗しました。") Console.WriteLine(("ERROR:" + ex.Message)) End Try Console.ReadLine() End Sub End Class
レスポンスファイル
「Unlha32.dll」では、コマンドラインに指定できるパラメータの最大数が2048までとなっています。よって、この数以上のファイルをコマンドラインに指定することはできません。もしこれ以上の数を指定したいのであれば、レスポンスファイルを使わなければなりません。
レスポンスファイルは、コマンドラインの一部(またはすべて)を記述したテキストファイルです。通常はShift JISで、各パラメータを改行記号で区切って記述します。レスポンスファイルをUnlha関数のコマンドラインに指定するには、レスポンスファイル名の前に「@」を付けます(ただし、「Unarj32.dll」は「!」、「Unimp32.dll」は「:」となります)。
常識的には、これだけ多くのファイルを一度に指定することはなさそうですが、「Unlha32.dll」のドキュメント「NOTE.TXT」には次のように書かれています。「基本的に、対応アプリとしては、渡すファイルの数に関係なくレスポンスファイルを介してファイルのリストを渡すようにしておくのが得策です」。また、「Cab32.dll」などは最大パラメータ数が256と少なめですので、油断はできません。
私の調べた限りでは、ほとんどの統合アーカイバDLLがレスポンスファイルに対応しています。ただし、レスポンスファイル内でファイル名以外のコマンドやスイッチを記述できるか、あるいはファイル名しか記述できないかという点においては、統一されていません。以下に私が調べた結果をまとめておきます。
- レスポンスファイルにファイル名以外のコマンドやスイッチを記述できるDLL
- レスポンスファイルにファイル名しか記述できない(リストファイルしか指定できない)DLL
- レスポンスファイルにファイル名を記述できることを実際に確認したDLL
- レスポンスファイルに対応していないDLL
この結果から、レスポンスファイルとしてファイル名のリストを使うのが無難かと思われます。
レスポンスファイルの記述法もそれぞれ細かい点で異なるようですが、以下の点はほぼ共通のようです。
- Shift JISで記述する。
- コマンド(ファイル名)を改行文字(CRLF)で区切る。
- ファイル名にスペース文字が含まれる時は、「"」で括る(ただし、「Unhki32.dll」は例外。「Unhki32.dll」では、「"」で括られたファイル名は無視されてしまう)。
- レスポンスファイルのネスト(レスポンスファイル内でレスポンスファイルを指定すること)はできない。よって、レスポンスファイル内では「@」で始まるファイル名はそのまま記述できる。
サンプルの「LhaCompressor_Console」プロジェクトでは、圧縮するファイルの数が多い時は、レスポンスファイルを使用するようにしています。
