今回はC言語を用いてWindows APIを呼び出しputs関数を再現します。
先ずはC言語標準puts関数の動作を確認します。
#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>
konnichiha
return = 0
konbanha
return = 0
今回再現するputsも標準仕様に倣い、CMD上での出力及びファイル出力の両方に対応させます。
又、処理が成功した場合の返り値は0であること確認しました。
(どうしたら失敗させられるのか分からない為、失敗した場合の返り値は確認が出来ません。><;)
putsで出力する文字の後には自動的に改行コードが付加されていることが確認できます。
これらの点を踏まえ以下の様に実装しました。
#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>
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);
第1引数:入力 hStdOutはハンドル(別の機会に説明します)
第2引数:入力 str 入力文字列のポインタ
第3引数:入力 len 書き込む文字数=strlen(str)
第4引数:出力 &written 実際に書き込まれたバイト数
第5引数:入力 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と変わらない為、割愛します。