Common Pitfalls in COM Components and OCX / ActiveX Development - Visual Studio 32-bit / 64-bit, Registration, and Admin-Rights Traps

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

In COM component and OCX / ActiveX work, teams often 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, but only the Visual Studio Designer dies
  • it works when launched as administrator, but breaks under normal permissions
  • you keep running regsvr32, and somehow it still does not get fixed

In many cases, those are not isolated bugs. They are signs that some COM precondition is not lining up correctly.

If you want to organize the terminology first, it helps to start with What COM / ActiveX / OCX Are - A Practical Guide to the Differences and Relationships.
This article picks up from that point and focuses on where people actually get stuck in practice, including Visual Studio bitness and admin-rights issues.

Contents

  1. 1. The short answer
  2. 2. Working backward from symptoms, it usually looks like this
  3. 3. Visual Studio bitness is a major trap
    • 3.1. Visual Studio 2022 became 64-bit
    • 3.2. Switching to AnyCPU does not automatically fix it
    • 3.3. The System32 and SysWOW64 trap
    • 3.4. regsvr32 cannot register everything
  4. 4. Admin-rights issues are another big trap
    • 4.1. Registration is wired into post-build and succeeds only under admin
    • 4.2. Mixing per-user and per-machine registration breaks things
    • 4.3. Running Visual Studio permanently as administrator is not a real fix
  5. 5. ActiveX / OCX have their own extra traps
    • 5.1. Design-time and runtime licenses can be different
    • 5.2. By the time it is inside WinForms, there is already a wrapper layer
    • 5.3. If you underestimate STA / MTA and the message loop, things freeze
  6. 6. A practical troubleshooting order that works in the field
    • 6.1. First, fix which process is what bitness
    • 6.2. Next, check what kind of registration is actually correct
    • 6.3. After that, check where it was actually registered
    • 6.4. Finally, check whether permissions are only hiding the real shape
  7. 7. Operating decisions that reduce accidents
    • 7.1. Decide the bitness policy first
    • 7.2. Decide the registration strategy
    • 7.3. Do not leave too many generated assets outside source control
  8. 8. This kind of consultation is a good fit
  9. 9. Summary
  10. References

1. The short answer

If we say it in a way that is useful in real work, the core points are these.

  1. Most COM / OCX / ActiveX failures are caused less by business logic and more by mismatches in bitness (32-bit / 64-bit), registration location, host process, and permissions.
  2. Visual Studio 2022 is a 64-bit process, so design-time integration paths that used to work under old 32-bit assumptions can now fail as-is.12
  3. regsvr32 is not a magic command that registers everything. It is for native in-proc COM servers such as DLLs and OCXs. If you expose .NET Framework assemblies to COM, the tool is Regasm.exe; in .NET 5+ / .NET 6+ / .NET 8+, the flow becomes registering the generated .comhost.dll.3456
  4. “It works when I run as administrator, so it must be fine” is dangerous. Very often that only means the component is visible by accident through per-user registration, or that something that should have been installed properly was only registered manually on the developer machine.378

So when you deal with COM / OCX / ActiveX, it is safest to start from these four axes:

  • which process is hosting it
  • whether that process is 32-bit or 64-bit
  • where it is registered (HKCU / HKLM, 32-bit view / 64-bit view)
  • whether the action or execution path really requires administrator rights

2. Working backward from symptoms, it usually looks like this

Symptom First thing to suspect What the real cause often is
0x80040154 Class not registered not registered in practice it is often “registered only in the other bitness view” or “registered only for that user”
DllRegisterServer failed: 0x80070005 insufficient permissions trying to register as a standard user, or running post-build registration without elevation
only the VS2022 Designer breaks Designer limitation 32-bit COM / ActiveX cannot be loaded directly by 64-bit Visual Studio
it works only when started as administrator a permission problem very often the real issue is a mismatch in registration scope or installation design, not permissions by themselves
a 64-bit app cannot call a 32-bit OCX COM architectural limitation in-proc servers must match the host bitness
it works on the UI thread but freezes on a background thread threading model STA / MTA, CoInitializeEx, or message-loop assumptions are being violated

Officially, 0x80040154 is REGDB_E_CLASSNOTREG, literally Class not registered.9
And 0x80070005 from regsvr32 is also documented as a case where administrator rights are missing and the process cannot write to the registry or System32.10

The important point is not to trust the error name too literally.
For example, Class not registered does not always mean “completely unregistered.” It can also happen when the class is registered only in another registry view or only in a per-user location that the current process does not see.117

3. Visual Studio bitness is a major trap

3.1. Visual Studio 2022 became 64-bit

This is one of the biggest traps in current COM / ActiveX development.

Visual Studio 2022 is 64-bit only for devenv.exe.1
That means the Visual Studio side of the WinForms design-time experience cannot directly load 32-bit components. Microsoft explicitly states that Visual Studio 2022 is a 64-bit process and therefore cannot load 32-bit .NET / COM / ActiveX components directly.2

In other words, a setup that used to work under assumptions like this:

  • the project is x86
  • the referenced ActiveX control is also x86
  • Visual Studio itself is 32-bit

turns into something different in VS2022:

  • the runtime app can still run as x86
  • but the Designer now runs inside 64-bit Visual Studio

That creates a mismatch.
The result is the particularly unpleasant state where runtime still works, but only the Designer dies.2

3.2. Switching to AnyCPU does not automatically fix it

This is another common misunderstanding.

AnyCPU is not magic that makes every dependency neutral too.
Microsoft also explains that even if a component itself looks like AnyCPU, design-time still breaks in Visual Studio 2022 if something below it ultimately depends on 32-bit COM / ActiveX.2

So when changing to AnyCPU does not make the error disappear, it is often faster to suspect:

  • whether that assembly depends on 32-bit native code further down
  • whether the ActiveX / OCX is fixed to x86
  • whether there is code that is loaded only at Designer time

3.3. The System32 and SysWOW64 trap

On x64 Windows, names and reality drift apart enough to confuse people.

Microsoft Learn explains that %windir%\\System32 on x64 Windows is for 64-bit applications. The 32-bit side is redirected elsewhere by the WOW64 file-system redirector.12
The registry works similarly: the WOW64 registry redirector presents different logical views for 32-bit and 64-bit code.11

That is why it is safer, when troubleshooting locally, to make which bitness of regsvr32 you are using completely explicit.

# When you want to register a 64-bit DLL / OCX
C:\Windows\System32\regsvr32.exe vendor.ocx

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

The nasty part of this trap is that you can register into the wrong side and still feel like you successfully registered it.
The end result becomes:

  • regsvr32 succeeded
  • but the app still gets 0x80040154
  • the registry looks as if the entry exists
  • but you are looking at the wrong view

That pattern is extremely common.1113

3.4. regsvr32 cannot register everything

This is also widely misunderstood.

Native in-proc COM servers usually export DllRegisterServer / DllUnregisterServer and support self-registration.3
regsvr32 is the tool for those DLL / OCX cases.4

By contrast, if you want to expose a .NET Framework assembly to COM, the normal tool is Regasm.exe. Microsoft Learn explicitly explains that Regasm.exe is used to register assemblies for COM use.514

Then the story changes again in .NET 5+ / .NET 6+ / .NET 8+: with <EnableComHosting>true</EnableComHosting>, the build generates a *.comhost.dll, and that generated DLL is what you register with regsvr32.6

In rough terms, the split is:

What you want to expose Typical registration method
native C++ DLL / OCX regsvr32
.NET Framework assembly exposed to COM Regasm.exe
.NET 5+ / 6+ / 8+ COM exposure register the generated .comhost.dll with regsvr32

When these are mixed up, a very common failure pattern is:

  • run regsvr32 on a managed DLL
  • DllRegisterServer is obviously missing
  • conclude that the DLL must be broken

That conclusion is usually wrong.

In worlds that need a type library as well, such as VBA, VB6, or some early-binding consumers, generating and registering the TLB is yet another separate issue. In .NET Framework, Regasm.exe /tlb can generate and register the type library, and Microsoft also explains that “type registration” and “type-library registration” are separate activities.15

4. Admin-rights issues are another big trap

4.1. Registration is wired into post-build and succeeds only under admin

In C++ builds with Visual Studio, it is perfectly normal in a mechanical sense to call regsvr32.exe from build events or custom build steps. Microsoft Learn even shows post-build examples that use regsvr32.exe.16

But possible and safe are not the same thing.

When a standard user runs regsvr32, it can fail with 0x80070005 because it cannot write to the registry or System32. Microsoft’s KB also describes the cause as lack of administrator rights.10

What often happens in practice is:

  • Visual Studio launched normally builds the binaries successfully
  • but only the post-build registration fails
  • the failure is overlooked
  • an old registration is still present from before, so the app happens to work locally
  • in a clean environment, it כמובן fails

In this kind of project, the safer default is to separate build from registration.

  • build should only produce binaries
  • registration should be a clear install step / script / installer step
  • in CI, the “needs registration” step should be a separate job from the build

That separation alone removes a surprising number of accidents.

4.2. Mixing per-user and per-machine registration breaks things

If the only memory you have is “COM looks in HKCR,” you can get stuck here.

Microsoft Learn explains that COM actually looks at HKEY_CURRENT_USER\\Software\\Classes first, and then deals with machine-wide information after that.3
It also explains that HKEY_CLASSES_ROOT is a merged view of HKLM\\Software\\Classes and HKCU\\Software\\Classes.717

That means situations like these are entirely normal:

  • developer A manually registers under their own user
  • it works for developer A
  • it does not work for developer B
  • it also fails for a service account
  • behavior changes again when running as administrator

Microsoft also states that applications requiring administrator rights should register dependent COM objects into the per-machine COM configuration store during installation.87

So in a real development team, the following distinction needs to be explicit:

  • is this developer-only per-user registration
  • or production per-machine registration for every user on the PC
  • or registration that must be visible to services or elevated apps

If that stays vague, you get classic confusion like “it works only as admin” or “it works from Explorer extension hosting, but not from the service.”

4.3. Running Visual Studio permanently as administrator is not a real fix

There are cases where elevation is necessary.
But if Visual Studio is always run as administrator, it can hide problems that should really be solved by the installer or by an explicit registration script.

Visual Studio itself also changes behavior in elevated mode. Microsoft Learn documents settings where per-user extensions are disabled when Visual Studio is run elevated.18

So, as an operating model, this is usually safer:

  • do normal development under normal user rights
  • run only the steps that actually require registration with an explicitly elevated Developer Command Prompt / PowerShell / installer
  • if something reproduces only as administrator, treat that assumption itself as a design fact that should be documented

That tends to cause fewer problems later.

5. ActiveX / OCX have their own extra traps

5.1. Design-time and runtime licenses can be different

One quietly troublesome part of OCX / ActiveX work is licensing.

Especially for older ActiveX controls, design-time license and run-time license can be separate. MFC ActiveX documentation also explains mechanisms where license files or license keys divide design-time from run-time use.1920

That creates situations like:

  • runtime use works
  • but placing the control on a form says there is no license
  • developer machine A can place it
  • developer machine B cannot

Errors like “license information for this component not found” or “you don’t have an appropriate license” are not unusual in older ActiveX environments.21

5.2. By the time it is inside WinForms, there is already a wrapper layer

When WinForms uses ActiveX, Windows Forms is not hosting the ActiveX control completely raw.
As Microsoft Learn explains in the Aximp.exe documentation, the ActiveX Control Importer generates a WinForms wrapper from the COM type library, and WinForms then works with that through an AxHost-based control.2223

So the problem is not one layer. It is at least these layers:

  1. the original OCX / ActiveX itself
  2. the type library
  3. the generated interop / wrapper
  4. the WinForms Designer / runtime

That is why things like these happen:

  • replacing the vendor OCX changes event signatures
  • re-adding the reference regenerates wrappers and creates a large diff
  • different machines generate slightly different interop results

Seeing it listed in Choose Toolbox Items does not mean you are safe.
It is better to validate whether the Designer can place it, whether runtime events arrive correctly, and whether the deployed environment works including the wrapper as separate checks.

5.3. If you underestimate STA / MTA and the message loop, things freeze

COM has to be initialized per thread with CoInitializeEx. Microsoft Learn explicitly says that each thread that uses COM must call CoInitializeEx for itself.24

Also, STA (single-threaded apartment) requires a message loop.2425

UI-oriented OCX / ActiveX components are often STA-based, so this kind of failure is common:

  • it works on the UI thread
  • it freezes when moved to Task.Run or the thread pool
  • events never come back
  • it reproduces only sometimes

In STA, there is also the rule that you must not simply copy interface pointers across threads; if you need cross-thread use, you must marshal them appropriately.2425

These bugs are as time-consuming as registry problems because they do not fail as clearly as 0x80040154. They often show up only as freezes, no callback, or occasional crashes.

6. A practical troubleshooting order that works in the field

In practice, it is often faster not to dive deep immediately, but to cut the problem in this order.

6.1. First, fix which process is what bitness

This is the first thing to check.

  • what is the host process (Visual Studio Designer / your own app / Office / Access / Explorer / browser-like host)
  • is that host 32-bit or 64-bit
  • is the target DLL / OCX 32-bit or 64-bit
  • is it in-proc or out-of-proc

If that is still fuzzy and you start from the registry, you usually get lost.

6.2. Next, check what kind of registration is actually correct

The next question is what should be registered with what.

  • native DLL / OCX → regsvr32
  • .NET Framework COM exposure → Regasm.exe
  • .NET 5+ / 6+ / 8+ COM exposure → .comhost.dll
  • a DLL with no self-registration support → not regsvr32

That classification alone prevents a lot of unforced errors.

6.3. After that, check where it was actually registered

A simple glance at HKCR is not enough.

You often need to look at:

  • HKCU\\Software\\Classes
  • HKLM\\Software\\Classes
  • 32-bit / 64-bit registry view if relevant
  • the target ProgID / CLSID / TypeLib
  • InprocServer32 / LocalServer32
  • ThreadingModel
  • the actual path to the referenced DLL

“It exists in HKCR” is still not enough. It only becomes meaningful once you line up who is looking, with which bitness, through which view.711

6.4. Finally, check whether permissions are only hiding the real shape

At the last step, check whether the issue is truly permissions, or whether permissions only change which registration is visible.

  • does behavior change between standard user and administrator
  • what changes if Visual Studio is elevated
  • does it reproduce under a service account or another user
  • does it also work in a clean installer-driven environment

If you only ever look at one developer machine, this is where people misread the situation very easily.

7. Operating decisions that reduce accidents

In COM / ActiveX / OCX development and maintenance, the way you decide the operating model can matter more than low-level implementation tricks.

7.1. Decide the bitness policy first

At the beginning, decide this explicitly:

  • are you going x86 only
  • is x64 the primary target
  • are you supporting both
  • does this component have to remain in-proc

If the vendor OCX is fixed to x86, ignoring that and making only the app x64 will usually fail later.
For the broader structure around this problem, How to Call a 64-bit DLL from a 32-bit Application - A Practical COM Bridge Case Study is closely related.

7.2. Decide the registration strategy

Registration is also better handled deliberately.

  • used by the whole machine → register per-machine through the installer
  • used only by that user → use per-user registration intentionally
  • stays completely inside your own app → consider registration-free COM
  • needed only for development → confine it to an explicit dev setup script

Registration-free COM is useful because it can hold activation information in a manifest instead of the registry, which helps reduce registration hell. Both registration-free COM on the Win32 side and RegFree COM on the .NET side are documented officially.26627

7.3. Do not leave too many generated assets outside source control

One common failure mode in OCX / ActiveX work is that dependencies scatter:

  • the OCX itself
  • dependent DLLs
  • the TLB
  • the .lic
  • interop DLLs
  • the AxHost wrapper
  • registration scripts
  • the sample host

If those exist only on someone’s local machine, that almost certainly becomes an accident a few months later.

At minimum, the following should be kept next to the code:

  • which version is assumed
  • what needs to be installed, and in what order
  • which commands register it
  • whether it is for x86 or x64

8. This kind of consultation is a good fit

This topic often delivers value even before a full rewrite starts, because triage and policy decisions alone can move the project forward.

For example, these are very compatible kinds of consulting work:

  • separating the causes of 0x80040154 or 0x80070005 into bitness, registration, and permission issues
  • checking how much of a broken Designer setup after moving to Visual Studio 2022 can realistically be rescued
  • keeping the vendor OCX in place while moving surrounding code toward .NET or C#
  • deciding how far to extend the life of x86-fixed assets, and where to bridge, wrap, or replace
  • redesigning install / deployment so the system no longer depends on manual regsvr32

The broader keep / wrap / replace decision itself is also much easier to organize together with How to Handle ActiveX / OCX Today - A Decision Table for Keep / Wrap / Replace.

9. Summary

When COM components and OCX / ActiveX work fail, the causes usually fall into these four groups:

  1. the bitness does not line up
  2. the registration method is wrong
  3. the registration scope is mismatched (HKCU / HKLM, 32-bit / 64-bit view)
  4. the system only appears to work because permissions happen to expose something

With Visual Studio 2022 becoming 64-bit, the old “it kind of worked somehow” style of setup became much easier to break in visible ways.12
That is exactly why, when touching COM / OCX / ActiveX today, the fastest path is often to align the environment assumptions before writing more code.

Instead of asking how many times to run regsvr32, it is usually much faster to sort out:

  • which process is hosting it
  • what bitness that process is
  • where it should be registered
  • whether that registration really should require admin
  • whether Designer and runtime are being checked separately

References

  1. Microsoft Learn, Visual Studio 2022 version 17.0 Release Notesdevenv.exe is now 64-bit only. https://learn.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.0  2 3

  2. Microsoft Learn, Troubleshoot 32-bit problems - Windows Forms — Visual Studio 2022 is a 64-bit process, cannot directly load 32-bit .NET / COM / ActiveX, and has out-of-process designer constraints. https://learn.microsoft.com/en-us/dotnet/desktop/winforms/visualstudio/troubleshoot-32bit  2 3 4 5

  3. Microsoft Learn, Classes and Servers — COM registration, HKCU / HKCR, self-registration, and DllRegisterServer. https://learn.microsoft.com/en-us/windows/win32/com/classes-and-servers  2 3 4

  4. Microsoft Learn, regsvr32 — syntax and role of regsvr32. https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/regsvr32  2

  5. Microsoft Learn, Registering Assemblies with COM — .NET Framework COM registration uses Regasm.exe. https://learn.microsoft.com/en-us/dotnet/framework/interop/registering-assemblies-with-com  2

  6. Microsoft Learn, Expose .NET Core components to COMEnableComHosting, generated .comhost.dll, regsvr32, and EnableRegFreeCom. https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com  2 3

  7. Microsoft Learn, Merged View of HKEY_CLASSES_ROOT — HKCR is a merged view of HKLM and HKCU. https://learn.microsoft.com/en-us/windows/win32/sysinfo/merged-view-of-hkey-classes-root  2 3 4 5

  8. Microsoft Learn, HKEY_CLASSES_ROOT Key — recommends per-machine COM registration for applications that require administrator rights. https://learn.microsoft.com/en-us/windows/win32/sysinfo/hkey-classes-root-key  2

  9. Microsoft Learn, COM Error Codes (Generic) (Winerror.h) — including REGDB_E_CLASSNOTREG (0x80040154). https://learn.microsoft.com/en-us/windows/win32/com/com-error-codes-1 

  10. Microsoft Learn, You receive 0x80070005 error when you try to register a DLL by using Regsvr32.exe — the typical permissions-related DLL-registration failure. https://learn.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/dllregisterserver-error  2

  11. Microsoft Learn, Registry Redirector — 32-bit / 64-bit registry views under WOW64. https://learn.microsoft.com/en-us/windows/win32/winprog64/registry-redirector  2 3 4

  12. Microsoft Learn, File System Redirector%windir%\\System32 on x64 Windows and WOW64 file-system redirection. https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector 

  13. Microsoft Learn, Overview of compatibility considerations for 32-bit programs on 64-bit versions of Windows — file and registry redirection under WOW64. https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/compatibility-limitations-32-bit-programs-64-bit-system 

  14. Microsoft Learn, Regasm.exe (Assembly Registration Tool) — the role of Regasm.exe and options such as /tlb. https://learn.microsoft.com/en-us/dotnet/framework/tools/regasm-exe-assembly-registration-tool 

  15. Microsoft Learn, Packaging a .NET Framework Assembly for COM — type libraries and Regasm.exe /tlb. https://learn.microsoft.com/en-us/dotnet/framework/interop/packaging-an-assembly-for-com 

  16. Microsoft Learn, Understanding Custom Build Steps and Build Events — includes a post-build example that uses regsvr32.exe. https://learn.microsoft.com/en-us/cpp/build/understanding-custom-build-steps-and-build-events 

  17. Microsoft Learn, The Windows registry for advanced users — behavior of HKCU\\Software\\Classes, HKLM\\Software\\Classes, and HKCR. https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/windows-registry-advanced-users 

  18. Microsoft Learn, Find, install, and manage extensions for Visual Studio — behavior of per-user extensions under elevated execution. https://learn.microsoft.com/en-us/visualstudio/ide/finding-and-using-visual-studio-extensions 

  19. Microsoft Learn, MFC ActiveX Controls: Licensing an ActiveX Control — design-time / run-time licensing and .LIC. https://learn.microsoft.com/en-us/cpp/mfc/mfc-activex-controls-licensing-an-activex-control 

  20. Microsoft Learn, Application Settings, MFC ActiveX Control Wizard — runtime-license generation and .lic files. https://learn.microsoft.com/en-us/cpp/mfc/reference/application-settings-mfc-activex-control-wizard 

  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. https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/license-information-for-this-component-not-found-you-don-t-have-an-appropriate-l 

  22. Microsoft Learn, Aximp.exe (Windows Forms ActiveX Control Importer) — converts ActiveX to a WinForms wrapper. https://learn.microsoft.com/en-us/dotnet/framework/tools/aximp-exe-windows-forms-activex-control-importer 

  23. Microsoft Learn, AxHost Class — the AxHost-based wrapper produced by ActiveX Control Importer. https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.axhost 

  24. Microsoft Learn, Initializing the COM LibraryCoInitializeEx, per-thread initialization, and the STA message loop. https://learn.microsoft.com/en-us/windows/win32/learnwin32/initializing-the-com-library  2 3

  25. Microsoft Learn, Single-Threaded Apartments — STA message loops, marshaling, and ThreadingModel. https://learn.microsoft.com/en-us/windows/win32/com/single-threaded-apartments  2

  26. Microsoft Learn, Creating Registration-Free COM Objects — registry-free activation through activation context. https://learn.microsoft.com/en-us/windows/win32/sbscs/creating-registration-free-com-objects 

  27. Microsoft Learn, Registration-Free COM Interop — .NET Framework registration-free COM interop. https://learn.microsoft.com/en-us/dotnet/framework/interop/registration-free-com-interop 

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.

Back to the Blog