Pitfalls in COM, OCX, and ActiveX Development - Visual Studio Bitness, Registration, and Admin-Rights Traps

· · COM, ActiveX, OCX, Windows Development, Legacy Technology

Introduction

In COM, OCX, and ActiveX development, you usually get stuck less on the code itself and more on the boundaries around runtime environment, registration, hosting, and permissions. Typical symptoms look like this:

  • the build succeeds, but startup fails with 0x80040154
  • it works on the developer machine but not on another PC
  • runtime works fine, but only the Visual Studio Designer breaks
  • it works as administrator but breaks under normal permissions

1. Short version

COM/OCX/ActiveX trouble is fastest to untangle along these four axes:

  1. is the bitness (32-bit/64-bit) consistent?
  2. is the registration method correct? (regsvr32 or Regasm?)
  3. is the registration target correct? (HKCU/HKLM, 32-bit/64-bit view)
  4. is it really a permission problem, or are permissions just exposing a different registration?

2. Symptom-to-cause cheat sheet

Symptom First suspicion Real cause
0x80040154 not registered only registered on the other bitness, or only for a specific user
0x80070005 insufficient permissions trying to register as a standard user
only the designer breaks in VS2022 designer limitation 64-bit VS cannot directly load a 32-bit COM component
only works as administrator permissions issue a mismatch in registration scope or installer design

3. The Visual Studio 2022 64-bit problem

In Visual Studio 2022, devenv.exe is 64-bit only. As a result, 32-bit COM/ActiveX components cannot be loaded directly at design time.

The consequence:

  • the app runs fine when launched as x86 at runtime
  • but the form designer runs inside 64-bit VS, so it cannot load 32-bit components and crashes

What to do

  • switching to AnyCPU does not help if a dependency is locked to 32-bit COM
  • check whether anything is being used purely from designer-only code
  • where possible, get a 64-bit version of the component

4. The System32 vs SysWOW64 trap

On Windows x64, the folder names are the opposite of what intuition suggests.

Folder Actual role
C:\Windows\System32 for 64-bit apps
C:\Windows\SysWOW64 for 32-bit apps
# register a 64-bit DLL/OCX
C:\Windows\System32\regsvr32.exe vendor.ocx

# register a 32-bit DLL/OCX on x64 Windows
C:\Windows\SysWOW64\regsvr32.exe vendor.ocx

If you register on the wrong side, regsvr32 reports success but the target process cannot see the component, and you get 0x80040154.

5. Picking the right registration tool

What you want to expose Tool to use
native C++ DLL/OCX regsvr32
.NET Framework assembly exposed via COM Regasm.exe
.NET 5+/6+/8+ COM exposure regsvr32 against the generated .comhost.dll

Common mistakes

  • running regsvr32 against a managed DLL — it cannot find DllRegisterServer
  • using regsvr32 on a .NET Framework assembly — nothing actually gets registered

6. The admin-rights problem

per-user vs per-machine registration

COM looks up classes in this order:

  1. HKEY_CURRENT_USER\Software\Classes (per user)
  2. HKEY_LOCAL_MACHINE\Software\Classes (machine-wide)

Which means:

  • developer A registers manually — works for A but not for B
  • it works as administrator only because a per-user HKCU registration happens to be visible (possibly)

What to do

  • separate build from registration — avoid auto-registering in a post-build event
  • if the component is for the whole machine, register per-machine via the installer
  • if it is dev-only, keep it inside an explicit setup script
  • consider Registration-Free COM (no registry needed)

7. ActiveX/OCX-specific gotchas

design-time vs runtime licenses

Older ActiveX controls sometimes ship with separate licenses for design time (dropping the control onto a form) and runtime. Errors like “license for this component not found” usually come down to that distinction.

STA and the message loop

UI-side OCX/ActiveX controls assume STA (single-threaded apartment).

  • they work on the UI thread but freeze when you fire them off with Task.Run
  • never hand an interface pointer directly to another thread

WinForms wrappers

WinForms does not host ActiveX directly — Aximp.exe generates a wrapper. The catch is that you end up with multiple layers (OCX itself → type library → interop → AxHost wrapper), and one problem in any layer cascades through the rest.

8. Triage order that actually works on the ground

  1. check the host process bitness — what is 32-bit and what is 64-bit?
  2. check the registration method — what should be registered with what?
  3. check the registration target — HKCU/HKLM, 32-bit/64-bit view
  4. check whether permissions are hiding something — does behavior change between standard user and admin?

9. Wrap-up

COM trouble almost always comes down to these four:

  1. bitness mismatch
  2. wrong registration method
  3. wrong registration scope
  4. mistaking “works because permissions expose a stale registration” for normal

Lining up the environmental assumptions before writing any code is the shortest path to a working component.

Related Articles

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

Related Topics

These topic pages place the article in a broader service and decision context.

Where This Topic Connects

This article connects naturally to the following service pages.

Back to the Blog