0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

今回は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);

第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と変わらない為、割愛します。

0
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?