MSB8020でビルドが止まったとき、直し方は古いツールセットをビルド機に入れるか、プロジェクトを再ターゲットするかです。MFCアプリで後者を選ぶと、exeが要求するMFCランタイムがmfc100.dllやmfc120.dllから14系へ変わり、配布先に入れる再頒布可能パッケージも入れ替わります。Visual Studio 2026でv120とv145の両方をビルドし、動いたexeに実際に載ったランタイムをDebug構成で確認します。
MSB8020が指しているのはビルド機のツールセット
Visual Studio 2026のビルド機で、PlatformToolsetをv120にしたMFCダイアログをビルドすると、こう出ます。
C:\Program Files\Microsoft Visual Studio\18\Professional\MSBuild\Microsoft\VC\v180\Microsoft.CppBuild.targets(473,5):
error MSB8020: Visual Studio 2013 (プラットフォーム ツールセット = 'v120') のビルド ツールが見つかりません。
v120 ビルド ツールを使用してビルドするには、Visual Studio 2013 ビルド ツールをインストールしてください。
または、[プロジェクト] メニューを選択するかソリューションを右クリックし [ソリューションの再ターゲット] を選択して、
現在の Visual Studio Tools にアップグレードすることもできます。
行番号 (473,5) が指しているのはMicrosoft.CppBuild.targetsの中で、自分のソースではありません。コンパイラーはまだ1行も読んでいません。#includeを直しても、警告レベルを下げても、この行は消えません。
対処は、メッセージ自体が挙げています。「v120ビルド ツールをインストールしてください」と「[ソリューションの再ターゲット] を選択して…アップグレード」の2つです。
MicrosoftのMSB8020のリファレンスは、このエラーが出る条件を「ビルドが要求するプラットフォーム ツールセットがインストールされていないか、必要なフォルダーが存在しないとき」と書いています。そして $(PlatformToolset)(たとえばv143)はVisual Studioのインストール内の特定のフォルダーを指していて、そのフォルダーにそのツールセット用のMSBuildの .props / .targetsがある、とも書いてあります。MSB8020が伝えているのは、そのフォルダーが無いということだけです。
似た番号でMSB8041がありますが、あちらはMFCのライブラリ自体が入っていない話で、原因も対処も別です。MFCコンポーネントの追加手順はMSB8041の記事にあります。
世代は .vcxprojのPlatformToolsetに書いてある
要求している世代は、テキストエディターで .vcxprojを開けば分かります。構成ごとに1行あります。
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseOfMfc>Dynamic</UseOfMfc>
<PlatformToolset>v120</PlatformToolset> <!-- ここ -->
</PropertyGroup>
番号とVisual Studioの対応は次のとおりです。v140からv143まではC++ バイナリ互換性のドキュメントに明記されている対応です。同じドキュメントは、バイナリ互換の対象としてv140・v141・v142・v143と並べてv145を挙げ、Visual Studio 2026を対象に含めています。v145でビルドして動くことは、今回のビルド機(Visual Studio 2026)で実測しました。
| PlatformToolset | Visual Studio | メジャー番号 |
|---|---|---|
| v100 | 2010 | 10 |
| v120 | 2013 | 12 |
| v140 | 2015 | 14 |
| v141 | 2017 | 14 |
| v142 | 2019 | 14 |
| v143 | 2022 | 14 |
| v145 | 2026(実測) | 14 |
見るのは右の列です。Visual Studio 2015を境に、ビルドツールのメジャー番号は14で止まっています。公式ドキュメントは、2013以前のビルドツールはメジャーバージョンをまたいだバイナリ互換を保証しない(オブジェクトファイル・静的ライブラリ・DLL・exeをリンクできない)と書き、2015以降は互換だと書いています。v120とv141の間には壁がありますが、v140とv145の間には壁がありません。
動かしているのは、この1行だけです。同じ内容のMFCダイアログを2つ用意し、ソース10ファイル(App.cpp、HostDlg.cpp、pch.cppほか)がsha256で完全に一致していることを確認したうえで、PlatformToolsetをv120とv145にしました。コンパイルとリンクの意味を変えている設定の違いはこの1行だけです。残りの違いはプロジェクトファイル名とRootNamespace、.rcのファイル名、そして出力先(OutDir / IntDir)で、いずれも2つを別々にビルドしてぶつからないようにするための都合です。
再ターゲットで変わるのは配布先の再頒布パッケージ
v145側は当然ビルドが通ります。確認したいのは、通ったあとにそのexeが何を要求するようになったかです。そこで、ビルド時のツールセット名をプリプロセッサ定義で受け取り、実行中の自プロセスに読み込まれているモジュールをCreateToolhelp32Snapshotで列挙して、MFC / CRTのランタイムだけをフルパスで表示させました。ファイル名を決め打ちせず、実際に載ったものを見るためです。
// .vcxproj 側: /D SAMPLE_TOOLSET=v145 の形でトークンとして渡す
<PreprocessorDefinitions>SAMPLE_TOOLSET=$(PlatformToolset);%(PreprocessorDefinitions)</PreprocessorDefinitions>
// コード側: 文字列化してから表示する
#define SAMPLE_STRINGIZE2(x) _T(#x)
#define SAMPLE_STRINGIZE(x) SAMPLE_STRINGIZE2(x)
LPCTSTR const kToolset = SAMPLE_STRINGIZE(SAMPLE_TOOLSET);
Windows 11 / Visual Studio 2026 / Debug・x64での実行結果がこれです。
PlatformToolset : v145
_MSC_VER : 1951
_MSC_FULL_VER : 195136248
_MFC_VER : 0x0E00
MFCの使用 : 共有DLL (_AFXDLL が定義されている)
構成 : Debug / x64
このプロセスへ読み込まれている MFC / CRT ランタイム
msvcp_win.dll C:\WINDOWS\System32\msvcp_win.dll
ucrtbase.dll C:\WINDOWS\System32\ucrtbase.dll
VCRUNTIME140_1D.dll C:\WINDOWS\SYSTEM32\VCRUNTIME140_1D.dll
mfc140ud.dll C:\WINDOWS\SYSTEM32\mfc140ud.dll
VCRUNTIME140D.dll C:\WINDOWS\SYSTEM32\VCRUNTIME140D.dll
ucrtbased.dll C:\WINDOWS\SYSTEM32\ucrtbased.dll

ツールセットはv145ですが、読み込まれたMFCランタイムはmfc140でした。_MFC_VERも0x0E00、つまり14.00です。前の節の表の右列が、実行時のファイル名にそのまま出ています。
これは配布側の話に直結します。公式ドキュメントは、再頒布可能パッケージのメジャーバージョン番号をVisual Studio 2015・2017・2019・2022・2026で同じに保っている、と明言しています。再ターゲット先がv140からv145のどれであっても、配布先に入れるものは14系の再頒布可能パッケージ1つです。v120から上げるということは、配布手順からVC++ 2013の再頒布可能パッケージ(mfc120.dllを提供していたもの)が消えて、14系が入る、という変更になります。DLL名から必要なパッケージを特定する手順はmfc140.dll / mfc120.dll / mfc100.dllが見つからないにまとめてあります。
実測しているのはDebug構成なので、載っているのは末尾にDの付くデバッグ版で、これは再頒布できません。Release構成のファイル名は測っていないので断定しませんが、_MFC_VERが0x0E00である以上、世代が14であることは変わりません。
一覧のうちucrtbase.dllはUniversal CRTで、公式ドキュメントはUCRTを「Microsoft Windowsのオペレーティング システム コンポーネント」と明記しています(Windows 10以降とWindows Server 2016以降ではOSに含まれます)。この行は、再ターゲットの判断で配布物として数えるものではありません。再頒布可能パッケージはUCRTが入っていない環境に初期バージョンを入れますが、世代の話に効いてくるのはmfc* とVCRUNTIME* / MSVCP140* の行です。なおmsvcp_win.dllは名前が紛らわしいですが、再頒布可能パッケージが入れるmsvcp140.dllとは別のファイルです。
配布まわりでは、次の決まりが事故につながります。いずれも公式ドキュメントに明記されています。
- メジャー番号が同じなので、再頒布可能パッケージは同時に1つしか入りません。新しい版が古い版を上書きします。
- 逆向きは入りません。新しい版が入っている環境へ古い版を入れようとすると、インストーラーが0x80070666 – Another version of this product is already installed. で失敗します。これは仕様です。自社インストーラーがこのエラーで止まらないようにしておく必要があります。
- 複数のビルドツールで作ったバイナリを混ぜる場合、再頒布可能パッケージは使ったうちで最も新しいビルドツール以上である必要があります。
そしてレガシー保守で見落としやすいのがWindows XPです。公式は、最新の再頒布可能パッケージにはWindows XP向けのランタイムサポートがもう無く、XPをサポートする最後の再頒布可能パッケージはバージョン16.7(ファイルバージョン14.27.29114.0)だと書いています。それより新しい版で配布・更新するとXP上のアプリは動きません。配布先にXPが残っているなら、再ターゲットは配布先のOSごと考え直す話になります。
もう1つ、混在させるときの例外があります。/GL(全プログラム最適化)でコンパイルした、あるいは /LTCGでリンクしたオブジェクトファイルと静的ライブラリは、マイナーバージョン違いでも互換がありません。コンパイルと最終リンクで完全に同じビルドツールを使う必要があり、違反するとC1047が出ます。第三者から /GL付きの .libを受け取っている場合、ここが再ターゲットの制約になります。
古いツールセットを足せるかは個別コンポーネント次第
2択のもう片方、ツールセットを足すほうです。MSB8020は「フォルダーが無い」と言っているだけなので、まず確認するのはそのフォルダーです。Windowsデスクトップのプロジェクトの場合、ツールセットのToolset.props / Toolset.targetsが置かれるのは $(VCTargetsPath)\Platforms\<アーキテクチャ>\PlatformToolsets\<番号>\ です($(VCTargetsPath) はVisual Studioのインストール先のMSBuild\Microsoft\VC\<VCのバージョン>\)。番号のフォルダーはMSBuild\Microsoft\VC\ の直下ではなく、アーキテクチャごとのPlatformToolsetsの下にある点に注意してください。このPlatformToolsets配下のフォルダー名が、そのままプロジェクトのプロパティの [プラットフォーム ツールセット] の選択肢になります。今回のビルド機では、エラーの発生元のパスからVS2026側のv180があることは分かりますが、その配下にv120が無かったのでMSB8020になりました。
足せるかどうかは、Visual Studioインストーラーの「個別のコンポーネント」に、その世代が並んでいるかで決まります。MSB8020のリファレンスは、ネイティブのツールはアーキテクチャごとに入れる必要があること(たとえばWindowsデスクトップARM64ならMSVC ARM64のコンポーネントを個別のコンポーネントから入れる)、UWPプロジェクトではUWP用のMSVCが別コンポーネントであること、そして不足しているツールセットがVisual Studioの拡張機能として提供されている場合があることを挙げています。「どの世代でも個別コンポーネントで足せる」とは書かれていません。手元のVisual Studioインストーラーを開いて、その番号があるかを確かめるのが唯一確実な方法です。
実務では再ターゲットを既定にして、古いツールセットを足すほうは、再ターゲットすると壊れるものが特定できているときだけ選びます。当たるのは次の場合です。
- ソースを持っていない第三者製の .libがv120以前でビルドされている。前の節のとおり2013以前は14系とバイナリ互換が無いので、この場合は再ターゲットできません。ライブラリを14系版に入れ替えるまで、古いツールセット側に留まる以外にありません。
- /GLや /LTCGでビルドされた入力が混ざっていて、同じビルドツールを揃えられない(C1047)。
- 配布先にWindows XPが残っていて、14系の最新再頒布可能パッケージを入れられない。
これらに当たらないなら、古いツールセットを足す選択は、ビルド機を1台ずつ手作業で特別扱いし続けることになります。新しく入った人のPCでも、CIサーバーを立て直したときも、同じインストール作業が要ります。手順書に残していないと、特定の1台でしかビルドできない状態になります。
なお再ターゲット自体は、メッセージが案内しているとおり [プロジェクト] メニュー、またはソリューションの右クリックから [ソリューションの再ターゲット] で行えます。.vcxprojのPlatformToolsetを直接書き換えても同じ効果になりますが、構成が複数ある場合は全構成ぶん揃っているかを確認してください。Debugだけ直してReleaseがv120のまま、という取りこぼしがよくあります。
まとめ
- MSB8020はMicrosoft.CppBuild.targetsから出ます。ソースの問題ではなく、$(PlatformToolset) が指すフォルダーがビルド機に無いという意味です。
- 世代は .vcxprojのPlatformToolset 1行に書いてあります。v140以降はメジャー番号が14で共通で、v120以前との間にだけバイナリ互換の壁があります。
- v145でビルドしても、実行時に載るのはmfc140ud.dllでした(Debug/x64実測)。再ターゲット先がv140〜v145のどれでも、配布先に入れるのは14系の再頒布可能パッケージ1つです。
- 再頒布可能パッケージは同時に1つだけで、古い版は後から入れられません(0x80070666)。XP向けは16.7が最後です。
- 古いツールセットを足すのは、v120以前でビルドされた第三者製ライブラリ、/GL混在(C1047)、XP配布のいずれかに当たるときだけにします。
