ファイルを開く - HANDLE、CreateFile()

Windows APIでは、CreateFile()という関数でファイルを開く。fopen(3)と比べると引数に指定する内容が多い。CreateFile()の返り値はHANDLEだ。CreateFile()でファイルを開き、それをHANDLEの変数へ代入する。これが基本的な操作方法となる。

この辺りの操作は、次の動作を参考にしてもらえればと思う。

ファイルが開けなかった場合は、HANDLEとしてINVALID_HANDLE_VALUEが返ってくるので、それで判断すればよい。

ファイルのサイズを取得 - DWORD、GetFileSize()

ファイルサイズの取得には、GetFileSize()を使用する。引数として先程取得したHANDLEを指定する。stat(2)システムコールは利用するにあたってファイルディスクリプタを必要としないが、GetFileSize()はすでにオープンしたファイルのHANDLEを指定する必要がある。この辺りは、動きというか考え方が異なるところだ。

GetFileSize()に関しては、次のドキュメントが参考になる。

ファイルサイズは返り値としてDWORDで返ってくる。

メモリを確保 - HeapCreate()、HeapAlloc()

もともとのutil_file.cではcalloc(3)でヒープからメモリを確保していた。Windows APIではHeapCreate()でヒープへのアクセスを取得し、その後でHeapAlloc()を使ってヒープからメモリを確保するという処理を行う。

HeapCreate()とHeapAlloc()については、次のドキュメントが参考になる。

ファイルもそうだったわけだが、HANDLEを取得し、取得したHANDLEを経由してさまざまな処理を行うのが基本的なアプローチと考えておくとわかりやすいと思う。

ファイルからメモリへ読み込み - ReadFile()

util_file.cではfgetc(3)でファイルから1バイトづつデータを取得し、それをcalloc(3)で確保したメモリへ書き込むという処理を行った。Windows APIではReadFile()関数で似たような感じの処理を行う。読み込まれたデータ数が得られるので、これが0になるまで読み込み処理を繰り返すといった感じだ。

ReadFile()は次のドキュメントに説明がまとまっている。

使用する関数は異なっているが、やっている処理はPOSIX的な関数を使う場合とほぼ同じだ。

クローズ処理 - CloseHandle()

開いたものは閉じる必要がある。C言語では、この処理をちゃんとやっておかないとバグや脆弱性の原因となるので、注意深く対処する必要がある。今回取得したHANDLEはCloseHandle()で閉じる。CloseHandle()の説明は、次のページに掲載されている。

すぐに終了するようなコマンドなら、閉じなくても問題が生じないこともあるかもしれないが、サーバ系の常に動き続けるタイプのソフトウェアを開発する場合はこの辺りの処理をやっておかないとバグや脆弱性の原因になったりするので注意しよう。

ビルドと実行を確認

今回はutil_file.cだけWindows APIを使うように書き換えたので、実際にはPOSIX的な書き方をしたソースコードと、Windows APIを使ったソースコードが混在した状態になっている。利用はできるが、意味の解釈が混乱してしまうので好ましい状態とは言えない。とはいえ、とりあえずWindows APIを使ってみる最初のステップとしてはいいんじゃないかと思う。

  • Visual Studio Codeでビルドした場合のサンプル

    Visual Studio Codeでビルドした場合のサンプル

Windows APIの使い方はMicrosoftがドキュメントを公開しているので、インターネットで調べながら利用することができる。時間はかかるからもしれないが、一つ一つ丁寧にやっていけば困ることはないはずだ。今回の例に限らず、とりあえずシンプルなところからWindows APIを使ってみていただきたい。