Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2005年02月15日] [更新 2022年03月15日] Delphi サンプルプログラム集
510_コンソールアプリケーションの作成
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
後半は Windows 10 ビルド 19042 + Delphi XE5(UP2) Pro VCL-32
510_Console1.zip [2.2 MB] 2022年03月15日版 (EXE 同梱)







コンソールアプリケーションの作成


コンソールアプリケーションとは、コマンドプロンプト上で動作するテキストベースのアプリケーションです。つまり文字だけを扱うアプリケーションです。コンソールアプリケーションの座標は文字単位で行と列を指定します。
このコンソール画面上でキーボードから入力したり、処理の結果をコンソール画面に表示するアプリケーションです。

Windows が登場する前のパソコンと呼ばれた OS の主流は MS-DOS (PC-DOS) でした。つまり、コンソールアプリケーションでした。これにグラフィクス機能のインターフェイスが用意されていて、多くのプログラマーはこの機能を使用する関数をアセンブラで作成していました。当サイトの [PC-9801] の各ページにあるプログラムもそのようにして作成したものです。

ウィンドウズアプリケーション全盛の時代になった今でも、システムの設定関係のアプリケーションでは、コンソールアプリケーションを使用しているものがあります。また、ホストコンピュータの端末の OS は Windows になっても、従来の業務アプリケーションを、コンソールアプリケーションとして動作させている場合もあります。
Delphi におけるコンソールアプリケーションの記事は、ネットで検索すると、かなり多くあります。プログラミングの基本として解説しているサイトもあります。
このページのサンプルの一部に「ゼロから始める Delphi 講座 14」の記事中のコードを利用させていただきました。



[備考 1]
コンソール画面を非表示にしたいという質問があります。コンソール画面を表示する必要がなければ、わさわざコンソールアプリにする必要はありません。
フォームを使用しないアプリ、あるいはフォームを表示しないアプリにすれば、何も表示しないで何かを実行することができます。


[備考 2]
コマンドプロンプトとコンソールアプリは、外観は似ていますが、その機能と用途は全く違います。





01_コンソールアプリケーション - 新規作成手順


コンソールアプリケーションのプロジェクトの新規作成手順です。
IDE のメニューの、[ファイル][新規作成]を選択します。現れたダイアログの [Delphiプロジェクト] の中から、[コンソールアプリケーション] を選択して [OK] ボタンをクリックします。

もちろん、別にユニットを作成し、それを uses 部に追加して使用することもできます。あるいは作成済みのユニットを [プロジェクト] [プロジェクトに追加] で追加することもできます。


図1
新規作成手順

下に示すのが作成したプロジェクトのソースコードです。ただし、例外処理の部分は全て削除しています。これ以降のサンプルも同様に例外処理のコードを省略しています。

program Project1 の Project1 がこのプロジェクトの名前で、アプリケーション (EXE) の名前となります。 {$APPTYPE CONSOLE} はこのプロジェクトがコンソールアプリであることを意味します。
このプロジェクトを [F9] でコンパイル実行すると、コンソール画面が一瞬だけ表示されますが、すぐに閉じてアプリが終了します。



リスト1
新規作成したプロジェクトのコード
{$apptype console} というコンパイラ指令がある
program Project1;
{$APPTYPE CONSOLE}

uses
  SysUtils;

begin
  try
    { TODO -oUser -cConsole メイン : ここにコードを記述してください }
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


[備考]
上のプロジェクトのソースコードから {$APPTYPE CONSOLE} の行を削除すると、フォームを使用しないアプリケーションとなります。




02_コンソールアプリケーション - 簡単な例


コンソールアプリケーションの簡単な例です。

コンソールウィンドウのキャプション (タイトルバー) には、起動した EXE のフルパスが表示されています。起動 EXE のフルパスは、VCL フォームアブのケーションと同じように、ParamStr(0) または Application.ExeName で取得できますが、コード内で Application 変数を使用するには uses に Forms が必要です。



図2
実行結果
  • 何かキーを押すると終了

リスト2
コンソールアプリケーションの簡単な例
何かキーを押すと終了してコンソールが閉じる
program ConsoleApp_Test;
{$APPTYPE CONSOLE}

uses
  SysUtils;

begin
  Writeln('Hello World !! ');
  Readln;
end.


上のコードでは「何かのキー押すと」終了しました。特定のキーを押したら終了するよにするには、例えば、以下のコードのように、キーの種類を指定します。
このサンプルでは、大文字の A を押すと終了します。


リスト3
大文字のAを押すと終了する例
program ConsoleApp_Test;
{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  S : String;
begin
  Writeln('Hello World !! ');
  while True do begin
    Readln(S);
    //コンソール上で文字'A'を入力したら終了
    if S = 'A' then exit;
  end;
end.




03_コンソールアプリケーション - 関数の定義位置


関数類を定義する際は、必ず begin~end. よりも前に記述しなければなりません。また、複数の関数を定義する場合、使用する関数は、その関数を使用する関数よりも前に書かれている必要があります。これは、[830_フック等のコールバック関数の記述位置] と同じです。

コンソールアプリも Delphi のプログラムです。クラスを定義して利用することもできます。クラスのメンバーとして定義した関数やメソッドの記述順番は、普通のクラス内のそれと同じに扱えます。

コンソールアプリケーションでクラス型を扱うサンプルは、塚越 一雄さんの著書「Delphi オブジェクト指向プログラミング」に詳しい解説がありますが、現在、この本は絶版となっています。中古本であれば Amazon などで購入できます (2012年01月10日現在)。
この著書は、クラスという概念の理解にも役に立つ内容となっています。


図3
実行結果

リスト4
関数は、begin~end, よりも前に書く
program Console_function;
{$APPTYPE CONSOLE}

uses
  SysUtils;

// 関数の定義。この場合、関数の名前はTestA
procedure TestA;

  // TestAの中でTestBという関数を定義。これを関数内関数と言う
  // 関数内関数は、関数内関数を定義した関数の中でしか使用できない
  procedure TestB;
  begin
    Writeln('TestA - TestB');
  end;

begin
  Writeln('TestA');
  // 下のコメント(//)をはずすとこの上の関数内の関数TestBを実行することになる
  // TestB;
end;

// 関数の定義。この場合、関数の名前はTestB
procedure TestB;
begin
  Writeln('TestB');
end;

//  実行部
begin
  TestB;
  Readln;
end.




04_コンソールアプリケーション - 変数や定数の定義位置


グローバルな定数や変数は、次のコード例のように、必ずその定数や変数を使用するよりも前に定義します。


図4
実行結果

リスト5
グローバルな定数や変数は、それらを使用するよりも前に定義しておく
TEISUU = 200; は、関数 Test 内でのみ有効な定数
program Console_const;
{$APPTYPE CONSOLE}

uses
  SysUtils;

// コード全体で使用する変数や定数も、使用する前に定義されいなければならない
const
  TEISUU = 500;

procedure Test;
const
  TEISUU = 200;
begin
  Writeln(TEISUU);
end;

// 実行部
// Testという関数を実行して、次にTEISUUの値を表示する
begin
  Test;
  Writeln(TEISUU);
  Readln;
end.




05_コンソールアプリケーションのフォントサイズと色


コンソールアプリケーションにおけるカーソル位置の制御、あるいは文字列の色の設定等は、コンソール関係の関数を使用します。
本サンプルは、主に、フォント (文字) のサイズと色を変更する例です。


図5
dwFontSize.Y が 12 の場合
図6
dwFontSize.Y を 22 にした場合

リスト6
コンソールアプリケーションの文字のサイズと文字の色を変更
program Project1;
{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  Messages,
  DWMAPI;

//-----------------------------------------------------------------------------
//  SetCurrentConsoleFontEx関数とこの関数で使用する構造体の定義
//  SetCurrentConsoleFontExはDelphi XE7 までは実装されていない
//  SetCurrentConsoleFontExはWindows Vista以降で使用可能
//-----------------------------------------------------------------------------
type
  PConsoleFontInfoEx = ^TConsoleFontInfoEx;
  _CONSOLE_FONT_INFOEX = record
    cbSize     : ULONG;
    nFont      : DWORD;
    dwFontSize : TCoord;
    FontFamily : UINT;
    FontWeigth : UINT;
    FontName   : array [0..LF_FACESIZE-1] of WideChar;
  end;
  TConsoleFontInfoEx = _CONSOLE_FONT_INFOEX;

  function SetCurrentConsoleFontEx(
            hConsoleOutput : THandle;
            bMaximumWindow : BOOL;
            lpConsoleCurrentFontEx : PConsoleFontInfoEx):
            BOOL; stdcall; external 'kernel32.dll';

//-----------------------------------------------------------------------------
//  Windows Vista以降では、フォームのフレームの仕様が拡張されている
//  これを考慮したウィンドウのTRect構造体の値を取得
//  usesにDWMAPIが必要
//-----------------------------------------------------------------------------
function GetWindowRectForVista(hWindow: HWND): TRect;
var
  LRect : TRect;
begin
  //拡張したウィンドウのフレームのTRect構造体の値を取得
  DwmGetWindowAttribute(hWindow,
                        DWMWA_EXTENDED_FRAME_BOUNDS,
                        @LRect,
                        SizeOf(TRect));
  Result := LRect;
end;
//-----------------------------------------------------------------------------

var
  LConsoleWnd : HWND;
  LhStdout    : THandle;
  LFontInfo   : TConsoleFontInfoEx;
  LBuffSize   : TCoord;
  LWndSize    : TSmallRect;
  LColor      : Word;
  LWndRect    : TRect;
begin
  //ウインドウハンドルを取得
  //LConsoleWnd := Application.Handle;でも同じ
  //Application.Handleを使用する場合はusesにFormsが必要
  LConsoleWnd := FindWindow('ConsoleWindowClass', nil);
  //標準出力デバイスのハンドルを取得
  LhStdout := GetStdHandle(STD_OUTPUT_HANDLE);

  //フォントサイズの設定
  LFontInfo.cbSize       := SizeOf(TConsoleFontInfoEx);
  LFontInfo.dwFontSize.X := 0;
  LFontInfo.dwFontSize.Y := 12;
  LFontInfo.FontFamily   := FF_DONTCARE;
  LFontInfo.FontWeigth   := FW_NORMAL;
  LFontInfo.FontName     := 'MS ゴシック';
  SetCurrentConsoleFontEx(LhStdout, False, @LFontInfo);


  //ウィンドウのサイズ(文字数)
  LWndSize.Left   := 0;
  LWndSize.Top    := 0;
  LWndSize.Right  := 50;
  LWndSize.Bottom := 15;
  SetConsoleWindowInfo(LhStdout, True, LWndSize);


  //画面のバッファのサイズ(文字数)
  LBuffSize.X := LWndSize.Right - LWndSize.Left;
  LBuffSize.Y := LWndSize.Bottom - LWndSize.Top + 50;
  SetConsoleScreenBufferSize(LhStdout, LBuffSize);


  //文字の色を、緑と赤で黄色にする
  LColor := FOREGROUND_GREEN or FOREGROUND_RED or FOREGROUND_INTENSITY;
  SetConsoleTextAttribute(LhStdout, LColor);


  //コンソールウィンドウのサイズ(ピクセル)を取得して位置を設定
  LWndRect := GetWindowRectForVista(LConsoleWnd);
  MoveWindow(LConsoleWnd,
             100,
             0,
             LWndRect.Right  - LWndRect.Left,
             LWndRect.Bottom - LWndRect.Top,
             True);


  //タイトルを設定
  SetConsoleTitle('コンソールのテスト');


  Writeln('ウィンドウの表示位置を変更');
  Writeln('文字の色とサイズも変更');

  LColor := FOREGROUND_GREEN or FOREGROUND_INTENSITY;
  SetConsoleTextAttribute(LhStdout, LColor);
  Writeln('');
  Writeln('文字の色を緑に変更');

  //何かキーを押したら終了
  Readln;
end.




06_コンソールアプリケーションでコマンドを実行


コンソールアプリケーション関係には cmd.exe や Windows のコマンドを実行する機能の関数類はありませんが、通常の Delphi のアプリのように、CreateProcess 関数を使用することができます。
TStartupInfo 構造体の標準入力、標準出力先として、現在の標準入力、標準出力のハンドルを指定して CreateProcess でコマンドを実行すれば、コンソールの画面に結果が表示されます。

下図は cmd.exe の dir コマンドを実行した結果です。コマンドの指定方法は、コンソールアプリケーションだからという特別なことはありません。



図7
実行結果

リスト7
コンソールアプリケーションでコマンドを実行する例
program Project1;
{$APPTYPE CONSOLE}

uses
  ShellAPI,
  Windows;

var
  LCommandLine : string;
  LStartupInfo : TStartupInfo;
  LProcessInfo : TProcessInformation;

begin
  SetConsoleTitle('コマンドの実行テスト');

  writeln('dir コマンドを実行します');
  writeln('何かキーを押してください');
  Readln;

  //CreateProcessの引数の構造体のメンバを設定
  //入出力を標準入出力に設定する
  ZeroMemory(@LStartupInfo, SizeOf(TStartupInfo));
  ZeroMemory(@LProcessInfo, SizeOf(TProcessInformation));
  LStartupInfo.cb          := SizeOf(TStartupInfo);
  LStartupInfo.hStdInput   := GetStdHandle(STD_INPUT_HANDLE);
  LStartupInfo.hStdOutput  := GetStdHandle(STD_OUTPUT_HANDLE);
  LStartupInfo.hStdError   := GetStdHandle(STD_ERROR_HANDLE);
  LStartupInfo.dwFlags     := STARTF_USESTDHANDLES;

  //コマンド実行
  try
    LCommandLine := 'cmd.exe /c dir';
    UniqueString(LCommandLine);
    if not CreateProcess(nil,
                         PChar(LCommandLine),
                         nil,
                         nil,
                         True,
                         0,
                         nil,
                         nil,
                         LStartupInfo,
                         LProcessInfo) then begin
      //CreateProcess失敗
    end;
  finally
    CloseHandle(LProcessInfo.hProcess);
    CloseHandle(LProcessInfo.hThread);
  end;

  Readln;
end.




07_コマンドプロンプトでコンソールアプリケーションを実行


コンソールアプリはコマンドプロンプトで、コマンドとして実行できます。そのサンプルです。上のサンプルプログラムを実行してもいいのですが、簡単なプログラムを作成してみました。
一般的に、コンソールアプリでは、その実行結果を見るために、コンソール画面が閉じないように、サ後に Readln 等を書きます。コマンドプロンプトで実行する場合は、実行後、すぐにコマンドプロンプトの入力待ちの状態にしたいので、Readln は書かない方がいいでしょう。



図8
コマンドプロンプトから実行した結果
図9
コマンドプロンプトから実行してリダイレク先を指定
図10
リダイレクトで出力されたテキストファイルの内容

リスト8
コマンドプロンプトから実行するテストプログラムのコード
program Project1;
{$APPTYPE CONSOLE}

uses
  Windows;

//-----------------------------------------------------------------------------
//  コマンドライン用のプログラムの例
//  必要があれば標準出力等のハンドルを取得して操作するく
//-----------------------------------------------------------------------------
begin
  writeln('');
  writeln('');
  writeln('コマンドアプリのテストです');
  writeln(100);
  writeln(200);
  writeln(300);

  //デバッグ時はコンソール画面を表示したままにする
  //処理が終了したらすぐコマンドプロンプトに戻るためReadln等は書かない
  if DebugHook <> 0 then begin
    Readln;
  end;
end.




08_コンソールアプリケーションのアプリケーションアイコンの設定


コンソールアプリのアプリケーションアイコンは、デフォルトでは標準のコンソールアプリのアイコンになります。この状態ではアプリケーションアイコンは設定されていません。

コンソールアプリのアプリケーションアイコンは [プロジェクト] [オプション] のダイアログからは設定できません。[プロジェクト] [リソースと画像] のダイアログで、下図のようにリソースとして設定します。リソースタイプは ICON とします。
リソース識別子の値は任意です。VCL フォームアプリケーションでは MAINICON という名前が設定されるようになっています。



図11
リソースタイプは ICON
  • 複数追加した場合は、リソース識別子の値が小さいアイコンが有効となる
図12
アイコンを設定して実行した結果

リスト9
コンソールアプリケーションのアプリケーションアイコンの設定
このコードはアプリケーションアイコンの設定とはほとんど関係ないが、{$R *.dres} は、リソースが組み込まれていることを示している
program Project1;
{$APPTYPE CONSOLE}

{$R *.dres}

uses
  Windows,
  SysUtils;

begin
  SetConsoleTitle('アイコンのテスト');
  Writeln('コンソールアプリのアイコンの設定');
  Readln;
end.




09_コンソールアプリケーション全体のプロパティ


コンソールアプリケーションの文字の色やフォントサイズは、実行時に設定できますが、コンソールのプロパティのダイアログからも変更できます。
コンソールウィンドウのシステムメニュー (マウス右ボタンで現れるメニュー) から [プロパティ] のダイアログを表示します。このダイアログで設定します。

コンソールウィンドウのサイズ等の単位は半角換算での文字数です。
これらの設定は、コマンドプロンプトの画面でも、そのコマンドプロンプトのプロパティとして設定できるようになっています。



図13
コンソールのプロパティ
  • キャプションバー (タイトルバー) でマウス右ボタンクリックで表示
  • または [Alt] + [SPACE] キー
  • コマンドプロンプトの状態でも同じ
図14
画面の色の設定ダイアログ
  • 表示タブの移動は [Ctrl] + [TAB]
図15
ウィンドウのサイズ等の設定
  • サイズは文字数単位
  • ウィンドウの位置は画面のドット単位




10_コンソールアプリケーションのイベント


コンソールアプリには、本サンプルのようなイベントが用意されています。イベントは、SetConsoleCtrlHandler 関数でコールバック半数を指定して、そのコールバック関数内で受信します。
本サンプルを実行して、[Ctrl] + [C] または [Alt] + [F4]、あるいは [x] (閉じるボタン) をクリックすると、ダイアログを表示します。ダイアログは自動的に閉じます。[OK] ボタンをクリックする必要はありません。

コンソールアプリケーションは、ウィンドウメッセージを捕捉すことはできません。コンソールアプリケーションのウィンドウはサブクラス化できません。メッセージを捕捉して処理可能なのは、本サンプルで使用している CTRL_XXX_EVENT のイベントだけです。



図16
実行画面

リスト10
コンソールアプリケーションのイベントのテスト
program Project1;
{$APPTYPE CONSOLE}

uses
  Winapi.Windows,
  Winapi.Messages,
  Vcl.Dialogs,
  Vcl.Forms;
               var
  FhConsole : HWND;

//-----------------------------------------------------------------------------
//  SetConsoleCtrlHandler のコールバック関数
//  以下のイベントが使用可能
//  処理の変更 (閉じる処理の阻止等) はできない
//
//  CTRL_C_EVENT        : [Ctrl] + [C] の信号を受信
//  CTRL_BREAK_EVENT    : [Ctrl] + [Break] の信号を受信
//  CTRL_CLOSE_EVENT    : コンソールウィンドウを閉じる時の信号
//  CTRL_LOGOFF_EVENT   : ユーザーがログオフする時の信号
//  CTRL_SHUTDOWN_EVENT : システムのシャットダウン時の信号
//-----------------------------------------------------------------------------
function ConsoleEventProc(CtrlType: DWORD): Bool; stdcall;
var
  LText : string;
begin
  Result := True;
  LText := '';

  case CtrlType of
  CTRL_C_EVENT:
    begin
      LText := '[Ctrl] + [C] のキー操作がありました。';
    end;
  CTRL_CLOSE_EVENT:
    begin
      LText := 'ウィンドウが閉じようとしています';
    end;
  else
  end;

  if LText <> '' then begin
    MessageBox(0, PChar(LText), '情報', MB_ICONINFORMATION);
  end;
end;
//-----------------------------------------------------------------------------

begin
  Writeln('コンソールアプリのイベント');

  // コンソールウィンドウのハンドルを取得
  FhConsole := FindWindow(nil, PChar(Application.ExeName));
  // ウィンドウのキャプション (タイトル) を変更
  SetConsoleTitle('CTRL_XXX_EVENT イベントのテスト');

  // イベントハンドラの設定
  SetConsoleCtrlHandler(@ConsoleEventProc, True);
  SetConsoleMode(FhConsole, ENABLE_PROCESSED_INPUT);

  Readln;
  // ダイアログを見られるようにするためのタイムラグ
  Sleep(3000);
end.




11_[ Ctrl ] + [ C ] を自動実行してコンソールアプリを終了


GenerateConsoleCtrlEvent 関数は、CTRL_XXX_EVENT のイベントを強制的に実行します。この関数の引数を CTRL_C_EVEN にすると、[Ctrl] + [C] を実行したことになり、コンソールアプリが終了します。
GenerateConsoleCtrlEvent 関数は、外部のプログラムから実行しても機能しません。



図17
実行結果

リスト11
GenerateConsoleCtrlEvent 関数を使用してコンソールアプリを強制終了
このコードよりも上のコードは、前項のサンプルと同じ
begin
  Writeln('コンソールアプリのイベント');
  Writeln('5 秒後に自動終了します');

  // コンソールウィンドウのハンドルを取得
  FhConsole := FindWindow(nil, PChar(Application.ExeName));
  // ウィンドウのキャプション (タイトル) を変更
  SetConsoleTitle('CTRL_XXX_EVENT イベントのテスト');

  // イベントハンドラの設定
  SetConsoleCtrlHandler(@ConsoleEventProc, True);
  SetConsoleMode(FhConsole, ENABLE_PROCESSED_INPUT);

  Sleep(5000);
  // ExitProcess ではなく [Ctrl] + [C] で終了してみる
  GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);

  // Readln;
  // ダイアログを見られるようにするためのタイムラグ
  Sleep(3000);
end.