COM/OCX/ActiveX開発でハマる登録とbitnessの罠

· 更新日: · · COM, ActiveX, OCX, Visual Studio, Windows開発, 32bit, 64bit, Interop

更新履歴(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. まず結論

先に、実務で効く言い方をするとこうです。

  1. COM / OCX / ActiveX のトラブルは、コードのロジックより bitness(32bit / 64bit)・登録先・ホスト・権限 の不一致で起きることが多いです。
  2. Visual Studio 2022 は 64bit プロセス なので、昔は通っていた 32bit 前提のデザイン時連携が、そのままでは壊れます。12
  3. regsvr32何でも登録する魔法のコマンドではありません。ネイティブの in-proc COM サーバー(DLL / OCX)向けです。 .NET Framework を COM 公開するなら Regasm.exe、.NET 5+ / .NET 6+ / .NET 8+ では生成された .comhost.dll を登録する流れになります。3456
  4. 「管理者で動くなら 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が呼び出しを配達する仕組みそのものであり、これを止めるとデッドロックします。

COM/ActiveX登録トラブルの知識マップCOM・OCX・ActiveXの登録トラブルが、Visual Studio 2022の64bit化・WOW64レジストリリダイレクター・regsvr32やRegasmといった登録手段の使い分け・HKCUとHKLMの登録スコープ・STAのメッセージループとどう関係するかを示す図利用する利用する両立しない前提とする実装を担う利用するに保存される利用する推奨される対応用いるのは非推奨推奨される対応利用する前提とする原因になり得る原因になり得る内容を継承する内容を継承する前提とする前提とする前提とする利用する実装を担う前提とする前提とする防止する前提とする利用する軽減する前提とする前提とする利用するに保存される前提とする利用するCOM/OCX/ActiveXの登録トラブルVisual Studio 2022WOW64レジストリリダイレクターActiveXOCXbitness一致要件COM(コンポーネントオブジェクトモデル)HKEY_CURRENT_USER\Software\ClassesCLSID(Class ID)WOW6432Noderegsvr32In-proc COM(DLLサーバー)Regasm.exe.NET Framework.NET(5以降)のEnableComHosting/.comhost.dll.NET(Core以降)WOW64ファイルシステムリダイレクター管理者権限0x80040154(Class not registered)per-user登録HKEY_CLASSES_ROOT(HKCR)HKEY_LOCAL_MACHINE\Software\Classesper-machine登録ActiveXのデザイン時/実行時ライセンスAxImp(ActiveX Control Importer)型ライブラリ(TLB)AxHostCOMアパートメントモデル(STA/MTA)メッセージループCoInitializeExSTAスレッドのブロックによるデッドロックマーシャリングReg-Free COM(登録不要COM)アセンブリマニフェストCOM LocalServer(別プロセスCOMサーバー)ProgID(Programmatic Identifier)

図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全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 また、regsvr320x80070005 は、公式にも 管理者権限がなくレジストリや System32 に書けない ケースとして説明されています。10

ここで大事なのは、エラー名をそのまま信じすぎない ことです。 たとえば Class not registered は「完全に未登録」とは限らず、違うレジストリ view に登録されているそのユーザーにしか見えない場所に登録されている だけでも起きます。117

3. Visual Studio のビット数でハマる

3.1 Visual Studio 2022 は 64bit になった

ここは、今の COM / ActiveX 開発で一番ハマりやすい点です。

Visual Studio 2022 は devenv.exe64bit 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 System32SysWOW64 の罠

Windows x64 では、名前の印象と実態がずれているので混乱しがちです。

Microsoft Learn では、x64 Windows 上の %windir%\System3264bit アプリケーション用 と説明されています。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

という状態になります。1113

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.dllregsvr32

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 が書き出すのはマネージドクラスのエントリだけで、TypeLibIDInterfaceID は出力されません。「.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_ROOTHKLM\Software\ClassesHKCU\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 licenserun-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 層ではなく、

  1. 元の OCX / ActiveX 本体
  2. タイプライブラリ
  3. 生成された interop / wrapper
  4. 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

つまり、メッセージループは「あったほうがよい作法」ではなく、呼び出しを配達する仕組みそのもの です。

OCX / ActiveXSTA スレッド隠しウィンドウのメッセージキュー別スレッドの呼び出し側OCX / ActiveXSTA スレッド隠しウィンドウのメッセージキュー別スレッドの呼び出し側メッセージループが止まっているとこの取り出しが起きず 呼び出し側は待ち続けるメソッド呼び出しがメッセージとして積まれるメッセージループが取り出すウィンドウプロシージャが対応するメソッドを呼ぶ戻り値が返る

だから、STA スレッドをブロックすると固まります。UI スレッドで Task.Wait()Task.Result を使う、WaitOne で待つ、といった書き方はメッセージの取り出しを止めるため、COM のコールバックとアパートメント間の呼び出しが配達されなくなり、デッドロックになります。25

同じ理由で、STA のオブジェクトのインターフェイスポインターを別スレッドへそのままコピーしてはいけません。コピーすると、本来 STA スレッドへ配達されるべき呼び出しが別スレッドで直接実行され、オブジェクト側が想定していない同時実行が起きます。アパートメントをまたぐときは CoMarshalInterThreadInterfaceInStreamCoGetInterfaceAndReleaseStream でマーシャリングします。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\Classes
  • HKLM\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 と完全に一致していなければなりません。 name version processorArchitecture のどれか 1 つでもずれると解決されません。
  • アセンブリ名と DLL 名は別にします。 マニフェストを別ファイルで置く場合、アセンブリ名とマニフェスト名は DLL 名と違う名前にする必要があります。
  • 要素名と属性名は大文字小文字を区別します。 comClassComClass と書くと読まれません。
  • 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. こういう相談は相性がよい

このテーマは、いきなり全面改修に入る前の 切り分け方針整理 だけでも価値が出やすいです。

たとえば、以下のような相談とは特に相性がよいです。

  • 0x800401540x80070005 の原因を、bitness / 登録 / 権限に分けて整理したい
  • Visual Studio 2022 に上げたら Designer が壊れたので、どこまで救えるか見たい
  • ベンダー OCX を残したまま、周辺を .NET や C# に寄せたい
  • x86 固定資産をどこまで延命し、どこから bridge / wrap / replace するか決めたい
  • 手作業の regsvr32 依存をやめて、install / deploy を再設計したい

置き換える・包む・残す、の判断については ActiveX / OCX を今どう扱うか - 残す・包む・置き換える判断表 も参考になるはずです。

9. まとめ

COM コンポーネントや OCX / ActiveX 開発でハマるとき、原因はだいたいこの 4 つに行き着きます。

  1. bitness が噛み合っていない
  2. 登録方法が間違っている
  3. 登録スコープ(HKCU / HKLM、32bit / 64bit view)がズレている
  4. 権限でたまたま見えているだけの状態を、正常だと思っている

Visual Studio 2022 の 64bit 化で、昔の「なんとなく動いていた」設計は、かなり表面化しやすくなりました。12 だからこそ、COM / OCX / ActiveX を触るときは、まず コードを書く前に環境の前提を揃える のが近道です。

regsvr32 を何回叩くかより、

  • どのプロセスがホストしているか
  • そのプロセスは何bitか
  • どこに登録するべきか
  • その登録は本当に管理者前提なのか
  • Designer と runtime を分けて見ているか

を整理したほうが、はるかに速く解けます。


参考

  1. Microsoft Learn, Visual Studio 2022 version 17.0 Release Notesdevenv.exe is now 64-bit only 2 3

  2. Microsoft Learn, Troubleshoot 32-bit problems - Windows Forms — Visual Studio 2022 は 64bit プロセスであり、32bit の .NET / COM / ActiveX を直接ロードできないこと、out-of-process designer の制約。  2 3 4 5

  3. Microsoft Learn, Classes and Servers — COM の登録、HKCU / HKCR、self-registration と DllRegisterServer。  2 3 4

  4. Microsoft Learn, regsvr32regsvr32 の構文と役割。  2

  5. Microsoft Learn, COM へのアセンブリの登録 — .NET Framework の COM 登録は Regasm.exe。  2

  6. Microsoft Learn, COM への .NET Core コンポーネントの公開EnableComHosting、生成される .comhost.dllregsvr32EnableRegFreeCom。  2 3

  7. Microsoft Learn, Merged View of HKEY_CLASSES_ROOT — HKCR は HKLM と HKCU の merged view。  2 3 4 5

  8. Microsoft Learn, HKEY_CLASSES_ROOT Key — 管理者権限を必要とするアプリは per-machine COM 構成への登録を推奨。  2

  9. Microsoft Learn, COM Error Codes (Generic) (Winerror.h)REGDB_E_CLASSNOTREG (0x80040154) など。 

  10. Microsoft Learn, You receive 0x80070005 error when you try to register a DLL by using Regsvr32.exe — 権限不足で DLL 登録に失敗する典型例。  2

  11. Microsoft Learn, Registry Redirector — WOW64 における 32bit / 64bit レジストリ view、HKLM\Software の物理的な置き場所が Wow6432Node であること、アプリから物理パスを直接触らないこと。  2 3 4 5

  12. Microsoft Learn, File System Redirector — x64 Windows の %windir%\System32 と WOW64 のファイルシステムリダイレクト。 

  13. Microsoft Learn, 64 ビット バージョンの Windows での 32 ビット プログラムの互換性に関する考慮事項の概要 — WOW64 によるファイル / レジストリ リダイレクト。 

  14. Microsoft Learn, Regasm.exe (Assembly Registration Tool)Regasm.exe の役割と /tlb などのオプション。  2 3

  15. Microsoft Learn, Packaging a .NET Framework Assembly for COM — タイプライブラリと Regasm.exe /tlb。 

  16. Microsoft Learn, Understanding Custom Build Steps and Build Events — post-build event で regsvr32.exe を使う例。 

  17. Microsoft Learn, 上級ユーザー向けの Windows レジストリHKCU\Software\ClassesHKLM\Software\Classes、HKCR の挙動。 

  18. Microsoft Learn, Find, install, and manage extensions for Visual Studio — elevated 実行時の per-user extension の扱い。 

  19. Microsoft Learn, MFC ActiveX Controls: Licensing an ActiveX Control — design-time / run-time license、.LIC。 

  20. Microsoft Learn, Application Settings, MFC ActiveX Control Wizard — ランタイムライセンス生成と .lic ファイル。 

  21. Microsoft Learn, License information for this component not found. You don’t have an appropriate license to use this functionality in the design environment. 

  22. Microsoft Learn, Aximp.exe (Windows Forms ActiveX Control Importer) — ActiveX を WinForms 用ラッパーへ変換。 

  23. Microsoft Learn, AxHost Class — ActiveX Control Importer が生成する AxHost ベースのラッパー。 

  24. Microsoft Learn, COM ライブラリの初期化CoInitializeEx、各スレッドごとの初期化、STA のメッセージループ。  2 3

  25. Microsoft Learn, Single-Threaded アパートメント — STA のメッセージループ、マーシャリング、ThreadingModel。  2 3 4 5

  26. Microsoft Learn, Registry Keys Affected by WOW64HKLM\SOFTWARE\Classes\CLSIDHKCU\SOFTWARE\Classes\CLSID はリダイレクト対象であること、HKCR が両者のマージ view であること。 

  27. Microsoft Learn, Registration-Free COM オブジェクトの作成 — activation context による登録不要 COM。  2

  28. Microsoft Learn, 登録を必要としない COM 相互運用機能 — .NET Framework の registration-free COM interop。 

  29. Microsoft Learn, Assembly Manifestsassembly / assemblyIdentity / dependency / file / comClass の各属性、REF 側と DEF 側の assemblyIdentity が一致する必要があること、名前の大文字小文字の扱い、miscStatus 系属性。  2

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

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登録を区別し、ビルドと登録を分離するのが基本です。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る