Win32 コンソール API


はじめに

コマンドプロンプト画面(コンソール)は、デフォルトでは黒に白の文字が表示されます。しかし、Win32 コンソール API 関数を使うといろいろな色で文字を表示できます。

この API はハイレベル関数とローレベル関数からなっていて、後者を使用するとコンソール画面の細かな制御が可能です。

参照:MSDN Using the Console

ハイレベル I/O 関数とローレベル I/O 関数

ハイレベル I/O 関数は ReadFile / WriteFile または ReadConsole / WriteConsole です。これらのグループはほぼ同じ動作を行いますが、ReadFile / WriteFile はワイド文字では使用できません。よって、ReadConsole / WriteConsole を使ったほうがよいでしょう。

このため、ReadFile / WriteFile を使う場合は、プロジェクトのプロパティで「全般」・「文字セット」をマルチバイト文字にする必要があります。

ローレベル I/O 関数を使うと、行単位の入出力だけでなく、文字単位の入出力が行えます。カーソルを制御して文字列の表示位置を変えたり、スクロールを制御したりと、様々な細かな処理ができますが、それだけ多くの関数を使いこなす必要があります。

ハイレベルでは CRLF があると自動的に改行しますが、ローレベルではモードによります。

文字列の表示と入力

ハイレベル関数を使って文字列の入出力を行うためには、次のような手順が必要です。

  • GetStdHandle 関数を使って標準入出力ハンドルを取得する。
  • 必要なら標準出力の属性を変更する。(その場合は、デフォルトの属性を取得して保存しておく)
  • GetConsoleScreenBufferInfo でデフォルトの属性を取得できる。SetConsoleTextAttribute で属性を変更できる。
  • WriteConsole 関数で文字列を出力する。ReadConsole 関数で文字列(行単位)を入力する。
  • 最後に、標準出力の属性を元に戻す。

サンプル

色を変更した文字列の出力

表示文字列の属性を変更するには、SetConsoleTextAttribute の第二引数で色と明るさを指定します。背景色の変更も同時に行うことができます。

前景色は、 FOREGROUND_xxxx というシステム定義定数を使って指定します。xxxx の部分が色の指定で、BLACK や RED などが指定できます。(指定できるのは原色だけで、任意の色は指定できません)

背景色は、BACKGROUND_xxxx というシステム定義定数を使って指定します。xxxxx の部分(色の名前)は前景色と同じです。

明るさは FOREGROUND_INTENSITY や BACKGROUND_INTENSITY で指定します。明るさの指定は明るい・暗いの2種類だけです。

これらの定数を | 演算子で結合したものが SetConsoleTextAttribute の第二引数になります。下に例を示します。

FOREGROUND_GREEN | FOREGROUND_INTENSITY (前景色が明るい緑、背景色は黒)
FOREGROUND_WHITE | BACKGROUND_BLUE | BAKCGROUND_INTENSITY (前景色が暗い白(シルバー)、背景色は明るい青)
次のサンプルは、 Fig.1 のように色を変えた文字列を表示した後、デフォルトに戻して終了するだけのものです。

Fig.1 色を変更した文字列の出力

ヘッダーファイル

// stdafx.h : 標準のシステム インクルード ファイルのインクルード ファイル、または
// 参照回数が多く、かつあまり変更されない、プロジェクト専用のインクルード ファイル
// を記述します。
//

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

// TODO: プログラムに必要な追加ヘッダーをここで参照してください
#include <Windows.h>

CPP ファイル

// ConsoleApi01.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

/*

  WriteConsole() の代わりに WriteFile() を使う場合、Unicode でなくマルチバイト文字にしないと正しく表示しない。

*/
int main()
{
    HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);

    if (h == INVALID_HANDLE_VALUE)
    {
        printf_s("ErrorCode = %d\n", GetLastError());
        return -1;
    }

    LPCSTR str = "Write Console.\n";
    LPCSTR done = "Done.\n";
    DWORD len = -1;
    DWORD n = strlen(str);
    DWORD nd = strlen(done);

    // デフォルトの表示文字の色を保存
    CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
    if (!GetConsoleScreenBufferInfo(h, &consoleInfo))
    {
        printf("ErrorCode = %d\n", GetLastError());
        return -1;
    }

    // 表示文字の色を変更
    WORD wAttr = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_BLUE;

    // 文字列を表示する。
    if (!SetConsoleTextAttribute(h, wAttr))
    {
        printf_s("ErrorCode = %d\n", GetLastError());
        return -1;
    }

    WriteConsole(h, str, n, &len, NULL);

    // 表示文字の色を元に戻す。
    wAttr = consoleInfo.wAttributes;
    SetConsoleTextAttribute(h, wAttr);
    WriteConsole(h, done, nd, &len, NULL);

    getchar();
    return 0;
}

コンソールから行を入力する

コンソールから文字列を行単位で入力するには、ReadConsole 関数 (マルチバイト文字モードなら ReadFile 関数も使用可能) を使用します。

次のサンプルは、文字列を入力し、それをメッセージボックスに表示します。


Fig.2 コンソールから行を入力する

ヘッダーファイル

// stdafx.h : 標準のシステム インクルード ファイルのインクルード ファイル、または
// 参照回数が多く、かつあまり変更されない、プロジェクト専用のインクルード ファイル
// を記述します。
//

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

// TODO: プログラムに必要な追加ヘッダーをここで参照してください
#include <Windows.h>

CPP ファイル

// ConsoleApi03.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#define BUFFLEN 1024

/*
    コンソール入力
    ==============
*/
int main()
{
    WCHAR buffer[BUFFLEN];
    DWORD len;
    LPCWSTR str = L"ReadConsole ..\n";

    // 標準入出力ハンドルを得る。
    HANDLE ho = GetStdHandle(STD_OUTPUT_HANDLE);
    HANDLE hi = GetStdHandle(STD_INPUT_HANDLE);

    if (ho == INVALID_HANDLE_VALUE || hi == INVALID_HANDLE_VALUE)
    {
        wprintf_s(L"ErrorCode = %d\n", GetLastError());
        return -1;
    }

    // str を表示する。

    WriteConsole(ho, str, wcslen(str), &len, NULL);

    // キーボード入力。
    ReadConsole(hi, buffer, BUFFLEN, &len, NULL);

    if (len < 3)
    {
        // Ctrl+C や Enter のみが押されたとき
        return -1;
    }

    // 行末に CRLF が入るので削除する。(ついでに行末に'\0'を追加)
    buffer[len - 1] = '\0';
    buffer[len - 2] = '\0';

    MessageBox(NULL, buffer, L"Result", MB_OK);
    return 0;
}

-