Windows APIを呼び出しputs関数を再現

今回はC言語を用いてWindows APIを呼び出しputs関数を再現します。
先ずはC言語標準puts関数の動作を確認します。

```puts.c
#include<stdio.h>

int main(){
	int putsRtn = puts("konnichiha");
	printf("return = %d\n",putsRtn);
	
	putsRtn = puts("konbanha");
	printf("return = %d\n",putsRtn);
	return 0;
}
```
コンパイル方法
```
C:\Users\nanashi\puts>gcc puts.c -o puts.exe
```
**動作結果**
CMD上に直接出力する場合
```
C:\Users\nanashi\puts>puts
konnichiha
return = 0
konbanha
return = 0
C:\Users\nanashi\puts>puts
```
ファイル出力する場合
```
C:\Users\nanashi\puts>puts >>output.txt
C:\Users\nanashi\puts>
```
```output.txt
konnichiha
return = 0
konbanha
return = 0
```

今回再現するputsも標準仕様に倣い、CMD上での出力及びファイル出力の両方に対応させます。
又、処理が成功した場合の返り値は0であること確認しました。
(どうしたら失敗させられるのか分からない為、失敗した場合の返り値は確認が出来ません。＞＜；)
putsで出力する文字の後には自動的に改行コードが付加されていることが確認できます。

これらの点を踏まえ以下の様に実装しました。
```myputs.c
#include <windows.h> 
#include <string.h>
#include <stdio.h>    //EOF定数使用

int my_puts(const char *str) {
    // 標準出力のハンドルを取得（Windows固有の処理）
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hStdOut == INVALID_HANDLE_VALUE) {  // ハンドル取得失敗
        return EOF;  // エラー時はEOFを返す
    }

    if (str == NULL) {
        return EOF;
    }

    DWORD len = strlen(str);    // 入力された文字列の長さを取得
    DWORD written;              // 実際に書き込まれたバイト数格納用

    /* 文字列本体の出力処理 -------------------------------------------*/
    // コンソール出力APIを使用
    BOOL success = WriteConsoleA(hStdOut, str, len, &written, NULL);
    
    // myputs >> output.txtとして実行した場合にWriteConsoleAは失敗する
    if (!success) {
        // エラー種別を確認（無効なハンドルエラーの場合）
        if (GetLastError() == ERROR_INVALID_HANDLE) {
            // ファイルへ書き込み
            success = WriteFile(hStdOut, str, len, &written, NULL);
            
            // 部分書き込みをエラーとして扱う
            if (success && written != len) {
                success = FALSE;
            }
        }
        if (!success) {
            return EOF;
        }
    }

    /* 改行文字の出力処理 */
    const char *newline = "\n";    // 改行コード
    const DWORD newline_len = 1;   // 改行文字のバイト数
    
    // 改行文字をコンソールに出力
    success = WriteConsoleA(hStdOut, newline, newline_len, &written, NULL);
    if (!success) {
        if (GetLastError() == ERROR_INVALID_HANDLE) {
            success = WriteFile(hStdOut, newline, newline_len, &written, NULL);
            if (success && written != newline_len) {
                success = FALSE;
            }
        }
        if (!success) {
            return EOF;  // 改行文字書き込み失敗時もEOFを返す
        }
    }

    return 0;
}

int main() {
	int putsRtn = my_puts("konnichiha");
	printf("return = %d\n",putsRtn);
	putsRtn = my_puts("konbanha");
	printf("return = %d\n",putsRtn);
    return 0;
}
```

実行結果
```
C:\Users\sumiya\puts>myputs
konnichiha
return = 0
konbanha
return = 0
```

```
C:\Users\nanashi\puts>puts >>output2.txt
C:\Users\nanashi\puts>
```
```output2.txt
konnichiha
return = 0
konbanha
return = 0
```
結果はputsと一致しました。

**WriteConsoleAの仕様**
https://learn.microsoft.com/ja-jp/windows/console/writeconsole

my_puts中での呼び出し
```
success = WriteFile(hStdOut, str, len, &written, NULL);
```
第１引数：入力　hStdOutはハンドル(別の機会に説明します)
第２引数：入力　str　入力文字列のポインタ
第３引数：入力　len　書き込む文字数＝strlen(str)
第４引数：出力　&written　実際に書き込まれたバイト数
第５引数：入力　NULL　この引数は常にNULLです。(いつか使うために開けておいたのでしょうか？)

**WriteFileの仕様**
https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-writefile
my_puts中での呼び出し
```
BOOL success = WriteConsoleA(hStdOut, str, len, &written, NULL);
```
引数はWriteConsoleAと変わらない為、割愛します。