CreateFileWやDeleteFileWが失敗して、GetLastError() が32、つまりERROR_SHARING_VIOLATIONを返したときは、同じファイルをほかのハンドルが開いていて、その共有モードと今回の指定がかみ合っていません。メッセージは「別のプロセスが使用中です」ですが、同じプロセスの中で開いたハンドル同士でも起きます。この記事では、先に開いた側と後から開く側のどちらの共有モードを直すかを実行結果で確かめ、ファイルを開いているプロセスをRmGetListで調べます。
エラー32で最初に見る場所
CreateFileWの説明では、開いているハンドルのアクセスと衝突する共有モードを指定すると、失敗して32になるとされています。次の表の条件を1つでも満たさないと、後から開く側は32で失敗します。
| 条件 | 満たさないときに直す側 |
|---|---|
| 後から開く側のアクセス(読む・書く・消す)が、先のハンドルの共有モードに含まれている | 先に開いた側(自分のコードでなければ、そのアプリを閉じてもらう) |
| 後から開く側の共有モードが、先のハンドルのアクセスを含んでいる | 後から開く側(自分のCreateFileWの第3引数) |
自分は読むだけでも、相手が書き込み中なら、自分の共有モードにFILE_SHARE_WRITEがないと開けません。
共有モードの組み合わせとエラー32の実行結果
Visual Studio 2026、Debug/x64のMFCダイアログで試しました。1つのログファイルを先に開いたままにして、後から開く側の指定を変えています。

| ケース | 先のハンドル(アクセス / 共有) | 後から | 結果 |
|---|---|---|---|
| A. 書き込み中のログを読む | WRITE / READ | READ / 共有READ | 32 |
| B. 同じログを共有を広げて読む | WRITE / READ | READ / 共有READ|WRITE|DELETE | 成功 |
| C. 共有0で開かれたファイルを読む | READ|WRITE / 0 | READ / 共有READ|WRITE|DELETE | 32 |
| D. 読み取り中のファイルへ書く | READ / READ | WRITE / 共有READ | 32 |
| E. 開かれたまま削除する | READ / READ|WRITE | DeleteFileW | 32 |
Aは、読み手の共有にWRITEを足すと開けました(B)。CとDは、先のハンドルが読み取り(C)や書き込み(D)を共有していないので、後から開く側が何を指定しても開けません。Eも同じで、DeleteFileWは、ほかのハンドルがFILE_SHARE_DELETEなしで開いていると失敗します。
書き込み中のファイルを読むならFILE_SHARE_WRITEを足す
別のプロセスが追記しているログを表示する、といった読み手のコードは、共有モードを広く取ります。読み手の共有モードは「自分が開いている間に、ほかのハンドルに何を許すか」の指定です。読むだけなら、書き込みと削除を許しておくと、書き手の追記や削除を邪魔しません。
HANDLE file = ::CreateFileW(logPath, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE)
{
const DWORD error = ::GetLastError(); // ほかのAPIより先に保存
TRACE(L"CreateFileW failed: path=%s error=%lu\n", logPath, error);
}
逆に、書き手が自分のアプリなら、書き手の共有モードにFILE_SHARE_READを入れておきます。ログファイルを共有0で開いたままにすると、ほかのツールからは一切読めなくなります。CFileを使う場合は、CFile::shareDenyWriteがFILE_SHARE_READに、CFile::shareDenyNoneがFILE_SHARE_READ | FILE_SHARE_WRITEに当たります。
ファイルを使っているプロセスをRmGetListで調べる
相手が自分のコードでないときは、どのプロセスが開いているかをログに残すと原因を追えます。Restart ManagerのRmGetListを使えば、ファイルを使っているプロセスのPIDと名前を取得できます(Windows Vista以降、restartmanager.h、Rstrtmgr.lib)。
#include <RestartManager.h>
#pragma comment(lib, "Rstrtmgr.lib")
void TraceFileHolders(LPCWSTR path)
{
DWORD session = 0;
WCHAR sessionKey[CCH_RM_SESSION_KEY + 1]{};
if (::RmStartSession(&session, 0, sessionKey) != ERROR_SUCCESS)
return;
LPCWSTR files[] = { path };
if (::RmRegisterResources(session, 1, files, 0, nullptr, 0, nullptr) == ERROR_SUCCESS)
{
RM_PROCESS_INFO processes[10]{};
UINT needed = 0;
UINT count = _countof(processes);
DWORD rebootReasons = RmRebootReasonNone;
if (::RmGetList(session, &needed, &count, processes, &rebootReasons) == ERROR_SUCCESS)
{
for (UINT i = 0; i < count; ++i)
{
TRACE(L"in use by PID %lu %s\n",
processes[i].Process.dwProcessId, processes[i].strAppName);
}
}
}
::RmEndSession(session);
}
別のアプリがファイルを読み書きで開いたままの状態で、上書き保存と同じ条件(書き込み・共有READ)で開くと32になりました。そこでRmGetListを呼ぶと、開いているアプリのPIDと名前が1件返りました。PIDをOpenProcessに渡し、得たハンドルをQueryFullProcessImageNameに渡せば、実行ファイル名も分かります。

ファイルを開いているのがRmGetListを呼んだ自分自身のときは、種類(ApplicationType)がRmCriticalで返りました。10件を超えるとRmGetListは234(ERROR_MORE_DATA)を返し、neededに必要な件数が入ります。
なお、似た番号の33(ERROR_LOCK_VIOLATION)は、LockFileなどでファイルの一部の範囲がロックされているときのエラーです。開くときの共有モードの問題ではありません。
まとめ
- 相手のアクセスを自分の共有モードに含めていなければ、自分の指定で直せます。相手が共有していなければ、相手が閉じるまで開けません。
- 相手が分からないときは、RmGetListでPIDと名前を取り、失敗時のエラー番号と一緒にログへ残します。
