更新履歴(4件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- 登録先のレジストリキーを4通りすべて示し、総当たりで探すスクリプトと結果の読み取り方を追加しました。あわせてSTA/MTAとマーシャリングの用語定義、メッセージループがないと固まる仕組みの図、Regasmとマニフェストのコード例を追加しています。
- 本文中の関連記事へのリンクの文言が、リンク先の現在のタイトルと食い違っていたのを、実際のタイトルに揃えました。本文の内容は変えていません。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.21589787)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「COM/OCX/ActiveX開発でハマる登録とbitnessの罠」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21589787 https://staging.comcomponent.com/blog/2026/04/15/001-com-ocx-activex-pitfalls-visual-studio-bitness-admin-rights/
- DOI(最新版)
- 10.5281/zenodo.21589787
- DOI(この版)
- 10.5281/zenodo.21732804
COM コンポーネントや OCX / ActiveX の案件は、コードそのものよりも 実行環境・登録・ホスト・権限 の境界でハマりやすいです。
ありがちなのは、こんな症状です。
- ビルドは通るのに、起動すると
0x80040154。 - 自分の開発機では動くのに、他の PC では動かない。
- 実行時は動くのに、Visual Studio の Designer だけ死ぬ。
- 管理者で起動したら動くのに、通常権限では壊れる。
regsvr32を叩いているのに、なぜか直らない。
このへんは、個別のバグというより、COM の前提がどこかで噛み合っていない 状態です。
COM / ActiveX / OCX の言葉自体を先に整理したい場合は、まず COM / ActiveX / OCX とは何か - 違いと関係をまとめて解説 を読むと全体像を掴みやすいです。 この記事では、その次の段階として、実際にどこで詰まりやすいか を、Visual Studio のビット数や管理者権限まわりも含めて整理します。
1. まず結論
先に、実務で効く言い方をするとこうです。
- COM / OCX / ActiveX のトラブルは、コードのロジックより bitness(32bit / 64bit)・登録先・ホスト・権限 の不一致で起きることが多いです。
- Visual Studio 2022 は 64bit プロセス なので、昔は通っていた 32bit 前提のデザイン時連携が、そのままでは壊れます。12
regsvr32は 何でも登録する魔法のコマンドではありません。ネイティブの in-proc COM サーバー(DLL / OCX)向けです。 .NET Framework を COM 公開するならRegasm.exe、.NET 5+ / .NET 6+ / .NET 8+ では生成された.comhost.dllを登録する流れになります。3456- 「管理者で動くなら OK」は危険です。per-user 登録でたまたま見えている、あるいは 本来インストーラーでやるべき登録を開発機だけ手で済ませている ことが珍しくありません。378
つまり、COM / OCX / ActiveX を扱うときは、まずこの 4 軸で見るのが安全です。
- どのプロセスがホストするのか
- そのプロセスは 32bit か 64bit か
- どこに登録されているのか(HKCU / HKLM、32bit view / 64bit view)
- その操作や実行に管理者権限が要るのか
この記事の知識マップ
COM・OCX・ActiveXのトラブルは、コードの不具合よりbitness・登録手段・登録スコープ・権限の不一致で起きがちです。Visual Studio 2022はdevenv.exeが64bitプロセスのためx86固定のActiveXをデザイン時に直接ロードできず、regsvr32はネイティブのin-proc COMサーバー向けで、.NET Frameworkの登録にはRegasm.exeを使い分ける必要があります。HKEY_CLASSES_ROOTはHKLMとHKCUのmerged viewで、CLSIDサブキーはWow6432Nodeを介して32bit/64bitで別になるため、別viewやper-userにしか登録されていないだけでもClass not registeredになります。STAではメッセージループとCoInitializeExが呼び出しを配達する仕組みそのものであり、これを止めるとデッドロックします。
flowchart LR
accTitle: COM/ActiveX登録トラブルの知識マップ
accDescr: COM・OCX・ActiveXの登録トラブルが、Visual Studio 2022の64bit化・WOW64レジストリリダイレクター・regsvr32やRegasmといった登録手段の使い分け・HKCUとHKLMの登録スコープ・STAのメッセージループとどう関係するかを示す図
com_registration_pitfalls["COM/OCX/ActiveXの登録トラブル"]
visual_studio_2022["Visual Studio 2022"]
wow64_registry_redirector["WOW64レジストリリダイレクター"]
activex["ActiveX"]
ocx["OCX"]
bitness_match_requirement["bitness一致要件"]
com["COM(コンポーネントオブジェクトモデル)"]
hkcu_classes["HKEY_CURRENT_USER\Software\Classes"]
clsid["CLSID(Class ID)"]
wow6432node["WOW6432Node"]
regsvr32["regsvr32"]
in_proc_com["In-proc COM(DLLサーバー)"]
regasm["Regasm.exe"]
dotnet_framework[".NET Framework"]
dotnet_comhost[".NET(5以降)のEnableComHosting/.comhost.dll"]
dotnet[".NET(Core以降)"]
file_system_redirector["WOW64ファイルシステムリダイレクター"]
admin_rights["管理者権限"]
class_not_registered_error["0x80040154(Class not registered)"]
per_user_registration["per-user登録"]
hkcr["HKEY_CLASSES_ROOT(HKCR)"]
hklm_classes["HKEY_LOCAL_MACHINE\Software\Classes"]
per_machine_registration["per-machine登録"]
activex_license["ActiveXのデザイン時/実行時ライセンス"]
aximp["AxImp(ActiveX Control Importer)"]
type_library["型ライブラリ(TLB)"]
axhost["AxHost"]
com_apartment_model["COMアパートメントモデル(STA/MTA)"]
message_loop["メッセージループ"]
coinitializeex["CoInitializeEx"]
sta_blocking_deadlock["STAスレッドのブロックによるデッドロック"]
com_marshaling["マーシャリング"]
reg_free_com["Reg-Free COM(登録不要COM)"]
assembly_manifest["アセンブリマニフェスト"]
com_localserver["COM LocalServer(別プロセスCOMサーバー)"]
progid["ProgID(Programmatic Identifier)"]
com_registration_pitfalls -->|"利用する"| wow64_registry_redirector
com_registration_pitfalls -->|"利用する"| visual_studio_2022
visual_studio_2022 -.->|"両立しない"| activex
ocx -->|"前提とする"| bitness_match_requirement
ocx -->|"実装を担う"| activex
com -->|"利用する"| hkcu_classes
clsid -.->|"に保存される"| wow6432node
regsvr32 -->|"利用する"| in_proc_com
regasm -->|"推奨される対応"| dotnet_framework
regsvr32 -->|"用いるのは非推奨"| dotnet_framework
dotnet_comhost -->|"推奨される対応"| dotnet
regsvr32 -->|"利用する"| file_system_redirector
regsvr32 -.->|"前提とする"| admin_rights
wow64_registry_redirector -.->|"原因になり得る"| class_not_registered_error
per_user_registration -.->|"原因になり得る"| class_not_registered_error
hkcr -->|"内容を継承する"| hklm_classes
hkcr -->|"内容を継承する"| hkcu_classes
per_machine_registration -->|"前提とする"| admin_rights
activex -.->|"前提とする"| activex_license
aximp -->|"前提とする"| type_library
aximp -->|"利用する"| axhost
axhost -->|"実装を担う"| activex
com_apartment_model -.->|"前提とする"| message_loop
com_apartment_model -->|"前提とする"| coinitializeex
message_loop -->|"防止する"| sta_blocking_deadlock
com_apartment_model -->|"前提とする"| com_marshaling
reg_free_com -->|"利用する"| assembly_manifest
reg_free_com -->|"軽減する"| com_registration_pitfalls
com_localserver -->|"前提とする"| clsid
progid -->|"前提とする"| clsid
hklm_classes -->|"利用する"| wow64_registry_redirector
clsid -->|"に保存される"| hkcr
activex -.->|"前提とする"| com_apartment_model
regsvr32 -->|"利用する"| dotnet_comhost
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全34件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. 症状から逆引きすると、だいたいこう見える
| 症状 | まず疑うこと | よくある本当の原因 |
|---|---|---|
0x80040154 Class not registered |
未登録 | 実際には「別ビット数側にだけ登録されている」「そのユーザーにしか登録されていない」ことも多い |
DllRegisterServer failed: 0x80070005 |
権限不足 | 標準ユーザーで登録しようとしている、post-build で昇格なしに登録している |
| VS2022 で Designer だけ壊れる | Designer の制約 | 32bit COM / ActiveX を 64bit の Visual Studio 側が直接読めない |
| 管理者で起動したときだけ動く | 権限の問題 | 本質は権限そのものより、登録スコープやインストール設計のズレであることが多い |
| 64bit アプリから 32bit OCX を呼べない | COM の仕組みの制約 | in-proc サーバーはホストと同じ bitness でないとロードできない |
| UI スレッドでは動くのにバックグラウンドスレッドで固まる | スレッドモデル | STA / MTA、CoInitializeEx、メッセージループの前提違反 |
0x80040154 は公式には REGDB_E_CLASSNOTREG で、文字通り Class not registered です。9
また、regsvr32 の 0x80070005 は、公式にも 管理者権限がなくレジストリや System32 に書けない ケースとして説明されています。10
ここで大事なのは、エラー名をそのまま信じすぎない ことです。
たとえば Class not registered は「完全に未登録」とは限らず、違うレジストリ view に登録されている、そのユーザーにしか見えない場所に登録されている だけでも起きます。117
3. Visual Studio のビット数でハマる
3.1 Visual Studio 2022 は 64bit になった
ここは、今の COM / ActiveX 開発で一番ハマりやすい点です。
Visual Studio 2022 は devenv.exe が 64bit only です。1
そのため、WinForms のデザイン時体験では、Visual Studio 側が 32bit コンポーネントを直接ロードできません。Microsoft も、Visual Studio 2022 は 64bit プロセスなので、32bit の .NET / COM / ActiveX コンポーネントをロードできないと明記しています。2
つまり、昔こうだったものが、
- プロジェクトは x86
- 参照先の ActiveX も x86
- Visual Studio 自体も 32bit
という前提でなんとなく成立していたのに、VS2022 では
- 実行時のアプリは x86 で動ける
- でも Designer は 64bit の Visual Studio 側で動く
というねじれが起きます。 この結果、実行時は生きているのに Designer だけ落ちる という、原因の見えにくい状態になります。実行時のホストはアプリのプロセス、デザイン時のホストは Visual Studio のプロセスであり、ホストが別物なので bitness も別 だからです。2
3.2 AnyCPU にしたら解決する、とは限らない
これもよくある誤解です。
AnyCPU は、依存先まで中立になる魔法ではありません。
Microsoft の説明でも、AnyCPU に見えるコンポーネントでも、その先で 32bit 固定の COM / ActiveX を参照していれば、Visual Studio 2022 のデザイン時には問題になります。2
なので、AnyCPU にしてエラーが消えないときは、
- そのアセンブリの先に 32bit ネイティブ依存がないか
- ActiveX / OCX が x86 固定ではないか
- Designer 時だけ読み込まれるコードがないか
を疑ったほうが早いです。
3.3 System32 と SysWOW64 の罠
Windows x64 では、名前の印象と実態がずれているので混乱しがちです。
Microsoft Learn では、x64 Windows 上の %windir%\System32 は 64bit アプリケーション用 と説明されています。32bit 側は WOW64 のファイルシステムリダイレクタによって別の場所へ誘導されます。12
レジストリも同様で、WOW64 のレジストリリダイレクタが 32bit / 64bit に 別の論理 view を見せます。11
このため、手元で切り分けるときは、どのビット数の regsvr32 を使っているか を明示したほうが安全です。
# 64bit DLL / OCX を登録したい場合
C:\Windows\System32\regsvr32.exe vendor.ocx
# x64 Windows 上で 32bit DLL / OCX を登録したい場合
C:\Windows\SysWOW64\regsvr32.exe vendor.ocx
間違った側の regsvr32 で登録したときが厄介です。登録自体は成功するのに、目的のプロセスからは見えません。
結果として、
regsvr32は成功した- でもアプリからは
0x80040154 - レジストリを見ると、あるように見える
- でも見ているのは別 view
3.4 regsvr32 で何でも登録できるわけではない
ここも誤解の多いところです。
ネイティブの in-proc COM サーバーは、通常 DllRegisterServer / DllUnregisterServer をエクスポートして自己登録をサポートします。3
regsvr32 は、そういう DLL / OCX に対して使う道具です。4
一方で、.NET Framework のアセンブリを COM から使いたい場合は、基本は Regasm.exe です。Microsoft Learn でも、COM で使用するアセンブリの登録には Regasm.exe を使う と説明されています。514
さらに、.NET 5+ / .NET 6+ / .NET 8+ の COM 公開では少し事情が変わり、<EnableComHosting>true</EnableComHosting> を設定してビルドすると *.comhost.dll が生成され、それを regsvr32 で登録します。6
つまり、ざっくり分けるとこうです。
| 公開したいもの | 代表的な登録手段 |
|---|---|
| ネイティブ C++ の DLL / OCX | regsvr32 |
| .NET Framework アセンブリを COM 公開 | Regasm.exe |
| .NET 5+ / 6+ / 8+ の COM 公開 | 生成された .comhost.dll を regsvr32 |
Regasm.exe は Developer Command Prompt / Developer PowerShell から実行します。よく使うのは次の 3 通りです。14
:: 1) アセンブリ内の公開クラスを登録する
regasm myTest.dll
:: 2) タイプライブラリも生成して登録する (VBA / VB6 など、TLB が要る相手向け)
regasm myTest.dll /tlb:myTest.tlb
:: 3) レジストリを直接変更せず、内容を .reg ファイルへ書き出して確認する
regasm myTest.dll /regfile:myTest.reg
解除は /unregister です。/tlb で登録したタイプライブラリは、/tlb と /unregister を 一緒に指定 して解除します。
regasm myTest.dll /tlb:myTest.tlb /unregister
ここで踏みやすい制約が 2 つあります。14
- GAC に入れないアセンブリは
/codebaseが要ります。/codebaseは登録時のファイルパスをレジストリに記録するため、あとでアセンブリを移動すると起動に失敗します。また Microsoft は、/codebaseを付けるアセンブリは 厳密名 (strong name) 付きにすることを強く推奨 しています。 /regfileは/unregisterや/tlbと併用できません。 また/regfileが書き出すのはマネージドクラスのエントリだけで、TypeLibIDとInterfaceIDは出力されません。「.regを配れば登録が済む」と考えると足りなくなります。
この違いを混ぜると、よくあるのが、
- マネージド DLL に
regsvr32をかける - 当然
DllRegisterServerが見つからない - そこで「DLL が壊れている」と誤解する
というパターンです。
タイプライブラリが必要な世界(VBA / VB6 / 一部の早期バインディング相手)では、さらに TLB の生成と登録 も別問題になります。 .NET Framework では Regasm.exe /tlb でタイプライブラリを生成・登録できますし、Microsoft も「型の登録」と「タイプライブラリの登録」は別活動だと説明しています。15
4. 管理者権限でハマる
4.1 post-build で登録していて、管理者でしか成功しない
Visual Studio の C++ ビルドでは、build event や custom build step で regsvr32.exe を呼ぶこと自体は普通にできます。Microsoft Learn でも、post-build event の例として regsvr32.exe による登録が挙がっています。16
ただし、できること と 安全なこと は別です。
標準ユーザーで regsvr32 を実行すると、レジストリや System32 へ書き込めず 0x80070005 になることがあります。Microsoft の KB でも、その原因は 管理者権限がないこと だと整理されています。10
ここで起きがちなのは、
- Visual Studio を通常権限で起動すると build は通る
- でも post-build の登録だけ失敗する
- 失敗ログを見逃す
- 直前の古い登録が残っているので、手元ではたまたま動く
- クリーン環境では当然動かない
という事故です。
この手のプロジェクトでは、ビルドと登録を分ける のが基本です。
- ビルドはバイナリを作るだけ
- 登録は明示的な install step / script / installer で行う
- CI では「登録を要する step」を build と別ジョブにする
この分離だけで、かなり事故が減ります。
4.2 per-user 登録と per-machine 登録を混ぜると壊れる
COM は HKCR を見ている、という覚え方だけだと、ここで詰まります。
実際には Microsoft Learn にある通り、COM は HKEY_CURRENT_USER\Software\Classes を先に見て、その後にコンピューター全体の情報を扱います。3
また、HKEY_CLASSES_ROOT は HKLM\Software\Classes と HKCU\Software\Classes の merged view です。717
つまり、
- 開発者 A のユーザーで手動登録した
- A のアカウントでは動く
- 開発者 B では動かない
- サービスアカウントでも動かない
- 管理者で動かすと挙動が変わる
ということが普通に起きます。
さらに Microsoft は、管理者権限を必要とするアプリケーションは、依存 COM オブジェクトをインストール時に per-machine の COM 構成ストアへ登録するべき としています。87
なので、開発現場ではこの区別をはっきりさせておくべきです。
- 自分のユーザーだけで使う開発用登録 なのか
- そのマシンの全ユーザーで使う本番登録 なのか
- サービスや昇格アプリが参照する登録 なのか
これを曖昧にすると、「なぜか管理者でだけ動く」「Explorer 拡張では動くのにサービスでは動かない」みたいな話になります。
4.3 Visual Studio を常時「管理者として実行」にするのは解決ではない
必要な場面があるのは事実です。 ただ、Visual Studio を常時管理者で起動しておくと、本来インストーラーや登録スクリプトで解決すべき問題を IDE 側の昇格で隠してしまう ことがあります。
しかも Visual Studio 自体も、昇格時には per-user extension の扱いが変わります。Microsoft Learn でも、Visual Studio を elevated で動かすと per-user extensions が無効化される 設定があることが説明されています。18
なので運用としては、
- 普段の開発は通常権限
- 登録が必要な step だけ、明示的に昇格した Developer Command Prompt / PowerShell / installer で行う
- 「管理者でしか再現しない」なら、その前提自体を仕様として整理する
のほうが、後で困りません。
5. ActiveX / OCX 固有でハマること
5.1 デザイン時ライセンスと実行時ライセンスが別
OCX / ActiveX で地味に厄介なのが、ライセンス です。
特に古い ActiveX コントロールでは、design-time license と run-time license が分かれていることがあります。MFC の ActiveX ドキュメントでも、ライセンスファイルやライセンスキーによって design-time / run-time を分ける仕組みが説明されています。1920
この世界では、
- 実行時には使える
- でもフォームに貼ろうとすると「ライセンスがない」
- 開発機 A では貼れる
- 開発機 B では貼れない
ということが起きます。
「このコンポーネントはライセンスが見つからない」「適切なライセンスがない」というエラーは、昔の ActiveX では珍しくありません。21
5.2 WinForms に載せた時点で、すでにラッパーが入っている
WinForms で ActiveX を使うとき、Windows Forms は ActiveX をそのままホストしているわけではありません。
Microsoft Learn の Aximp.exe ドキュメントにある通り、ActiveX Control Importer は COM タイプライブラリから WinForms 用のラッパーを生成 し、それを AxHost ベースのコントロールとして扱います。2223
つまり、問題は 1 層ではなく、
- 元の OCX / ActiveX 本体
- タイプライブラリ
- 生成された interop / wrapper
- WinForms Designer / runtime
の複数層に分かれます。
そのため、
- ベンダー OCX を入れ替えたらイベントシグネチャが変わった
- 参照の貼り直しで wrapper が再生成され、差分が大量に出た
- 開発機ごとに interop の生成結果が微妙に違う
といったことが起きます。
Choose Toolbox Items で表示されたから安心、ではありません。
Designer が載せられるか、実行時にイベントが来るか、配布先で wrapper ごと成立するか は別々に見たほうが安全です。
5.3 STA / MTA とメッセージループを甘く見ると固まる
先に、用語を 3 つだけ定義します。
| 用語 | 意味 |
|---|---|
| アパートメント (apartment) | COM オブジェクトとスレッドを、同じ同時実行ルールでまとめた論理的なグループです。1 つのオブジェクトは 1 つのアパートメントにしか属せません |
| STA (single-threaded apartment) | 1 スレッドだけが属するアパートメントです。そのオブジェクトへの呼び出しは、必ずその 1 スレッド上で実行されます |
| マーシャリング (marshaling) | アパートメントをまたいでインターフェイスポインターを渡すときに、COM が呼び出しを橋渡しする仕組みです。プロキシとスタブを経由します |
COM は、使うスレッドごとに CoInitializeEx で初期化する必要があります。Microsoft Learn でも、COM を使う各スレッドは個別に CoInitializeEx を呼ぶ 必要があると明記されています。24
また、STA(single-threaded apartment)では メッセージループが必要 です。2425
なぜメッセージループがないと固まるのか
ここは結論だけ覚えると応用が効かないので、仕組みを 1 段だけ開けておきます。
COM は STA ごとに、OleMainThreadWndClass というウィンドウクラスの 隠しウィンドウ を 1 つ作ります。別のアパートメントからそのオブジェクトを呼ぶと、呼び出しは直接の関数呼び出しにはならず、この隠しウィンドウ宛てのウィンドウメッセージ として届きます。STA のスレッドがメッセージを取り出してディスパッチすると、そのウィンドウプロシージャが対応するインターフェイスのメソッドを呼びます。25
つまり、メッセージループは「あったほうがよい作法」ではなく、呼び出しを配達する仕組みそのもの です。
sequenceDiagram
participant Caller as 別スレッドの呼び出し側
participant Queue as 隠しウィンドウのメッセージキュー
participant STA as STA スレッド
participant Obj as OCX / ActiveX
Caller->>Queue: メソッド呼び出しがメッセージとして積まれる
STA->>Queue: メッセージループが取り出す
Queue->>Obj: ウィンドウプロシージャが対応するメソッドを呼ぶ
Obj-->>Caller: 戻り値が返る
Note over STA,Obj: メッセージループが止まっていると<br/>この取り出しが起きず 呼び出し側は待ち続ける
だから、STA スレッドをブロックすると固まります。UI スレッドで Task.Wait() や Task.Result を使う、WaitOne で待つ、といった書き方はメッセージの取り出しを止めるため、COM のコールバックとアパートメント間の呼び出しが配達されなくなり、デッドロックになります。25
同じ理由で、STA のオブジェクトのインターフェイスポインターを別スレッドへそのままコピーしてはいけません。コピーすると、本来 STA スレッドへ配達されるべき呼び出しが別スレッドで直接実行され、オブジェクト側が想定していない同時実行が起きます。アパートメントをまたぐときは CoMarshalInterThreadInterfaceInStream と CoGetInterfaceAndReleaseStream でマーシャリングします。25
特に UI 系の OCX / ActiveX は STA 前提が多いので、
- UI スレッドでは動く
Task.Runや ThreadPool へ投げたら固まる- イベントが返ってこない
- たまにだけ再現する
という、嫌な不具合になります。
さらに STA では、インターフェイス ポインターをそのまま別スレッドへコピーしてはいけない、必要ならマーシャリングする、という前提もあります。2425
この手の不具合は、0x80040154 ほど親切ではなく、固まる・返ってこない・たまに落ちる としか見えないので、レジストリ系トラブルと同じくらい時間を食います。
6. 現場で効く切り分け順
実務では、最初から深追いするより、この順で切ると速いです。
6.1 まず「どのプロセスが何bitか」を固定する
最初に見るのはここです。
- ホストは何か(Visual Studio Designer / 自前アプリ / Office / Access / Explorer / ブラウザ互換環境)
- そのホストは 32bit か 64bit か
- 対象の DLL / OCX は 32bit か 64bit か
- それは in-proc か out-of-proc か
この 4 点が確定していないままレジストリを見始めると、どの view を見ればよいか決まらないため、調査が空回りします。まずここを固定してください。
6.2 次に「登録の種類」を確認する
次に見るべきは、何を何で登録するのが正しいのか です。
- ネイティブ DLL / OCX →
regsvr32 - .NET Framework COM 公開 →
Regasm.exe - .NET 5+ / 6+ / 8+ COM 公開 →
.comhost.dll - そもそも self-registration を持たない DLL →
regsvr32ではない
この分類だけで、かなりの誤爆を止められます。
6.3 そのあとで「どこに登録されたか」を見る
見る場所は、単なる HKCR では足りません。
HKCU\Software\ClassesHKLM\Software\Classes- 必要なら 32bit / 64bit の registry view
- 対象の ProgID / CLSID / TypeLib
InprocServer32/LocalServer32- ThreadingModel
- 参照先 DLL の実体パス
HKCR にはある だけでは不十分で、誰から、どの bitness で、どの view を見ているか まで揃えて初めて意味があります。711
見るべきキーのパス
HKEY_CLASSES_ROOT\CLSID\{...} を開くと、いま自分が実行しているツールの bitness とユーザーで見えるものだけが表示されます。切り分けでは、マージ前の実体を 4 か所に分けて見ます。CLSID の配下は WOW64 のリダイレクト対象で、32bit 側の物理的な置き場所は Classes の下の Wow6432Node です。26
| 見たいもの | レジストリのパス |
|---|---|
| マシン全体 / 64bit view | HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{CLSID} |
| マシン全体 / 32bit view | HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSID\{CLSID} |
| そのユーザーだけ / 64bit view | HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{CLSID} |
| そのユーザーだけ / 32bit view | HKEY_CURRENT_USER\SOFTWARE\Classes\Wow6432Node\CLSID\{CLSID} |
| ProgID から CLSID を引く | HKEY_CLASSES_ROOT\{ProgID}\CLSID の既定値 |
Windows 10 / 11 の regedit.exe は 64bit プロセスなので、上のパスを そのまま入力すれば 両方の view を直接開けます。アドレスバーにパスを貼り付ければ、その位置へ移動できます。
なお、Wow6432Node を含むパスは物理的な置き場所であり、Microsoft はアプリケーションのコードから直接触らないよう案内しています。手作業の調査で見る分には問題ありませんが、スクリプトやアプリからは後述の view 指定を使うほうが安全です。11
コマンドで確認する
reg.exe なら /reg:32 と /reg:64 で view を明示できます。
reg query "HKLM\SOFTWARE\Classes\CLSID\{00000000-0000-0000-0000-000000000000}\InprocServer32" /reg:32
reg query "HKLM\SOFTWARE\Classes\CLSID\{00000000-0000-0000-0000-000000000000}\InprocServer32" /reg:64
PowerShell で 4 か所を一度に見るなら、RegistryKey.OpenBaseKey に view を渡します。PowerShell 自体の bitness に結果が引きずられないので、切り分けではこちらが確実です。
# 調べたい ProgID を 1 か所で指定する
$progId = 'Vendor.Control.1'
# 1) ProgID から CLSID を引く (HKCR は HKLM と HKCU のマージ view)
$clsid = (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\$progId\CLSID" -Name '(default)' -ErrorAction Stop).'(default)'
"ProgID $progId -> CLSID $clsid"
# 2) ハイブ 2 種 x view 2 種の計 4 か所を総当たりで確認する
foreach ($hive in [Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryHive]::CurrentUser) {
foreach ($view in [Microsoft.Win32.RegistryView]::Registry64, [Microsoft.Win32.RegistryView]::Registry32) {
$base = [Microsoft.Win32.RegistryKey]::OpenBaseKey($hive, $view)
$key = $base.OpenSubKey("SOFTWARE\Classes\CLSID\$clsid\InprocServer32")
if ($null -eq $key) {
'{0,-12} {1,-11} : 登録なし' -f $hive, $view
}
else {
'{0,-12} {1,-11} : {2} (ThreadingModel={3})' -f $hive, $view, $key.GetValue(''), $key.GetValue('ThreadingModel')
$key.Dispose()
}
$base.Dispose()
}
}
この 4 行の結果が、そのまま切り分けの答えになります。
- 4 か所すべて「登録なし」 → 本当に未登録。登録手段が 6.2 の分類と合っているかを見直します。
Registry32にだけ出る → 32bit 側にしか登録されていません。64bit プロセスからは0x80040154になります。CurrentUserにだけ出る → そのユーザーにしか見えません。別ユーザー、サービスアカウント、昇格プロセスからは見えません。- パスは出るのに動かない →
InprocServer32の既定値が指すファイルが実在するか、そのファイルの bitness がホストと一致しているかを確認します。
6.4 最後に「権限で隠れていないか」を見る
最後に、問題が本当に権限なのか、それとも 権限によって別の登録が見えているだけか を確認します。
- 標準ユーザー / 管理者で挙動は変わるか
- Visual Studio を昇格すると何が変わるか
- サービスアカウントや別ユーザーでも再現するか
- インストーラーを通したクリーン環境でも成立するか
開発機 1 台でしか見ていないと、ここは本当に見誤ります。
7. 先に決めておくと事故が減る運用
COM / ActiveX / OCX の開発や保守では、実装テクニックより 運用の決め方 のほうが効くことがあります。
7.1 先に bitness 方針を決める
最初に、これを決めます。
- x86 固定で行くのか
- x64 を正とするのか
- 両対応するのか
- その部品は in-proc でなければならないのか
特にベンダー OCX が x86 固定なら、そこを無視してアプリだけ x64 化しても、あとで詰まります。 このテーマは、より大きい構成の話として 32bitアプリから64bit DLLを呼ぶCOMブリッジ実例 も関連します。
7.2 登録戦略を決める
登録も、場当たりでやらないほうがよいです。
- マシン全体で使う → installer で per-machine 登録
- そのユーザーだけで使う → per-user を意図的に使う
- 自分のアプリだけで閉じる → registration-free COM を検討
- 開発用にだけ必要 → 明示的な dev setup script に閉じ込める
registration-free COM は、レジストリではなく manifest でアクティブ化情報を持てるので、登録地獄を減らす 手段として有効です。Win32 側の registration-free COM も、.NET 側の RegFree COM も公式に説明があります。27628
仕組みとしては、COM が CoCreateInstance などを処理するとき、まずアクティブ化コンテキストを探し、そこに情報がなければレジストリを見る という順序になっています。マニフェストは、そのアクティブ化コンテキストの中身を宣言するファイルです。27
必要なマニフェストは 2 つで、どちらも exe と同じフォルダーに置きます。
1 つ目は、コンポーネント側の アセンブリマニフェスト (VendorCtl.manifest) です。どのファイルがどの CLSID を提供するかを書きます。29
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32"
name="MyCompany.VendorCtl"
version="1.0.0.0"
processorArchitecture="x86" />
<file name="vendor.ocx">
<comClass description="Vendor Control"
clsid="{00000000-0000-0000-0000-000000000000}"
threadingModel="Apartment"
progid="Vendor.Control.1" />
</file>
</assembly>
2 つ目は、アプリ側の アプリケーションマニフェスト (MyApp.exe.manifest) です。上のアセンブリに依存することだけを書きます。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32"
name="MyCompany.MyApp"
version="1.0.0.0"
processorArchitecture="x86" />
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="MyCompany.VendorCtl"
version="1.0.0.0"
processorArchitecture="x86" />
</dependentAssembly>
</dependency>
</assembly>
書くときに間違えやすいのは次の点です。29
- 依存側の
assemblyIdentityは、コンポーネント側のassemblyIdentityと完全に一致していなければなりません。nameversionprocessorArchitectureのどれか 1 つでもずれると解決されません。 - アセンブリ名と DLL 名は別にします。 マニフェストを別ファイルで置く場合、アセンブリ名とマニフェスト名は DLL 名と違う名前にする必要があります。
- 要素名と属性名は大文字小文字を区別します。
comClassをComClassと書くと読まれません。 processorArchitectureは bitness そのものです。 x86 の OCX に対してamd64のアプリからは解決できません。7.1 の bitness 方針と必ず揃えてください。- OCX を貼り込むなら
miscStatus系の属性が要ることがあります。 レジストリのMiscStatusキーに相当する情報を、マニフェスト側でmiscStatus/miscStatusContentなどとして書き写す必要があります。
7.3 生成物を source control の外に置きすぎない
OCX / ActiveX 系でありがちなのが、依存物の散逸です。
- OCX 本体
- 依存 DLL
- TLB
.lic- interop DLL
- AxHost wrapper
- 登録スクリプト
- サンプルホスト
このへんが人のローカルだけにあると、数か月後に確実に事故ります。
最低でも、
- どのバージョンを前提にしているか
- 何をどの順序で入れるか
- どのコマンドで登録するか
- x86 / x64 のどちら向けか
は、コードと同じ場所に残したほうが安全です。
8. こういう相談は相性がよい
このテーマは、いきなり全面改修に入る前の 切り分け と 方針整理 だけでも価値が出やすいです。
たとえば、以下のような相談とは特に相性がよいです。
0x80040154や0x80070005の原因を、bitness / 登録 / 権限に分けて整理したい- Visual Studio 2022 に上げたら Designer が壊れたので、どこまで救えるか見たい
- ベンダー OCX を残したまま、周辺を .NET や C# に寄せたい
- x86 固定資産をどこまで延命し、どこから bridge / wrap / replace するか決めたい
- 手作業の
regsvr32依存をやめて、install / deploy を再設計したい
置き換える・包む・残す、の判断については ActiveX / OCX を今どう扱うか - 残す・包む・置き換える判断表 も参考になるはずです。
9. まとめ
COM コンポーネントや OCX / ActiveX 開発でハマるとき、原因はだいたいこの 4 つに行き着きます。
- bitness が噛み合っていない
- 登録方法が間違っている
- 登録スコープ(HKCU / HKLM、32bit / 64bit view)がズレている
- 権限でたまたま見えているだけの状態を、正常だと思っている
Visual Studio 2022 の 64bit 化で、昔の「なんとなく動いていた」設計は、かなり表面化しやすくなりました。12 だからこそ、COM / OCX / ActiveX を触るときは、まず コードを書く前に環境の前提を揃える のが近道です。
regsvr32 を何回叩くかより、
- どのプロセスがホストしているか
- そのプロセスは何bitか
- どこに登録するべきか
- その登録は本当に管理者前提なのか
- Designer と runtime を分けて見ているか
を整理したほうが、はるかに速く解けます。
参考
-
Microsoft Learn, Visual Studio 2022 version 17.0 Release Notes —
devenv.exe is now 64-bit only. ↩ ↩2 ↩3 -
Microsoft Learn, Troubleshoot 32-bit problems - Windows Forms — Visual Studio 2022 は 64bit プロセスであり、32bit の .NET / COM / ActiveX を直接ロードできないこと、out-of-process designer の制約。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Classes and Servers — COM の登録、HKCU / HKCR、self-registration と
DllRegisterServer。 ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, COM へのアセンブリの登録 — .NET Framework の COM 登録は
Regasm.exe。 ↩ ↩2 -
Microsoft Learn, COM への .NET Core コンポーネントの公開 —
EnableComHosting、生成される.comhost.dll、regsvr32、EnableRegFreeCom。 ↩ ↩2 ↩3 -
Microsoft Learn, Merged View of HKEY_CLASSES_ROOT — HKCR は HKLM と HKCU の merged view。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, HKEY_CLASSES_ROOT Key — 管理者権限を必要とするアプリは per-machine COM 構成への登録を推奨。 ↩ ↩2
-
Microsoft Learn, COM Error Codes (Generic) (Winerror.h) —
REGDB_E_CLASSNOTREG (0x80040154)など。 ↩ -
Microsoft Learn, You receive 0x80070005 error when you try to register a DLL by using Regsvr32.exe — 権限不足で DLL 登録に失敗する典型例。 ↩ ↩2
-
Microsoft Learn, Registry Redirector — WOW64 における 32bit / 64bit レジストリ view、
HKLM\Softwareの物理的な置き場所がWow6432Nodeであること、アプリから物理パスを直接触らないこと。 ↩ ↩2 ↩3 ↩4 ↩5 -
Microsoft Learn, File System Redirector — x64 Windows の
%windir%\System32と WOW64 のファイルシステムリダイレクト。 ↩ -
Microsoft Learn, 64 ビット バージョンの Windows での 32 ビット プログラムの互換性に関する考慮事項の概要 — WOW64 によるファイル / レジストリ リダイレクト。 ↩
-
Microsoft Learn, Regasm.exe (Assembly Registration Tool) —
Regasm.exeの役割と/tlbなどのオプション。 ↩ ↩2 ↩3 -
Microsoft Learn, Packaging a .NET Framework Assembly for COM — タイプライブラリと
Regasm.exe /tlb。 ↩ -
Microsoft Learn, Understanding Custom Build Steps and Build Events — post-build event で
regsvr32.exeを使う例。 ↩ -
Microsoft Learn, 上級ユーザー向けの Windows レジストリ —
HKCU\Software\ClassesとHKLM\Software\Classes、HKCR の挙動。 ↩ -
Microsoft Learn, Find, install, and manage extensions for Visual Studio — elevated 実行時の per-user extension の扱い。 ↩
-
Microsoft Learn, MFC ActiveX Controls: Licensing an ActiveX Control — design-time / run-time license、
.LIC。 ↩ -
Microsoft Learn, Application Settings, MFC ActiveX Control Wizard — ランタイムライセンス生成と
.licファイル。 ↩ -
Microsoft Learn, License information for this component not found. You don’t have an appropriate license to use this functionality in the design environment. ↩
-
Microsoft Learn, Aximp.exe (Windows Forms ActiveX Control Importer) — ActiveX を WinForms 用ラッパーへ変換。 ↩
-
Microsoft Learn, AxHost Class — ActiveX Control Importer が生成する AxHost ベースのラッパー。 ↩
-
Microsoft Learn, COM ライブラリの初期化 —
CoInitializeEx、各スレッドごとの初期化、STA のメッセージループ。 ↩ ↩2 ↩3 -
Microsoft Learn, Single-Threaded アパートメント — STA のメッセージループ、マーシャリング、
ThreadingModel。 ↩ ↩2 ↩3 ↩4 ↩5 -
Microsoft Learn, Registration-Free COM オブジェクトの作成 — activation context による登録不要 COM。 ↩ ↩2
-
Microsoft Learn, 登録を必要としない COM 相互運用機能 — .NET Framework の registration-free COM interop。 ↩
-
Microsoft Learn, Assembly Manifests —
assembly/assemblyIdentity/dependency/file/comClassの各属性、REF 側と DEF 側のassemblyIdentityが一致する必要があること、名前の大文字小文字の扱い、miscStatus系属性。 ↩ ↩2
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Office 2024/Microsoft 365でActiveXが動かない原因と確認手順
Office 2024/Microsoft 365でActiveXが動かない時に、既定無効化、32bit/64bit、COM登録、依存DLL、IEモード、Click-to-Runログを切り分ける順番を整理します。
COM / ActiveX / OCX とは何か - 違いと関係をまとめて解説
COMとは何か、ActiveXとは何か、OCXとは何かを、違いと関係、OLEとのつながり、どこで使われるのか、今どう捉えるべきかまで実務目線で整理します。
ActiveX / OCX を今どう扱うか - 残す・包む・置き換える判断表
ActiveX / OCX を見つけたときに、残す・包む・置き換えるのどれを選ぶべきかを、32bit / 64bit、登録、ブラウザ依存、ベンダー保守まで含めて整理します。
32bitアプリから64bit DLLを呼ぶCOMブリッジ実例
32bit アプリから 64bit DLL を直接呼び出せないときに、COM ブリッジで連携する考え方を、Windows の制約、構成、処理の流れとあわせて整理します。
Windowsアプリ 外注・受託開発を依頼する前に整理したいこと
Windowsアプリの外注・受託開発を依頼する前に、既存ソフト改修、装置連携、COM/ActiveX、配布・更新、保守の整理ポイントを解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
ActiveX / 移行テーマ
COM / ActiveX / OCX を残すか、包むか、置き換えるかを整理するトピックです。
32bit / 64bit テーマ
32bit / 64bit、ネイティブ連携、C++/CLI まわりを整理するトピックです。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
既存資産活用・移行支援
COM / ActiveX / OCX、32bit / 64bit 制約を抱える既存資産の活用と移行を支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- 0x80040154(Class not registered)はどう調べればよいですか?
- 公式にはREGDB_E_CLASSNOTREGで、文字通り「クラスが登録されていない」エラーですが、完全に未登録とは限りません。別ビット数側のレジストリviewにだけ登録されている、あるいはそのユーザーにしか見えないHKCU側にだけ登録されている場合でも起きます。まずホストプロセスが32bitか64bitかを固定し、次に登録手段が正しいか、そしてHKCU / HKLMと32bit / 64bit viewのどこに登録されたかを順に確認するのが早道です。
- regsvr32で登録しても動かないのはなぜですか?
- regsvr32はDllRegisterServerをエクスポートするネイティブのin-proc COMサーバー(DLL / OCX)向けで、何でも登録できる魔法のコマンドではありません。.NET FrameworkアセンブリのCOM公開はRegasm.exe、.NET 5以降ではEnableComHostingで生成される.comhost.dllをregsvr32で登録する流れです。また、x64 Windowsでは64bit用はSystem32、32bit用はSysWOW64のregsvr32を使い分ける必要があり、間違った側に登録すると成功したのに目的のプロセスから見えない状態になります。
- Visual Studio 2022でDesignerだけ壊れるのはなぜですか?
- Visual Studio 2022のdevenv.exeは64bitプロセスのため、32bitのCOM / ActiveXコンポーネントを直接ロードできないからです。実行時のアプリはx86で動けても、DesignerはVisual Studio側の64bitプロセスで動くため、実行時は生きているのにDesignerだけ落ちるというねじれが起きます。AnyCPUにしても、参照先で32bit固定のCOM / ActiveXに依存していれば問題は残ります。
- 管理者として実行すると動くのに、通常権限では動かないのはなぜですか?
- 本質は権限そのものより、登録スコープやインストール設計のズレであることが多いです。COMはHKCU\Software\Classesを先に見て、HKEY_CLASSES_ROOTはHKLMとHKCUのmerged viewです。開発者Aのユーザーで手動登録した場合、Aのアカウントでは動くのに他のユーザーやサービスアカウントでは動かない、ということが普通に起きます。開発用のper-user登録と、インストーラーで行う本番のper-machine登録を区別し、ビルドと登録を分離するのが基本です。