Skip to content

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: プリプロセッサマクロによる回避(推奨)

ソースコードで次のプリプロセッサマクロを定義します:

cpp
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
#include <mutex>

または、プロジェクト設定でグローバルに定義します(Visual Studio):

  1. プロジェクトのプロパティを開く
  2. [構成プロパティ] → [C/C++] → [プリプロセッサ]
  3. 「プリプロセッサの定義」に _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR を追加

この設定により、新しい constexpr mutex コンストラクタが無効化され、古いランタイムとの互換性が維持されます。

方法2: 再頒布可能パッケージの更新

エンドユーザー環境でランタイムを更新する手順:

  1. Windowsのコントロールパネルを開く
  2. [プログラムと機能] を選択
  3. 「Microsoft Visual C++ 2015-2022 Redistributable」を探す
  4. 選択して [変更] をクリック
  5. [修復] オプションを実行

管理者としてコマンドプロンプトでインストーラーを直接実行:

cmd
vc_redist.x64.exe /repair

インストーラーはMicrosoft公式サイトからダウンロード可能: Visual Studio 2022 の最新のサポートされている最新の再頒布可能パッケージ

方法3: ランタイムバージョンチェックの実装

ユーザーに分かりやすいエラーメッセージを表示するには、アプリ起動時にランタイムバージョンを検証:

cpp
#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;
    }
    
    // 通常のアプリケーション実行
}

重要

このアプローチでは:

  • ユーザーフレンドリーなエラーメッセージを表示
  • 直接ダウンロードリンクを提供
  • 異常終了前に問題の原因を明確に通知

開発時のベストプラクティス

  1. バイナリ互換性ルールの厳守
    異なるVSバージョンのバイナリを混在させる場合、再頒布ランタイムは最も新しいツールセットのバージョンと一致あるいはそれ以上である必要があります

  2. インストーラーにランタイム同梱
    アプリケーション配布時にはインストーラーに適切なバージョンの vc_redist を含め、インストール時に自動展開

  3. 継続的インテグレーション(CI)環境の管理
    GitHub ActionsなどのCI環境では、Runnerイメージが最新のランタイムを使用していることを確認:

    yaml
    steps:
    - 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 マクロを使用した開発時対応と、適切なランタイムバージョンの配布により完全に回避可能です。