Visual Studio 2022(VS2022)で VB.NET の WinForms をデバッグ中、ブレーク中にログ出力の1行だけ直して「ホットリロード(編集の適用)」したいのに、毎回 ENC0005 で失敗してしまう——。本記事では、再現しやすい条件、現時点で判明している直接原因(AssemblyVersion のワイルドカード設定)、そして現場で使える回避策とバージョン運用の落とし所を整理します。
現象の整理:VS2022 の VB WinForms でホットリロードが ENC0005 になる
相談内容を要約すると、次の状況でホットリロードが機能しなくなります。
- Visual Studio 2022 で VB.NET の Windows フォーム(WinForms)をデバッグ実行
- Handles 句付きのイベントハンドラ(例:Button.Click、MenuItem.Click など)内でブレークポイント停止
- 停止したまま、ログ出力の行や軽微なコードを編集して「編集の適用(ホットリロード)」
- 結果:常に ENC0005 となり、変更が適用されない
調査中に分かった「再現しやすい条件」も重要です。
| 観点 | 再現・傾向 | 補足 |
|---|---|---|
| ターゲット | .NET Framework 4.8 の古い VB WinForms で発生しやすい | 「古い形式のプロジェクト」で顕著 |
| フォームのリソース | アイコンや文字列などリソースが1つでも登録されると再現しやすい | デザイナ生成コードやリソース関連の差分が絡む可能性 |
| イベント紐付け | Handles 句で強く再現 | AddHandler だと回避できるケースがある |
| .NET(SDK)側 | .NET 8 では一見出ないように見えるが、.NET 9 でも同様の現象が確認 | フレームワーク依存というより VS 側の問題の見立て |
ここでのポイントは、「VB.NET の WinForms」「Handles」「デバッグ中の編集適用」「ENC0005」という組み合わせが揃うと、小さな変更すら一切適用できなくなることです。ホットリロード(Hot Reload)というより、実態としては Edit and Continue(継続編集)の適用が拒否されている状態に近く、開発効率に直撃します。
ENC0005 とは何か:なぜ “ちょい修正” すら拒否されるのか
ENC0005 は、Visual Studio のデバッグ中にコード変更を適用しようとしたとき、その変更を現在のデバッグセッションへ適用できない場合に出る代表的なエラーの一つです。多くのケースでは「大きな変更をした」「メソッドシグネチャを変えた」「ラムダやイテレータなど制限に触れた」など、ユーザー側の変更内容が原因になります。
しかし今回の相談では、変更が「ログ出力の行」「文字列」「軽微な処理」でも発生し、しかも “常に” 起きます。つまり、変更内容の難しさというより、プロジェクトのビルド/メタデータ条件が Edit and Continue(ホットリロード)と衝突している疑いが濃厚になります。
直接原因:AssemblyVersion のワイルドカード(*)がホットリロード失敗の引き金
提供された再現用プロジェクトの解析結果として、次の2点がセットで存在していることが分かっています。
AssemblyInfo.vbに、ワイルドカード付きのバージョン指定がある- プロジェクト側で
<Deterministic>false</Deterministic>が設定されている
とくに重要なのが、次の設定です。
<Assembly: AssemblyVersion("2.0.*")>
この 「AssemblyVersion をワイルドカード(*)で自動採番している」ことが、ホットリロード失敗(ENC0005)の直接原因として報告されています。現象としては、VB WinForms で Handles 句を持つイベントハンドラに対して影響が出やすい、という形で表面化します。
注意したいのは、「ワイルドカードが悪いから完全にダメ」という単純な話ではなく、VS2022 の VB WinForms + Handles + デバッグ中編集適用という特定の経路で、内部的に噛み合わない状態が起きている、という点です。現時点の整理では、根本原因は Visual Studio 本体側の不具合とみなされ、開発チームへバグとして報告済み、という扱いになっています。
なぜ AssemblyVersion の “自動採番” が効くと壊れるのか(実務向けの理解)
ホットリロード(編集の適用)は、デバッグ中のプロセスに対して「差分パッチ」を当てるような動作になります。そのとき、アセンブリのメタデータ(バージョン情報やデバッグ情報など)が、想定外に変化している/一意性が崩れると、変更適用が拒否されやすくなります。
AssemblyVersion のワイルドカード(例:2.0.*)は、ビルドごとにアセンブリバージョンが自動更新されます。通常のビルド運用では便利でも、Edit and Continue のように「同じプロセスへ差分を当てたい」局面では、VS の内部処理と衝突しやすくなります。さらに VB の Handles はコンパイラ側の仕組み(イベント紐付け生成)とも絡むため、“見た目は小さな変更” でも VS が安全に適用できないと判断しやすい、という状況になり得ます。
まず確認すべきチェックリスト:あなたのプロジェクトも該当する?
再現条件に近いかを、手早く確認するためのチェックリストです。
| チェック項目 | 確認場所 | 該当すると起きやすいこと |
|---|---|---|
| AssemblyVersion に “*” がある | My Project\AssemblyInfo.vb など | ENC0005 でホットリロード不能 |
| Deterministic が false | .vbproj 内 | ビルド再現性が下がり、E&C と衝突する可能性 |
| Handles 句のイベントハンドラが多い | 各フォームのコード | ブレーク中の編集適用が失敗しやすい |
| フォームにリソースが登録されている | フォームのプロパティ、リソース設定 | 再現性が上がる傾向(観測情報) |
もし「AssemblyVersion が x.y.* になっている」なら、次の回避策(A)が最優先の検討対象になります。
回避策(A):AssemblyVersion を固定値にしてホットリロードを復活させる
最も効果が高く、しかも変更量が少ないのがこの方法です。ワイルドカードをやめ、固定値にします。
' 変更前
<Assembly: AssemblyVersion("2.0.*")>
' 変更後(例)
<Assembly: AssemblyVersion("2.0.0.0")>
これにより、質問のケースでは ENC0005 が出なくなり、ホットリロード(編集の即時反映)が正常に動作するようになった、という結果が得られています。
メリット/デメリット
| 観点 | 内容 |
|---|---|
| メリット | ENC0005 が発生しづらくなり、ホットリロードが実用的に戻る Handles 句のまま運用でき、デザイナの自動生成と相性が良い |
| デメリット | ビルドごとの自動インクリメントが止まる(AssemblyVersion の範囲では) 問い合わせ対応で「どのビルドか」を AssemblyVersion だけで判別しづらくなる |
ただし、このデメリットは運用で十分に吸収できます。次のセクションでは、AssemblyVersion を固定しつつ、ビルド識別を失わない実務的な方法をまとめます。
AssemblyVersion を固定しても困らない:ビルド識別を残す設計パターン
「AssemblyVersion の自動採番をやめると、ユーザー問い合わせで追跡できない」という不安はもっともです。そこでおすすめなのが、“互換性・参照解決” のための AssemblyVersion と、“問い合わせ追跡” のための情報を分離することです。
よく使う「バージョン3兄弟」を役割分担する
| 属性 | 主な用途 | この問題との相性 | おすすめ方針 |
|---|---|---|---|
| AssemblyVersion | 参照解決・互換性(強名や参照の観点) | ワイルドカードが引き金になり得る | 固定値にする(例:2.0.0.0) |
| AssemblyFileVersion | ファイルのバージョン(エクスプローラ等で確認) | 比較的自由に運用しやすい | CI のビルド番号や日付で更新する |
| AssemblyInformationalVersion | 製品表示用(About、ログ、問い合わせ用) | 柔軟。コミットIDなども入れられる | 「2.0.0+コミット」等で追跡性を確保 |
VB WinForms(.NET Framework 4.8)でも使える “追跡性の残し方” 例
たとえば AssemblyInfo.vb で、AssemblyVersion は固定し、FileVersion/InformationalVersion に追跡情報を寄せます。
<Assembly: AssemblyVersion("2.0.0.0")>
<Assembly: AssemblyFileVersion("2.0.1234.0")>
<Assembly: AssemblyInformationalVersion("2.0.0 (build 1234)")>
ここでの “build 1234” は、CI の連番、あるいは日付+時刻(例:20251212.1530)などにしてもよいです。重要なのは、ユーザーから送られてくるスクリーンショットやログに 追跡可能な文字列が残ることです。
さらに実務では次の工夫が効きます。
- アプリ起動時に、InformationalVersion をログの先頭に必ず出す(問い合わせ時に最短で特定できる)
- 「ヘルプ → バージョン情報(About)」に ビルド番号/コミットID を表示する
- クラッシュレポートやエラーダイアログに ビルド識別子 を埋め込む
これなら、ホットリロードの生産性を取り戻しつつ、「どのビルドか分からない」という運用上の痛みも最小化できます。
回避策(B):Handles をやめて AddHandler に置き換える(ただし設計の工夫が必要)
もう一つの回避策として、質問者側で効果があったのが Handles 句を使わず AddHandler でイベントを紐付ける方法です。
' 従来(Handles)
Private Sub mnuLogShown_Click(sender As Object, e As EventArgs) Handles mnuLogShown.Click
' …
End Sub
' 例:AddHandler を使う場合(Load で紐付け)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler mnuLogShown.Click, AddressOf mnuLogShown_Click
End Sub
メリット/デメリット
| 観点 | 内容 |
|---|---|
| メリット | 環境によっては ENC0005 の回避につながり、ホットリロードが通ることがある イベント紐付けがコードで明示され、動的に差し替えやすい |
| デメリット | デザイナからの「クリック一発でイベントハンドラ生成」が使いづらくなる 紐付け漏れ・二重登録などの事故が起きやすくなる フォームが大きいほど、初期化コードが肥大化しがち |
AddHandler 運用を “事故らせない” ためのテンプレ
AddHandler を採用するなら、場当たり的に Load に追記していくのではなく、イベント登録の責務を1か所に集約するのがおすすめです。
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BindEvents()
End Sub
Private Sub BindEvents()
AddHandler mnuLogShown.Click, AddressOf mnuLogShown_Click
AddHandler btnRun.Click, AddressOf btnRun_Click
AddHandler chkVerbose.CheckedChanged, AddressOf chkVerbose_CheckedChanged
End Sub
さらに、二重登録を避けたい場合は「BindEvents を一度しか呼ばない」「登録済みフラグを置く」などのガードも検討できます。フォームのライフサイクルが複雑なアプリ(MDI、画面再生成、動的UI)ほど、この設計が効きます。
ただし、今回のケースでは「根本は VS 側の不具合」という見立てなので、AddHandler はあくまで “回避できることがある” 手段です。最初に取り組むなら、変更範囲が小さい回避策(A)(AssemblyVersion固定)を優先し、AddHandler は第二候補に置くのが現実的です。
回避策(C):ホットリロードを割り切り、「アプリケーションの再起動」で開発効率を落としにくくする
バグが解消されるまでの応急策として、デバッグ中ツールバーの 「アプリケーションの再起動」 を積極的に使う運用も有効です。
- 手動で「停止 → 実行」を繰り返すより手数が少ない
- 同じデバッグ構成で素早く再実行できる
- ホットリロードが使えない局面でも “回転数” を維持しやすい
もちろん、ホットリロードの即時性には勝てませんが、「毎回 ENC0005 で止まる」状態に比べると、開発者のストレスと時間損失を現実的に抑えられます。
設定変更の具体手順:AssemblyInfo.vb と .vbproj の見つけ方
古い VB WinForms(.NET Framework 4.8 など)では、AssemblyInfo は次の場所にあることが多いです。
My Project\AssemblyInfo.vb- ソリューションエクスプローラで「すべてのファイルを表示」をオンにすると見つかりやすい
一方、SDKスタイル(.NET 8/9 など)では、プロジェクトファイル(.vbproj)側にバージョンが寄っていることもあります。その場合は、次のような項目を探します。
<PropertyGroup>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>2.0.1234.0</FileVersion>
<Version>2.0.0</Version>
</PropertyGroup>
今回の論点は「AssemblyVersion のワイルドカード」です。どこに書かれていても、最終的に “*” が混ざっているなら、固定値へ寄せるのが最短の改善につながります。
よくある落とし穴:フォームのリソースが絡むと再現しやすい理由(現場視点)
観測として「フォームにリソース(アイコンや文字列など)が1つでも登録されていると再現しやすい」という情報があります。ここは断定よりも、実務上の注意点として捉えるのがおすすめです。
- WinForms はデザイナ生成コード(Designer.vb)とリソース(.resx)が絡む
- デバッグ中の編集適用では、ユーザーが触っていない箇所も含めて差分の整合性チェックが走る可能性がある
- 結果として “軽微な変更” のはずが、VS 内部的には「適用が難しい形」に見えてしまうことがある
もし同じプロジェクト内でも「リソースの多いフォームだけ失敗しやすい」という偏りがあるなら、切り分けのヒントになります。具体的には、まずリソースが少ない画面で同様の手順を試し、差が出るかを確認すると、原因追跡が早くなります。
どれを選ぶべきか:優先順位別のおすすめ
回避策は複数ありますが、現場の意思決定は「何を優先するか」で決まります。迷ったときの指針を表にまとめます。
| 優先したいこと | おすすめ | 理由 |
|---|---|---|
| ホットリロードを使って開発速度を上げたい | (A) AssemblyVersion を固定 | 変更範囲が小さく、Handles も維持できる |
| 自動バージョン付けを崩したくない | (A)+「FileVersion/InformationalVersion で追跡」 | AssemblyVersion 固定でも運用上の追跡性は維持できる |
| コード生成に依存せずイベント配線を制御したい | (B) AddHandler に集約 | イベント紐付けの見通しは良くなるが設計が必要 |
| とにかく今すぐ作業を止めたくない | (C) アプリケーションの再起動運用 | 根治ではないが、作業の詰まりを減らせる |
将来的な解決:VS2022 の更新で直る可能性に備える
この問題は、現時点では Visual Studio 2022 本体側(VB WinForms のホットリロード周り)の不具合として扱われ、Developer Community にバグ報告済み、という位置づけです。また、.NET Framework 4.8 だけでなく .NET 9 でも同様の問題が確認された、という経緯から、ターゲットフレームワーク依存ではなく VS 側の問題である可能性が高い、と整理されています。
そのため、長期的には次の方針が現実的です。
- 当面は (A) などのワークアラウンドで生産性を確保する
- Visual Studio 2022 のアップデートで修正が入ったタイミングで、必要なら設定を戻す
- 戻す場合も、AssemblyVersion の運用は「互換性のための固定」と「追跡のための情報」を分離する設計が安全
まとめ:ENC0005 に悩んだら、まず AssemblyVersion の “*” を疑う
VS2022 の VB.NET WinForms で、デバッグ中にホットリロード(編集の適用)をしたら ENC0005 で必ず失敗する——この手の症状は、コード変更の難しさよりも プロジェクト設定と VS の相性問題で起きていることがあります。
- 最優先で確認:
AssemblyVersion("x.y.*")になっていないか - 最小変更での回避:AssemblyVersion を固定値にする
- 運用の落とし所:追跡は FileVersion / InformationalVersion / ログ出力で担保する
- 次善策:Handles を AddHandler に寄せる(設計を整える)
- 応急運用:デバッグ中の「アプリケーションの再起動」を活用
ホットリロードが効かない状態は、開発者の集中力と速度を確実に奪います。まずは “*” を外して安定させ、必要な追跡性は別の属性やログに移す——この分離が、VB WinForms を長く保守する現場ほど効いてきます。

コメント