Visual Studio 2022 で DLL をビルドしたときに「Cannot register assembly ~ Access to the registry key… is denied.」と怒られ、管理者実行でも直らない――そんなハマり方をしたことはないでしょうか。この記事では、LogEvents.dll を例に、原因の正体と実際に効いた解決策、そして再発させないための運用までを詳しく解説します。
エラーの現象と前提環境
まず、今回のケースを整理します。
- 環境:Visual Studio 2022(64ビット版)、Windows 11
- 対象:.NET Framework のクラスライブラリ(例:LogEvents.dll)
- ビルド時のエラー:
error MSB3216: Cannot register assembly "…LogEvents.dll" - access denied. Access to the registry key 'HKEY_CLASSES_ROOT\CLSID{E08E6F37-...}' is denied. - Visual Studio を「管理者として実行」しても解消しない
- 同僚の PC では同じソースでも再現しない
- 一度は
gacutil /iによる GAC 登録は成功したが、その後また同じエラーに戻る - 問題の CLSID キーにレジストリ権限を付与しても改善しない
MSBuild のエラー番号は MSB3216 で、公式メッセージとしては「Cannot register assembly ‘…’ – access denied. Please make sure you’re running the application as administrator.」というものです。
一見すると「GAC 登録に失敗しているのでは?」と思いがちですが、ここに今回の落とし穴があります。
GAC 登録と COM 登録はまったく別物
まず押さえておきたいのは、GAC(グローバル アセンブリ キャッシュ)への登録と、COM 登録(レジストリへの CLSID 登録)は別ルートだという点です。
| 項目 | GAC 登録 | COM 登録(RegAsm / Register for COM interop) |
|---|---|---|
| 目的 | .NET アセンブリを共有ライブラリとして格納し、強名で解決する | COM クライアント(VB6 / Excel / VBA / C++ など)から .NET クラスを呼び出せるようにする |
| 主な操作 | gacutil /i MyAssembly.dll、MSI など | RegAsm.exe MyAssembly.dll、もしくは VS の「Register for COM interop」 |
| 触る場所 | GAC ストア(ファイル システム) | レジストリ(HKCR\CLSID など) |
| 今回のエラーの関係 | 直接関係なし(GAC だけなら CLSID には触らない) | 今回の MSB3216 はこちら側の失敗 |
エラー メッセージに HKEY_CLASSES_ROOT\CLSID{...} と出ている時点で、GAC ではなく COM 登録のフェーズでコケていると判断できます。
なぜ「管理者として実行」なのにアクセス拒否になるのか
「レジストリに書くなら管理者権限でしょ? VS も管理者で起動してるのに、なぜアクセス拒否?」と感じるかもしれません。理由はいくつかの要素が絡んでいます。
Visual Studio 2022 は 64 ビットプロセス
VS 2022 以降は IDE 自体が 64 ビットプロセスになりました。そのため、「Register for COM interop」有効時の COM 登録も基本的には 64 ビット側のレジストリ ビュー(HKCR\CLSID → 実体は HKLM\SOFTWARE\Classes\CLSID)に書き込もうとします。
ここに対して、
- 社内ポリシーやグループポリシーで
CLSID以下への書き込みが制限されている - 既存の CLSID キーの所有者が
TrustedInstallerや別ユーザーになっており、管理者でも変更できない - EDR / ウイルス対策製品が CLSID 書き込みをブロックしている
といった条件が重なると、「管理者であってもアクセス拒否」という状態が簡単に発生します。
GAC 登録は通るのに COM 登録だけ失敗するワケ
GAC 登録(gacutil /i)は、内部的には GAC ストアへのファイル追加と GAC 内部のカタログ更新が主であり、HKCR\CLSID には触れません。一方、COM 登録は RegAsm 相当の処理を行い、レジストリに CLSID、ProgID、タイプライブラリへのパスなどを大量に書き込みます。
そのため、
- GAC だけなら通る
- COM 登録のタイミングで初めて
HKCR\CLSIDにアクセスし、そこでアクセス拒否
という挙動になります。今回の「一度は gacutil /i に成功したが、ビルド時は失敗する」というのは、まさにこの違いが表面化した例だと言えます。
決定打となった解決策:「Register for COM interop」をオフにする
今回、最終的に問題を解決したのは非常にシンプルな対応でした。
プロジェクトの「Register for COM interop(COM 相互運用の登録)」のチェックを外す。
手順は以下の通りです(C# プロジェクト例)。
- Visual Studio で該当プロジェクトを右クリックし、「プロパティ」を開く
- 「ビルド」タブを開く
- 下の方にある 「COM 相互運用の登録」(Register for COM interop)のチェックを外す
- プロジェクトを再ビルド
これだけでビルド中の COM 登録処理そのものが行われなくなるため、MSB3216 のエラーは発生しなくなります。CI / Team Build などの環境で「ビルド サーバーでは COM 登録したくない」というケースでも、この方法が定番の回避策として紹介されています。
つまり、
- ログ出力用など、COM 経由で呼び出される予定のない DLLであれば、そもそも COM 登録は不要
- COM 登録が必要な DLL であっても、ビルドのたびに自動登録させる必要はない(インターフェイスや配置パスが変わったときだけ登録し直せばよい)
という設計にしておくと、開発・ビルド環境でのトラブルを大きく減らせます。
COM 登録が本当に必要な場合の安全なやり方
とはいえ、Excel アドインや VB6 から呼び出されるライブラリなど、どうしても COM 登録が必要なケースもあります。その場合は、以下の方針をおすすめします。
- プロジェクトの「Register for COM interop」はオフにする
- COM 登録は ビルド後イベントや別途用意したバッチファイルで明示的に行う
- COM クライアントのビット数(32/64)と RegAsm のビット数を必ず合わせる
- GAC 登録が必要な場合は
gacutil.exeを別途呼び出す
32ビット COM クライアント向け登録(WOW6432Node配下)
Excel / Access / VBA など、多くの Office は 32 ビット版がまだまだ現役です。その場合は 32 ビットの RegAsm.exe を使って登録します。
"%WINDIR%\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" "$(TargetPath)" /codebase /nologo
ポイント:
Frameworkパス配下のRegAsm.exeは 32 ビット版- これを使うと、レジストリの
WOW6432Node配下に CLSID 情報が書き込まれ、32 ビット COM クライアントから参照できるようになります
GAC への登録(.NET Framework 用)
GAC にも登録したい場合は、Visual Studio の「開発者用コマンド プロンプト」で gacutil.exe を呼び出します(パスは環境により異なります)。ビルド後イベントで呼ぶなら例として以下のようになります。
"%ProgramFiles(x86)%\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\gacutil.exe" /i "$(TargetPath)"
実際には where gacutil コマンドで実体パスを確認してから、それを使うと安全です。
64ビット COM クライアント向け登録
COM クライアントが 64 ビット(たとえば 64 ビット版の Excel や自作 C++ アプリ)である場合は、64 ビット版の RegAsm.exe を使う必要があります。
"%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe" "$(TargetPath)" /codebase /nologo
ビット数が合っていないと、「登録したのにクライアントがクラスを見つけられない」「32 ビット Excel では動くが 64 ビット Excel では動かない」といった、原因が分かりづらい不具合につながるので注意してください。
/codebase オプションの扱いについて
/codebase オプションは、DLL の物理パスをレジストリに書き込むため、開発環境では便利な一方で、本番環境ではDLL Hell の原因になりがちです。更新のたびに古いアプリへ影響が波及しうるため、一般的には MSI インストーラなどで配置と登録を一元管理する方が安全とされています。
開発用 PC では /codebase を使い、配布・本番環境では MSI などの正式なインストーラで登録する、といった使い分けをおすすめします。
ビット数とクライアントの対応表
| COM クライアント | 推奨 RegAsm | 書き込まれる場所の例 |
|---|---|---|
| 32 ビット Excel / Access / VB6 | %WINDIR%\Microsoft.NET\Framework\... | HKCR\WOW6432Node\CLSID\{…} |
| 64 ビット Excel / 64 ビット C++ アプリ | %WINDIR%\Microsoft.NET\Framework64\... | HKCR\CLSID\{…} |
| AnyCPU ライブラリ(両対応を狙う) | 32 ビット版と 64 ビット版の RegAsm を両方呼ぶ | 両方の CLSID ツリーに登録 |
32/64ビットとレジストリの見え方をもう少し整理
今回のような「アクセス拒否」トラブルでは、レジストリの仕組みをざっくり理解しておくと原因切り分けがしやすくなります。
HKCR(HKEY_CLASSES_ROOT)は実体としてはHKLM\SOFTWARE\ClassesとHKCU\Software\Classesの合成ビュー- 64 ビット OS では、32 ビットアプリから見ると
HKLM\SOFTWARE\Classes\WOW6432Node以下が 32 ビット用のビューとして見える - VS 2022 は 64 ビットプロセスのため、プロジェクトの COM 自動登録は 64 ビット側に書き込もうとする
つまり、
- 32 ビット Excel で使いたい DLLなのに、64 ビット側にだけ登録されている
- 64 ビット側の
CLSIDにだけアクセス制限がかかっている
といった条件によって「ビルド時の自動登録は失敗するのに、手動で 32 ビット RegAsm すると動く」といった、不思議な現象が起こりえます。
原因切り分けに使えるチェックリスト
同様のエラーにハマったときに確認したいポイントを表にまとめます。
| チェック項目 | 確認ポイント | 想定される対処 |
|---|---|---|
| VS が本当に管理者権限か | IDE 右上のタイトルバーに「Admin」表示があるか | ない場合は VS を右クリック→「管理者として実行」で再起動 |
| 32/64 ビットどちらに登録しようとしているか | エラーに出ているキーが HKCR\CLSID か HKCR\WOW6432Node\CLSID か | クライアントのビット数と RegAsm のビット数を合わせるようスクリプトを修正 |
| レジストリの ACL/所有権 | HKLM\SOFTWARE\Classes\CLSID や WOW6432Node\Classes\CLSID の権限を確認 | 所有権が TrustedInstaller などに固定されている場合、ポリシーや運用ルールを見直し |
| セキュリティ製品・グループポリシー | EDR / AV のログや GPO で HKCR\CLSID 書き込み禁止が設定されていないか | インフラ担当に相談し、開発用 PC だけ例外設定をしてもらう |
| アセンブリの読み込み失敗 | Fuslogvw.exe(Assembly Binding Log Viewer)で依存 DLL の解決エラーがないか | 依存アセンブリが見つからないと COM 登録も失敗するので、参照設定や配置を見直す |
| IDE / .NET の破損 | 他の COM 登録プロジェクトでも同じように失敗するか | VS の修復インストールや .NET 再インストール、Windows Update 適用を検討 |
「同僚の PC では再現しない」理由
同じソースコードなのに、ある PC だけエラーが発生する――こうした「再現しない」問題の多くは、以下のような環境差に起因します。
- 既に別バージョンの DLL が COM 登録されており、その CLSID キーの権限が変わっている
- ローカル管理者ではなく、ドメインポリシーで制限されたロールで VS を動かしている
- EDR / AV のポリシーが PC ごとに微妙に異なっている
- 過去のアンインストールでレジストリ キーだけ残ってしまい、所有者が謎の状態になっている
このような「環境差」に振り回されないためにも、プロジェクトで自動 COM 登録を行わず、必要な人だけ手動スクリプトで登録するという運用に切り替えるのが安全です。
プロジェクト ファイル側で Register for COM interop を明示的にオフにする
GUI からチェックを外すだけでなく、.csproj 側でも設定しておくと、CI / チーム開発でも意図がブレません。
<PropertyGroup>
<RegisterForComInterop>false</RegisterForComInterop>
</PropertyGroup>
もし「Debug では開発者マシンで自動登録したいが、Release では CI で登録させたくない」といったニーズがあるなら、条件付きでフラグを切り替えることもできます。
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<RegisterForComInterop>true</RegisterForComInterop>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<RegisterForComInterop>false</RegisterForComInterop>
</PropertyGroup>
ただし、今回のように環境依存のアクセス拒否が起きる場合は、Debug も含めて一律 false にして、必要な開発者だけ手動登録スクリプトを用意するほうが安定します。
実運用のコツ:登録の頻度と責任範囲を決める
COM 登録を行う場合、次のようなルールをチーム内で決めておくとトラブルを減らせます。
- 誰が COM 登録を行うのか(各開発者、リード、インフラ担当など)
- どこで 登録するのか(開発機だけ、本番サーバーだけなど)
- いつ 登録し直すのか(インターフェイス変更時のみ、毎回のデプロイ時など)
- どのツールを使うのか(RegAsm / MSI / 独自ツールなど)
特に本番環境では、
- 勝手に
RegAsm.exeやgacutil.exeを叩かない - インストーラ(MSI、WiX、Setup プロジェクトなど)に登録処理を閉じ込める
- ロールバックやバージョン差し替えの手順を必ず決めておく
といったポリシーを徹底しておくと、長期的な保守が楽になります。
今回のケースに当てはめる意思決定フロー
最後に、「LogEvents.dll の MSB3216 問題」を例に、どう判断していけばよいかをフローチャート風にまとめます。
- その DLL は本当に COM クライアントから呼び出される必要があるか?
- いいえ:→ すぐに「Register for COM interop」をオフにして終了
- はい:→ 2 へ
- 32 ビットクライアントか、64 ビットクライアントか?
- 32 ビットのみ:32 ビット RegAsm だけを使ったスクリプトを作成
- 64 ビットのみ:64 ビット RegAsm だけを使ったスクリプトを作成
- 両方:両方の RegAsm を順に呼ぶスクリプトを作成
- 登録スクリプトは開発者ごとに実行するか、CI / インストーラ側で実行するか?
- 開発者ごと:Post-build イベントやバッチで自動実行しても良い(ただし権限制限に注意)
- CI / インストーラ:ビルド環境では COM 登録を行わず、インストール フェーズで一括登録
- MSB3216 が出た場合は?
- プロジェクトの「Register for COM interop」がオンになっていないか確認
- オンならオフにし、COM 登録は手動スクリプトに完全移行
- それでも必要な手動登録が失敗する場合は、権限・ビット数・セキュリティ製品・既存 CLSID の状態を順に確認
まとめ:MSB3216 と戦わない Visual Studio 運用
この記事で扱ったポイントをまとめます。
- MSB3216「Cannot register assembly … access denied」は GAC ではなく COM 登録フェーズのエラー
- エラーに
HKCR\CLSID{...}が出ているときは、Register for COM interop によるレジストリ書き込みが失敗しているサイン - VS 2022 は 64 ビット化しており、既定では 64 ビット側レジストリに書き込もうとするため、ポリシーや ACL と衝突しやすい
- COM 登録が不要な DLL は、迷わず「Register for COM interop」をオフにするのが正解
- COM 登録が必要な DLL でも、自動登録は避けて RegAsm / gacutil をビット数を合わせて明示的に実行した方が安全
- 32/64 ビットや WOW6432Node の仕組みを軽く理解しておくと、「登録したのに見えない」トラブルの原因特定が楽になる
- 最終的には、「誰が・どこで・いつ・どのツールで」 COM 登録をするかという運用ルールを決めておくことが、トラブルを防ぐ一番の近道
LogEvents.dll のケースと同じように、Visual Studio でのビルド時 COM 登録エラーに悩んでいる方は、まずは「Register for COM interop」をオフにしてビルドが通るかを確認し、そのうえで本当に必要な範囲だけに絞って COM 登録を行う、というスタイルに切り替えてみてください。

コメント