Visual Studio 2022 ビルドアプリの起動時クラッシュ問題と解決策
問題概要
Visual Studio 2022 v17.10 でビルドしたアプリケーションが起動直後にクラッシュする現象が報告されています。具体的には、std::mutex::lock()
の初回呼び出し時に次の例外が発生します:
0xC0000005: Access violation reading location 0x0000000000000000.
スタックトレース:
msvcp140.dll!mtx_do_lock(_Mtx_internal_imp_t * mtx, const xtime * target) Line 100 C++
[Inline Frame] my.dll!std::_Mutex_base::lock()
[Inline Frame] my.dll!std::unique_lock<std::mutex>::{ctor}(std::mutex &)
この問題は、新しいVisual Studioでビルドされたアプリケーションが古いバージョンのランタイム(msvcp140.dll)を使用する環境で実行された場合に発生します。根本原因は、STL(Standard Template Library)のmutex実装変更によるABI(Application Binary Interface)互換性の問題です。
根本原因
Microsoft STLチームの変更ログによると、次の変更が問題の原因となっています:
Fixed mutex's constructor to be constexpr.
注意: バイナリ互換性の制限に従っていないプログラムは、mutex機構でnull参照エラーが発生する可能性があります。次のルールを守る必要があります:異なるバージョンのツールセットでビルドされたバイナリを混在させる場合、再頒布可能パッケージのバージョンは、アプリコンポーネントで使用される最新のツールセットと少なくとも同じ新しさである必要があります。
この変更により std::mutex
のコンストラクタが constexpr
化されましたが、これが古いランタイムとの互換性を損なう結果となりました。ユーザー環境で他のアプリのインストールにより msvcp140.dll
がダウングレードされると、新しいコンパイラでビルドされたアプリが動作しなくなります。
解決方法
方法1: プリプロセッサマクロによる回避(推奨)
ソースコードで次のプリプロセッサマクロを定義します:
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
#include <mutex>
または、プロジェクト設定でグローバルに定義します(Visual Studio):
- プロジェクトのプロパティを開く
- [構成プロパティ] → [C/C++] → [プリプロセッサ]
- 「プリプロセッサの定義」に
_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
を追加
この設定により、新しい constexpr
mutex コンストラクタが無効化され、古いランタイムとの互換性が維持されます。
方法2: 再頒布可能パッケージの更新
エンドユーザー環境でランタイムを更新する手順:
- Windowsのコントロールパネルを開く
- [プログラムと機能] を選択
- 「Microsoft Visual C++ 2015-2022 Redistributable」を探す
- 選択して [変更] をクリック
- [修復] オプションを実行
管理者としてコマンドプロンプトでインストーラーを直接実行:
vc_redist.x64.exe /repair
インストーラーはMicrosoft公式サイトからダウンロード可能: Visual Studio 2022 の最新のサポートされている最新の再頒布可能パッケージ
方法3: ランタイムバージョンチェックの実装
ユーザーに分かりやすいエラーメッセージを表示するには、アプリ起動時にランタイムバージョンを検証:
#include <Windows.h>
#include <cstdlib>
#include <iostream>
bool check_runtime_version() {
HMODULE hModule = LoadLibraryA("msvcp140.dll");
if (!hModule) return false;
// ファイルバージョン取得処理
char path[MAX_PATH];
GetModuleFileNameA(hModule, path, MAX_PATH);
DWORD dummy;
DWORD size = GetFileVersionInfoSizeA(path, &dummy);
if (!size) return false;
std::vector<BYTE> buffer(size);
if (!GetFileVersionInfoA(path, 0, size, buffer.data())) return false;
VS_FIXEDFILEINFO* fileInfo;
UINT len;
if (!VerQueryValueA(buffer.data(), "\\", (LPVOID*)&fileInfo, &len)) return false;
// メジャーバージョン14.40以上を要求
const DWORD major = HIWORD(fileInfo->dwFileVersionMS);
const DWORD minor = LOWORD(fileInfo->dwFileVersionMS);
return (major > 14) || (major == 14 && minor >= 40);
}
int main() {
if (!check_runtime_version()) {
std::cerr << "エラー: 古いMicrosoft Visual C++ランタイムが検出されました\n"
<< "以下のリンクから最新バージョンをインストールしてください:\n"
<< "https://aka.ms/vs/17/release/vc_redist.x64.exe\n";
return 1;
}
// 通常のアプリケーション実行
}
重要
このアプローチでは:
- ユーザーフレンドリーなエラーメッセージを表示
- 直接ダウンロードリンクを提供
- 異常終了前に問題の原因を明確に通知
開発時のベストプラクティス
バイナリ互換性ルールの厳守
異なるVSバージョンのバイナリを混在させる場合、再頒布ランタイムは最も新しいツールセットのバージョンと一致あるいはそれ以上である必要がありますインストーラーにランタイム同梱
アプリケーション配布時にはインストーラーに適切なバージョンのvc_redist
を含め、インストール時に自動展開継続的インテグレーション(CI)環境の管理
GitHub ActionsなどのCI環境では、Runnerイメージが最新のランタイムを使用していることを確認:yamlsteps: - name: Install Latest VC Redist run: | curl -LO https://aka.ms/vs/17/release/vc_redist.x64.exe vc_redist.x64.exe /install /quiet /norestart
参考情報
この問題はランタイムのバージョン不一致が根本原因ですが、_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
マクロを使用した開発時対応と、適切なランタイムバージョンの配布により完全に回避可能です。