3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Delphi】コマンドラインパラメータを独自に処理する (Windows)

3
Last updated at Posted at 2026-06-04

はじめに

Delphi でコマンドラインパラメータを独自に処理してみます。

コマンドラインパラメータの処理

Delphi でアプリケーションに渡されたコマンドラインパラメータを処理するには次のルーチンを使います。

メンバ 説明
CmdLine アプリケーションの呼び出し時に指定されたコマンドライン引数を示します。
ParamCount コマンドラインで渡されたパラメータの数を返します。
ParamStr 指定されたパラメータをコマンドラインから取得して返します。
FindCmdLineSwitch 文字列がアプリケーションのコマンドライン引数として渡されたかどうかを判定します。

ParamStr(0) には実行された EXE のフルパス名が入ります。

See also:

Delphi におけるコマンドラインパラメータ処理の問題点

しかしながら、コマンドラインパラメータ処理でたまに問題が起きる事があります。

test.dpr
program test;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, WinApi.Windows;

begin
  var RawCmd := GetCommandLine;
  Writeln('Raw: ', RawCmd);
  Writeln('ParamCount: ', ParamCount);
  for var i:=1 to ParamCount do
    Writeln('ParamStr(', i, '): ', ParamStr(i));
end.

実際に渡されたパラメータ文字列と、Delphi が処理したパラメータを表示するコンソールアプリケーションで確認してみます。

問題点 1: ダブルクォーテーションの処理

文字としてのダブルクォーテーションを渡す方法がありません。test "abc"def" を実行するとこうなります。

Raw: test "abc"def"
ParamCount: 1
ParamStr(1): abcdef

ダブルクォーテーションは 2 つ重ねなきゃ!」と思われたでしょう?

Raw: test "abc""def"
ParamCount: 1
ParamStr(1): abcdef

そういう問題ではないのです。もちろん \ でエスケープしてもダメなのです。

Raw: test "abc\"def"
ParamCount: 1
ParamStr(1): abc\def

問題点 2: 空文字列

空文字列パラメータが無視されてしまいます。test "" を実行するとこうなります。

Raw: test ""
ParamCount: 0

コマンドラインパラメータを独自に処理する

コマンドラインパラメータを独自に処理する TCmdLine レコードを作ってみました。

uCmdLine.pas
unit uCmdLine;

interface

uses
  System.SysUtils, System.Generics.Collections, Winapi.Windows;

type
  TCmdLine =
  record
    RawCmd: string;
    Data: array of string;
  public
    procedure Init;
    function ParamStr(Idx: Integer): string;
    function ParamCount: Integer;
  end;

implementation

{ TCmdLine }

procedure TCmdLine.Init;
  function SplitCommandLine(const S: string): TArray<string>;
  begin
    var L := TList<string>.Create;
    try
      var i := 1;
      var SLen := S.Length;
      while i <= SLen do
      begin
        while (i <= SLen) and (S[i] = ' ') do
          Inc(i);
        if i > SLen then
          Break;
        var Cur := '';
        var InQuote := False;
        while i <= SLen do
        begin
          if (S[i] = '"') then
          begin
            if InQuote and (i < SLen) and (S[i + 1] = '"') then
            begin
              Cur := Cur + '"';
              Inc(i);
            end
            else
              InQuote := not InQuote;
          end
          else if (S[i] = ' ') and not InQuote then
            Break
          else
            Cur := Cur + S[i];
          Inc(i);
        end;
        L.Add(Cur);
      end;
      Result := L.ToArray;
    finally
      L.Free;
    end;
  end;
begin
  RawCmd := Trim(GetCommandLine);
  Data := SplitCommandLine(RawCmd);
  Data[0] := System.ParamStr(0);
end;

function TCmdLine.ParamCount: Integer;
begin
  Result := Length(Data) - 1;
end;

function TCmdLine.ParamStr(Idx: Integer): string;
begin
  result := Data[Idx];
end;

end.

使い方は以下のようになります。コマンドラインサポートメンバを使った時とほぼ変わりませんね。

program Test;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, uCmdLine;

begin
  var CmdLine: TCmdLine;
  CmdLine.Init;
  Writeln('Raw: ', CmdLine.RawCmd);
  Writeln('ParamCount: ', CmdLine.ParamCount);
  for var i:=1 to CmdLine.ParamCount do
    Writeln('ParamStr(', i, '): ', CmdLine.ParamStr(i));
end.

ダブルクォーテーションの処理も、

Raw: test "abc""def"
ParamCount: 1
ParamStr(1): abc"def

空文字列パラメータも大丈夫です。

Raw: test ""
ParamCount: 1
ParamStr(1):

気になったので System.ParamCount()System.ParamStr() の実装を眺めてみたのですが、TCmdLine の実装とは結構違っていました。

おわりに

多くの場合、標準のルーチンで事足りるのですが、たまにこうやって独自に処理を行わなければならない事があります。

See also:

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?