コンソールの文字色と背景色の変更方法 - P/Invoke

コンソールプログラムで、文字の色や背景色を変更する方法を紹介します。

コンソールプログラムは通常はキャラクターベースのプログラムを実行します。

文字ベースということで、エラーが起きたときは例えば "ERROR" などと文字を出力して、 それを検出して次なるプログラムで、ERROR という文字を含む行をログに記録する、などの作業を行うのが一般的です。

ですから、例えば赤い色で何かを表示することによってエラーであることを示すだけだと、 find などで文字を捕まえられないので都合がよくありません。

こうしたことが、案外色を変えるということが重要視されない理由といえるでしょう。

しかし、そうはいっても、エラーが起きたときなんかはやっぱり、赤色とか、なにやらヤバそうな色で表示した方がわかりやすい場合も多いですよね。

必要に応じて使えるように覚えておいて損はないと思います。

コンソールの属性設定

さて、ここで実際にコンソールの色が変わるプログラムを書いてみましょう。実行例は次の通りです。

コンソールの文字色と背景色の変更方法

World! の文字の色と背景色が変わっています。

これをやるには Windows API を利用するのですが、C/C++ だとかなり簡単な感じでできちゃうので、 あえて C# から P/Invoke (Platform Invoke) で API を使ってみましょう。

手順は簡単です。

  1. GetStdHandle で標準出力のハンドルを取得
  2. SetConsoleTextAttribute で文字色と背景色を設定

これだけです。

ここの例では GetConsoleScreenBufferInfo で現在の設定を取得して、元に戻せるようにしてますが、色を変えるだけなら上の手順で OK です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ColorTest
{
	class Program
	{
		[DllImport("kernel32.dll")]
		static extern bool SetConsoleTextAttribute(
			IntPtr hConsoleOutput, 
			ushort wAttributes);

		[DllImport("kernel32.dll", SetLastError = true)]
		static extern IntPtr GetStdHandle(
			int nStdHandle);

		[DllImport("kernel32.dll")]
		static extern bool GetConsoleScreenBufferInfo(
			IntPtr hConsoleOutput,
			out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);

		[StructLayout(LayoutKind.Sequential)]
		struct COORD
		{
			public short x;
			public short y;
		}

		[StructLayout(LayoutKind.Sequential)]
		struct SMALL_RECT
		{
			public short Left;
			public short Top;
			public short Right;
			public short Bottom;
		}

		[StructLayout(LayoutKind.Sequential)]
		struct CONSOLE_SCREEN_BUFFER_INFO
		{
			public COORD dwSize;
			public COORD dwCursorPosition;
			public ushort wAttributes;
			public SMALL_RECT srWindow;
			public COORD dwMaximumWindowSize;
		}

		/* WinCon.h
		#define FOREGROUND_BLUE      0x0001 
		#define FOREGROUND_GREEN     0x0002 
		#define FOREGROUND_RED       0x0004 
		#define FOREGROUND_INTENSITY 0x0008 
		#define BACKGROUND_BLUE      0x0010 
		#define BACKGROUND_GREEN     0x0020 
		#define BACKGROUND_RED       0x0040 
		#define BACKGROUND_INTENSITY 0x0080 
		//*/

		static void Main(string[] args)
		{
			const int STD_OUTPUT_HANDLE = -11;
			const int FOREGROUND_GREEN = 2;
			const int FOREGROUND_INTENSITY = 8;
			const int BACKGROUND_BLUE = 16;

			CONSOLE_SCREEN_BUFFER_INFO bufferInfo 
				= new CONSOLE_SCREEN_BUFFER_INFO();
			IntPtr hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

			GetConsoleScreenBufferInfo(hConsole, out bufferInfo);

			Console.WriteLine("Hello!");

			SetConsoleTextAttribute(
				hConsole, 
				FOREGROUND_GREEN 
				| FOREGROUND_INTENSITY 
				| BACKGROUND_BLUE);

			Console.WriteLine("World!");

			SetConsoleTextAttribute(hConsole, bufferInfo.wAttributes);

			Console.WriteLine("Bye!");

		}

	}
}

コツとしては、P/Invoke のシグネチャは自分で書かないで pinvoke.net からなるべく持ってくることと、コードは Windows SDK のヘッダファイル (WinCon.h など) を参照するとよい、ということです。

特にシグネチャは自分で下手に書いて失敗すると、そうそうコラプトしない思われがちなマネージドヒープがやられたりしますので、気をつけましょう。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説