↑コマンドライン引数を分解するクラスの分解アルゴリズムの見直し、メソッドの追加・変更を行いました。
主な変更点・仕様は次の通り。
1.分解アルゴリズム(GetNext()メソッド)見直しについて
●前回のはエクスプローラでD&D起動したときに渡されるコマンドライン文字列を解析した結果から、それに見合うようコーディングしたが、
今回はコマンドラインインタプリタ(cmd.exe)の分解手順を「ダブルクォーテーションの外側にあるスペースを引数の区切り文字として扱う」
と仮定し、それに従いコーディングした。
●エクスプローラからのD&D、関連付けによる実行はもちろん、コマンドラインからの
複数のダブルクォーテーションを持つ場合や
スペースが入るフォルダのみダブルクォーテーションで括った場合にも対応。
●lpCurrentPosをBytePtr+添え字による配列アクセスからポインタ扱いとすることで多少コードをスリム化。
●引数間に余分なスペースがあってもOK。
●あと対応が必要といえば、Unicode、DBCS(全角文字)などがありますが、現状で特に問題ないため放置してます。(--);
不具合等あれば対応したいので御報告ください。m(__)m
2.追加・変更されたメソッドについて
●PublicメソッドGetCommandLine(), GetCurrentPos()を追加。詳細は下記のリファレンスを参照してください。
●内部関数(Protectメソッド)を前の2つからConvertToLongFileName()1つに集約。
各メソッドのリファレンスも付けました↓
Sub CommandLineArgumentsIterator(lpStr As BytePtr)
説明:コンストラクタ
引数:lpStr …コマンドライン引数の文字列を指定します。
GetCommandLine()関数(API)で取得したり、PathGetArgs()関数で引数部分のみ取り出したものを指定できます。
Function HasNext() As BOOL
説明:次の引数があるかないかを返します。Iteratorパターン。
戻り値:次の引数があるときはTRUE(=1)、ないときはFALSE(=0)。
Function GetNext() As BytePtr
説明:引数を1つ分離しそれを返します。Iteratorパターン。
実行後、内部変数lpCurrentPosが次の引数の位置に移り、バッファszCurrentArgに分離した引数が格納されます。
戻り値:バッファszCurrentArgへのポインタが返ります。分離した引数をBytePtr文字列として使えます。
Sub Reset()
説明:次の引数の位置をコンストラクタで指定された文字列のアドレス位置に戻します。
Function GetCommandLine() As BytePtr
説明:コンストラクタで指定したコマンドライン引数の文字列を返します。
Function GetCurrentPos() As BytePtr
説明:次に分離されるの引数以下の文字列を返します。
コード:
TypeDef BOOL = Long
'コマンドライン引数を1つずつ切り出すクラス
Class CommandLineArgumentsIterator
lpCmdLineArgs As BytePtr
Protected
lpCurrentPos As BytePtr
szCurrentArg[ELM(MAX_PATH)] As Byte
Sub ConvertToLongFileName(lpStr As BytePtr)
Dim osver As OSVERSIONINFO
osver.dwOSVersionInfoSize=SizeOf(OSVERSIONINFO)
GetVersionEx(osver)
If osver.dwMajorVersion=4 Then 'Windows 95/98/Me、Windows NT4.0
Dim buffer[ELM(MAX_PATH)] As Byte
GetLongPathName(lpStr, buffer, MAX_PATH)
lstrcpy(lpStr, buffer)
End If
End Sub
Public
Sub CommandLineArgumentsIterator(lpStr As BytePtr)
lpCmdLineArgs=lpStr
lpCurrentPos=lpCmdLineArgs
End Sub
Function HasNext() As BOOL
If lpCurrentPos[0]<>NULL Then
HasNext=TRUE
Else
HasNext=FALSE
End If
End Function
Function GetNext() As BytePtr
Dim i=0 As Long
'引数ひとつ分を抽出する
While (lpCurrentPos[0]<>Asc(" ") And lpCurrentPos[0]<>NULL And i<MAX_PATH-1)
If lpCurrentPos[0]=Asc(Ex"\q") Then 'ダブルクォーテーションの内側
'ダブルクォーテーションを読み飛ばす
lpCurrentPos=lpCurrentPos+1
'次のダブルクォーテーションまで読み込む
While (lpCurrentPos[0]<>Asc(Ex"\q") And lpCurrentPos[0]<>NULL And i<MAX_PATH-1)
szCurrentArg=lpCurrentPos[0]
i=i+1: lpCurrentPos=lpCurrentPos+1
Wend
If lpCurrentPos[0]=Asc(Ex"\q") Then lpCurrentPos=lpCurrentPos+1
Else 'ダブルクォーテーションの外側
szCurrentArg=lpCurrentPos[0]
i=i+1: lpCurrentPos=lpCurrentPos+1
End If
Wend
szCurrentArg=NULL
'長いファイル名に変換する
ConvertToLongFileName(szCurrentArg)
'次の引数の位置を格納(余分なスペースを読み飛ばす)
While lpCurrentPos[0]=Asc(" ")
lpCurrentPos=lpCurrentPos+1
Wend
GetNext=szCurrentArg
End Function
Sub Reset()
lpCurrentPos=lpCmdLineArgs
End Sub
Function GetCommandLine() As BytePtr
GetCommandLine=lpCmdLineArgs
End Function
Function GetCurrentPos() As BytePtr
GetCurrentPos=lpCurrentPos
End Function
End Class
使用例:
こちらもただ使うだけから引数をまとめて表示するようグレードアップしました。
PathGetArgsを使い引数部分のみ表示するようにしています。
コード:
Declare Function GetLongPathName Lib "kernel32" Alias "GetLongPathNameA" (lpszShortPath As BytePtr, lpszLongPath As BytePtr, cchBuffer As DWord) As DWord
Declare Function PathGetArgs Lib "shlwapi" Alias "PathGetArgsA" (psz As BytePtr) As BytePtr
Dim arg As CommandLineArgumentsIterator(PathGetArgs(GetCommandLine()))
Dim str As String
Dim buf As String
Dim i As Long
If arg.HasNext() Then
i=1
Else
MessageBox(0, "ファイルをドロップしてください。", "CommandLineArgumentsIterator", MB_OK)
ExitProcess(0)
End If
While arg.HasNext()
buf=Str$(i) + "個目:" + arg.GetNext() + Ex"\n"
str=str+buf
i=i+1
Wend
MessageBox(0, str, "CommandLineArgumentsIterator", MB_OK)
ExitProcess(0)