Windows APIのメモ。
CommandLineToArgW。
CommandLineToArgW APIはコマンドライン引数を分割してくれる便利なAPIです。
引数は半角スペースとタブで分割されます。
引数を二重引用符で囲うことで引数に半角スペースを含めることもできます。
CommandLineToArgW APIは二重引用符で区切られた引数の場合、引数の最後の文字が「\」であるとおかしな動作になってしまうことがあります。
具体的には「\」が消えて「"」が残ります。
おそらく「\」や「"」は特殊な扱いになっていて、「\」が次の「"」をエスケープして引数の区切りとしてではなく引数内の文字だということになっているのだと思われます。
その結果二重引用符で囲んだ引数が期待通りに分割されなくなってしまいます。
ただ、最初の分割は期待通りに「\」が最後について分割されます。
それ以降の分割がおかしくなります。
また二重引用符で囲わなければ期待通りに「\」が最後について分割されます。
ちょっとややこしい動作になっています。
そのため引数の中の「\"」をStringReplace関数で全部「\\"」に変換するという手は使えません。
エクスプローラーからフォルダをドロップした場合は終わりの「\」はつかないのでそれほど問題にならないと思いますが、ショートカットファイルの引数を自前で書く場合や半角スペース区切りのデータを分割するのにこのAPIを利用する場合などには気をつける必要があります。
上記のような引数の最後の文字が「\」である場合にも対処したければ分割する関数を自作することになると思います。
function gfniStrCmdLineDiv(const sWStr: WideString; AList: TStrings): Integer;
//sWStrを半角スペースもしくはタブで区切ってAListにセットし、引数の数を返す関数。
//返す値はParamCountに合わせてリストの数-1。
const
lcs_WQUOT = WideString('"');
var
i: Integer;
ls_WStr, ls_WChar: WideString;
lb_Quot, lb_Skip: Boolean;
begin
AList.Clear;
lb_Quot := False;
lb_Skip := False;
ls_WStr := '';
for i := 1 to Length(sWStr) do
begin
ls_WChar := sWStr[i];
if (ls_WChar = lcs_WQUOT) then
begin
if not(lb_Quot) then
begin
//最初にみつかった引用符は区切り
lb_Quot := True;
if (ls_WStr <> '') then
begin
AList.Add(ls_WStr);
ls_WStr := '';
end;
end else
if (lb_Skip) then
begin
//引用符内引用符
lb_Skip := False;
ls_WStr := ls_WStr + ls_WChar;
end else
if (i < Length(sWStr)) and (sWStr[i +1] = lcs_WQUOT) then
begin
//この引用符のすぐ後にも引用符があるので引用符内引用符であるので引数の取り出しにはならない
//例) "引用符を含めるには""とする"
lb_Skip := True;
end else
if (lb_Quot) then
begin
//引用符の閉じ⇒引数取り出し
lb_Quot := False;
// if (ls_WStr <> '') then begin
//空文字も返すためコメントアウト。
AList.Add(ls_WStr);
ls_WStr := '';
// end;
end;
end else
if (ls_WChar = WideString(' ')) or (ls_WChar = WideString(#9)) then
begin
//引数の区切り
if (lb_Quot) then
begin
//引用符つき引数であるので引数の区切りではない⇒引数の文字列に加える
ls_WStr := ls_WStr + ls_WChar;
end else
begin
//引用符つき引数ではないので引数の区切り⇒引数の取り出し
if (ls_WStr <> '') then
begin
AList.Add(ls_WStr);
ls_WStr := '';
end;
end;
end else
begin
ls_WStr := ls_WStr + ls_WChar;
end;
end;
if (ls_WStr <> '') then
begin
AList.Add(ls_WStr);
end;
Result := AList.Count-1;
end;
ParamStrとまったく同じ要領で分割されるわけではありません。
大まかには一緒なのですが二重引用符をイレギュラーな書き方で囲んだ場合の処理がParamStrと違います。
ABC DEF | 'ABC' 'DEF' |
|
"ABC DEF" | 'ABC DEF' | |
"ABC""DEF" | 'ABC"DEF' | 間の""は区切りではなく引用符中に引用符を入れるため二つ続けているもの。 |
"ABC" "DEF" | 'ABC' 'DEF' |
上記の場合と違い、CとDの間の"と"の間にスペースがあるので区切り。 |
ABC""DEF | 'ABC' '' 'DEF' |
間の""は区切りとみなすので ABC"DEF とはならない。 空文字も一つの引数として返す。 |
ABC "" DEF | 'ABC' '' 'DEF' |
上と同じ。 こちらの方が分かりやすい書き方。 |
ABC"""DEF | 'ABC' '"DEF' |
一つ目の"は区切り。 二つ目と三つ目の"で引用符内の引用符。 この例の場合二つ目の引数は最後がきちんと"で終了していない。 'ABC"""DEF"' か 'ABC """DEF"' が最後がきちんと終了している例。 |
ABC""""DEF | 'ABC' '"' 'DEF' |
間の""""は引用符でかこまれた引用符ということ。 |
ABC """" DEF | 'ABC' '"' 'DEF' |
上と同じ。 こちらの方が分かりやすい書き方。 |