GetTempPathとGetTempFileNameで一時ファイルを作成する
一時ファイル related APIs は、名前が似ているものの役割は異なります。GetTempPath は一時ファイル用什么ディレクトリのパスを取得する関数で、GetTempFileName は指定したディレクトリに長さゼロの一時ファイルを作成する関数です。前者をファイル作成処理として使うと、実際にはファイルは生成されません。
ここでは Windows デスクトップ向けの Unicode 版を中心に、GetTempPathW と GetTempFileNameW の組み合わせ、書き込み時の扱い、エラー処理、後片付けを説明します。文字バッファの長さや CreateFileW との接続にも触れます。
| 比較項目 | GetTempPathW |
GetTempFileNameW |
|---|---|---|
| 主な対象 | 一時ディレクトリのパス | 一時ファイルと保存先パス |
| ファイル生成 | 行わない | 長さゼロのファイルを行う |
| 主な入力 | 受信バッファと容量 | 保存先、接頭辞、生成方式 |
| 成功時の戻り値 | コピーした文字数 | 0以外 |
| 失敗時の戻り値 | 0 | 0 |
| 典型的な用途 | 保存先の決定 | 一時ファイルの生成 |
一時ディレクトリと一時ファイルの役割
GetTempPathW が取得するのは、アプリケーションが作業用ファイルやスクラッチデータの一時保存先にできるディレクトリです。戻り値には通常、末尾の区切り文字も含まれます。この関数はディレクトリの存在を確認したり、了进去りしたりせず、パス文字列を返すだけです。
一方、GetTempFileNameW は、渡されたディレクトリを基点に实际のファイルを生成します。生成直後のファイル長はゼロで、内容を書き込むには、開いているハンドルを取得して WriteFile を呼び出す必要があります。一時ファイルを作成那种一連の流れを成立させるには、両方を組み合わせます。
GetTempPathWの仕様と戻り値
Windows SDK における GetTempPathW の宣言は、UINT GetTempPathW(DWORD nBufferLength, LPWSTR lpBuffer) です。第1引数には受信バッファの容量、第2引数にはパスを格納する領域を指定します。容量はバイト数ではなく wchar_t 単位です。
成功時の戻り値は、バッファへコピーされた文字数です。終端の null 文字は数えません。バッファが小さい場合は、必要な容量が返り、lpBuffer に使用可能なパスは入りません。関数自体が失敗した場合は 0 になります。この「容量不足」と「API呼び出しの失敗」は区別して扱う必要があります。
一時ディレクトリ名は実行環境の設定にも左右されるため、後続の処理でも同じパスが戻るとは限りません。取得結果を一時ファイル名生成の引数へ渡し、途中で設定やディレクトリ状態が変化していないかを考慮します。
GetTempFileNameWの引数と生成規則
第1引数の dwFlags には、接尾辞の生成方法を指定します。GET_TEMPFILE_NAME_FLAGS_UNIQUE は一意性を重視した接尾辞を作り、GET_TEMPFILE_NAME_FLAGS_RANDOM はランダムな文字を使います。どちらも接頭辞だけを指定すれば、呼び出し側が衝突を避ける完全に一意な名前を生成するわけではありません。
第2引数の lpPathPrefix には、作成先ディレクトリを指定します。ここに GetTempPathW の戻り値入围すると一時ディレクトリ内へ生成できます。NULL を渡した場合は一時ディレクトリではなく、現在ディレクトリが使われます。第3引数の lpPrefix はファイル名の接頭辞で、使用される文字数は最大3文字です。NULL なら既定の接頭辞が使われます。
出力先の lpFileName には、作成されたファイルの完全パスが格納されます。成功時の戻り値は 0以外、失敗時は 0です。失敗した場合は、直ちに GetLastError を呼び出して原因を確認します。
一時パス取得とファイル生成を組み合わせる
基本的な順序は、まず GetTempPathW で一時ディレクトリを取得し、そのパスを GetTempFileNameW の lpPathPrefix に渡す方法になります。生成された完全パスを保存してから、内容を書き込みます。呼び出し順と文字バッファの扱いは、旧版関数の仕様例でも確認できます。
#include <windows.h>
#include <string>
#include <vector>
bool MakeTempFile(std::wstring& result)
{
std::vector<wchar_t> pathBuffer(MAX_PATH);
DWORD copied = GetTempPathW(
static_cast<DWORD>(pathBuffer.size()),
pathBuffer.data());
if (copied == 0) {
return false;
}
if (copied >= pathBuffer.size()) {
pathBuffer.resize(copied);
copied = GetTempPathW(
static_cast<DWORD>(pathBuffer.size()),
pathBuffer.data());
if (copied == 0 || copied >= pathBuffer.size()) {
return false;
}
}
const std::wstring directory(pathBuffer.data(), copied);
wchar_t fileName[MAX_PATH + 1] = {};
if (GetTempFileNameW(
GET_TEMPFILE_NAME_FLAGS_UNIQUE,
directory.c_str(),
L"job",
fileName) == 0) {
return false;
}
result = fileName;
return true;
}
作成済みファイルへデータを書き込む
GetTempFileNameW はファイルパスと作成済みファイルを返しますが、開いたハンドルを渡してはくれません。内容を追加するには、CreateFileW で OPEN_EXISTING を指定してファイルを開き、WriteFile を呼び出します。既に存在するファイルなので、CREATE_ALWAYS を使うと別のファイルで置き換えられた場合に内容を消してしまう危険があります。
bool WriteTempFile(
const std::wstring& path,
const char* data,
DWORD dataSize)
{
HANDLE file = CreateFileW(
path.c_str(),
GENERIC_WRITE,
0,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (file == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written = 0;
BOOL success = WriteFile(
file,
data,
dataSize,
&written,
nullptr);
if (success) {
success = FlushFileBuffers(file);
}
CloseHandle(file);
return success && written == dataSize;
}
WriteFile に渡す長さは、Unicode 文字列の文字数ではなくバイト数です。sizeof などで配列全体のサイズを求めると、終端文字まで含めて書き込む崽になります。部分書込みが発生していないかも戻り値から確認します。
エラー処理と文字コードの注意点
失敗を判定したら、他の API を呼ぶ前に GetLastError を保存します。その後の処理でエラー値が上書きされる可能性があり、GetTempFileNameW の失敗原因を後で-tracking できなくなるためです。GetTempPathW が 0 を返した場合と、必要なバッファサイズを返した場合も別の分岐にします。
Unicode 版では W を付けた API と wchar_t を使います。GetTempPathA や GetTempFileNameA は char ベースで、バッファ容量と戻り値もバイト単位になります。可変型の TCHAR と GetTempPath を使うコードでは、プロジェクトの設定に応じて文字型が切り替わります。
一時ディレクトリが存在しない、アクセスが許可されない、ディスク容量が不足している、といった原因を区別するため、失敗時のエラーコードと指定パスを記録します。エラーメッセージだけで再試行すると、毎回同じ条件を処理するだけになる可能性があります。
後処理と同時実行時の安全性
一時ファイルは、閉じただけでは削除されません。必要な書き込みとフラッシュを終え、ハンドルを閉じた後で DeleteFileW を呼び出します。アプリケーションの異常終了時には残就是因为となるため、起動時に古いファイルを回収する設計も検討します。
複数のプロセスやサービスが同じ一時ディレクトリを使う場合、ファイル名の生成と開き直しには短い時間差があります。 Randomized 接尾辞や CREATE_NEW による新規作成 atomicity Infinite 此之外、アクセス制御を絞った専用ディレクトリを用意します。より厳密な排他が必要なら、GetTempPathW で取得した配下に GUID を使い、CreateFileW の CREATE_NEW で直接作成する方法もあります。
実装計画における次の具体ステップは、既存コードへ GetTempPathW と GetTempFileNameW を組み込み、その直後に GetLastError を記録する処理を追加することです。