cmdコンソールでこんにちは世界に白黒つける（カラーテーブル実行時中のみ変更）

この記事は[F# Advent Calendar 2022](https://qiita.com/advent-calendar/2022/fsharp)参加記事です


# Summary

cmdコンソールで起動時にカラーテーブルを変更して常に意図したカラーを出力する

カラーテーブルの変更期間はプログラム実行時中のみとする


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149269/a749e666-9a51-10b9-6dd8-f68698bb9e43.png)


## 背景

実行するパソコンによっては出力した文字色と背景色が同じになる場合がありcmdコンソールの色調を固定する必要があった

## 方策

実行している間、cmdコンソールのカラーテーブルを意図したものにする

## 実施策

[SetConsoleScreenBufferInfoEx 関数](https://learn.microsoft.com/ja-jp/windows/console/setconsolescreenbufferinfoex)を使用してカラーテーブル情報を書き換えればよい

```c
// SetConsoleScreenBufferInfoEx 関数
// https://learn.microsoft.com/ja-jp/windows/console/setconsolescreenbufferinfoex
// 指定したコンソール画面バッファーに関する拡張情報を設定します

BOOL WINAPI SetConsoleScreenBufferInfoEx(
  _In_ HANDLE                        hConsoleOutput,
  _In_ PCONSOLE_SCREEN_BUFFER_INFOEX lpConsoleScreenBufferInfoEx
);

// CONSOLE_SCREEN_BUFFER_INFOEX 構造体
// https://learn.microsoft.com/ja-jp/windows/console/console-screen-buffer-infoex
// コンソール画面バッファーに関する拡張情報が含まれます。

typedef struct _CONSOLE_SCREEN_BUFFER_INFOEX {
  ULONG      cbSize;
  COORD      dwSize;
  COORD      dwCursorPosition;
  WORD       wAttributes;
  SMALL_RECT srWindow;
  COORD      dwMaximumWindowSize;
  WORD       wPopupAttributes;
  BOOL       bFullscreenSupported;
  COLORREF   ColorTable[16];       // <---------- ★ここがcmdコンソールのカラーテーブル情報
} CONSOLE_SCREEN_BUFFER_INFOEX, *PCONSOLE_SCREEN_BUFFER_INFOEX;
```

## 実際のコード

### メインコード
```fsharp
(*

  サンプルプログラム
  ------------------
  cmdコンソールの色調をどんな環境でも同じ色調にするように固定する
  背景色：白色（rgb = 255,255,255）
  前景色：黒色（rgb = 0  ,0  ,0  ）

*)

open System
open System.Runtime.InteropServices
open DllImported

[<Literal>]
let STD_OUTPUT_HANDLE = -11

// 1. コンソール画面バッファーの容器を作成
let mutable screenBuffer = new CONSOLE_SCREEN_BUFFER_INFOEX(
    cbSize = Marshal.SizeOf(typeof<CONSOLE_SCREEN_BUFFER_INFOEX>)
)

// 2. 現状のコンソール画面バッファーの拡張情報を取得
GetConsoleScreenBufferInfoEx(GetStdHandle(STD_OUTPUT_HANDLE), &screenBuffer ) |> ignore

// 3. 変更したい拡張情報を作成
//  カラーテーブル番号0  に白色を設定
screenBuffer.colorScheme.black <- {Red=byte 255;Green=byte 255;Blue=byte 255}
//  カラーテーブル番号15 に黒色を設定
screenBuffer.colorScheme.white <- {Red=byte 0  ;Green=byte 0  ;Blue=byte 0  }

// 4. コンソール画面バッファーの拡張情報を変更する
SetConsoleScreenBufferInfoEx(GetStdHandle(STD_OUTPUT_HANDLE), &screenBuffer ) |> ignore

// 5. コンソールの背景色・前景色を設定
//    ここでは背景色をカラーテーブル番号0 に設定
//    ここでは前景色をカラーテーブル番号15に設定
Console.BackgroundColor <- ConsoleColor.Black
Console.ForegroundColor <- ConsoleColor.White

"""
(｡･ω･｡)ﾉ hello world!
""" |> printfn "%s"
```

### DLL取込み部分

`ColorScheme`部分で`@haxe`さんよりアドバイス頂き改善しました。ありがとうございます！

```fsharp 
module DllImported =

  open System
  open System.Runtime.InteropServices

  [<Struct; StructLayout(LayoutKind.Sequential, Size = 4)>]
  type RGB = {
    Red   : byte
    Green : byte
    Blue  : byte
  }

  [<Struct; StructLayout(LayoutKind.Sequential)>]
  type ColorScheme = {
    mutable black       : RGB
    mutable darkBlue    : RGB
    mutable darkGreen   : RGB
    mutable darkCyan    : RGB
    mutable darkRed     : RGB
    mutable darkMagenta : RGB
    mutable darkYellow  : RGB
    mutable gray        : RGB
    mutable darkGray    : RGB
    mutable blue        : RGB
    mutable green       : RGB
    mutable cyan        : RGB
    mutable red         : RGB
    mutable magenta     : RGB
    mutable yellow      : RGB
    mutable white       : RGB
  }

  [<Struct; StructLayout(LayoutKind.Sequential)>]
  type COORD = { X : int16; Y : int16 }

  [<Struct; StructLayoutAttribute(LayoutKind.Sequential)>]
  type SMALL_RECT = { Left : int16; Top : int16; Right : int16; Bottom : int16 }

  [<Struct; StructLayoutAttribute(LayoutKind.Sequential)>]
  type CONSOLE_SCREEN_BUFFER_INFOEX =
    val mutable cbSize               : int
    val mutable dwSize               : COORD
    val mutable dwCursorPosition     : COORD
    val mutable wAttributes          : int16
    val mutable srWindow             : SMALL_RECT
    val mutable dwMaximumWindowSize  : COORD
    val mutable wPopupAttributes     : int16
    val mutable bFullscreenSupported : bool
    val mutable colorScheme          : ColorScheme

  [<DllImportAttribute("kernel32.dll",SetLastError=true)>]
  extern IntPtr GetStdHandle(int nStdHndle)

  [<DllImportAttribute("kernel32.dll",SetLastError=true)>]
  extern IntPtr GetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFOEX& ConsoleScreenBufferInfo)

  [<DllImportAttribute("kernel32.dll",SetLastError=true)>]
  extern IntPtr SetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFOEX& ConsoleScreenBufferInfo)
```


## その他

下記にサンプルプログラム置いてます
(｡･ω･｡)ﾉﾖｶｯﾀﾗﾐﾃﾈ

https://github.com/callmekohei/colorfulHellofs


それでは！よいクリスマスを🎄