Trampas de registro y bitness en el desarrollo COM/OCX/ActiveX
· Go Komura · COM, ActiveX, OCX, Visual Studio, Desarrollo en Windows, 32 bits, 64 bits, Interop
Historial de revisiones (primera versión, publicada el 15 Apr 2026)
- Primera publicación
Citar este artículo(DOI: 10.5281/zenodo.22173832)
Este artículo está archivado en Zenodo. A continuación se muestran tanto el DOI que siempre resuelve a la última versión como el DOI fijado a la versión que está leyendo.
Go Komura (2026). Trampas de registro y bitness en el desarrollo COM/OCX/ActiveX. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22173832 https://comcomponent.com/es/blog/com-ocx-activex-pitfalls-visual-studio-bitness-admin-rights/
- DOI (última versión)
- 10.5281/zenodo.22173832
- DOI (esta versión)
- 10.5281/zenodo.22173833
En los proyectos de componentes COM y OCX / ActiveX, los problemas surgen con más frecuencia en los límites entre entorno de ejecución, registro, host y permisos que en el código en sí.
Los síntomas típicos son estos:
- La compilación pasa, pero al iniciar aparece
0x80040154. - Funciona en el equipo de desarrollo propio, pero no en otros PC.
- Funciona en tiempo de ejecución, pero solo el Diseñador (Designer) de Visual Studio falla.
- Funciona al iniciar como administrador, pero falla con permisos normales.
- Se ejecuta
regsvr32y, sin razón aparente, el problema no se soluciona.
Esto no suele ser un error puntual, sino un estado en el que alguna premisa de COM deja de encajar en algún punto.
Si primero quiere ordenar los propios términos COM / ActiveX / OCX, le resultará más fácil captar el panorama general leyendo antes Qué son COM, ActiveX y OCX - diferencias y relaciones explicadas. En este artículo, como paso siguiente, se ordena dónde se suele atascar realmente el trabajo, incluyendo lo relativo a la bitness de Visual Studio y a los permisos de administrador.
1. Conclusión inicial
Por delante, dicho en términos que sirven en la práctica:
- Los problemas de COM / OCX / ActiveX suelen originarse más por una falta de coincidencia en bitness (32 bits / 64 bits), destino de registro, host y permisos que por la lógica del código.
- Como Visual Studio 2022 es un proceso de 64 bits, la integración en tiempo de diseño que antes funcionaba bajo la premisa de 32 bits se rompe tal cual. 12
regsvr32no es un comando mágico que registre cualquier cosa. Está pensado para servidores COM in-proc nativos (DLL / OCX). Para exponer .NET Framework como COM se usaRegasm.exe, y en .NET 5+ / .NET 6+ / .NET 8+ el flujo consiste en registrar el.comhost.dllgenerado. 3456- Pensar que «si funciona como administrador, está bien» es peligroso. No es raro que se vea por casualidad gracias a un registro per-user, o que el registro que debería hacer el instalador se haya resuelto a mano solo en el equipo de desarrollo. 378
En definitiva, al trabajar con COM / OCX / ActiveX, lo más seguro es mirar primero estos cuatro ejes:
- Qué proceso actúa como host.
- Si ese proceso es de 32 bits o de 64 bits.
- Dónde está registrado (HKCU / HKLM, vista de 32 bits / vista de 64 bits).
- Si esa operación o ejecución requiere permisos de administrador.
2. Visto al revés, desde los síntomas, esto es lo que suele verse
| Síntoma | Primera sospecha | Causa real habitual |
|---|---|---|
0x80040154 Class not registered |
No está registrado | En la práctica también ocurre cuando «solo está registrado en el otro bitness» o «solo está registrado para ese usuario» |
DllRegisterServer failed: 0x80070005 |
Permisos insuficientes | Se intenta registrar con un usuario estándar, o se registra en el post-build sin elevación |
| Solo el Diseñador falla en VS2022 | Limitación del Diseñador | El Visual Studio de 64 bits no puede leer directamente el COM / ActiveX de 32 bits |
| Solo funciona al iniciar como administrador | Problema de permisos | En el fondo suele ser un desajuste en el ámbito de registro o en el diseño de la instalación, más que los permisos en sí |
| No se puede llamar a un OCX de 32 bits desde una app de 64 bits | Limitación del propio mecanismo de COM | Un servidor in-proc no puede cargarse si su bitness no coincide con la del host |
| Funciona en el hilo de UI pero se congela en un hilo en segundo plano | Modelo de hilos | Incumplimiento de las premisas de STA / MTA, CoInitializeEx y el bucle de mensajes |
0x80040154 es, oficialmente, REGDB_E_CLASSNOTREG, literalmente Class not registered (clase no registrada). 9
Del mismo modo, el 0x80070005 de regsvr32 está documentado oficialmente como el caso en que no hay permisos de administrador y no se puede escribir en el registro o en System32. 10
Lo importante aquí es no confiar demasiado en el nombre literal del error.
Por ejemplo, Class not registered no siempre significa «completamente sin registrar»: también aparece solo porque está registrado en una vista de registro distinta o registrado en un lugar visible únicamente para ese usuario. 117
3. Problemas con la bitness de Visual Studio
3.1 Visual Studio 2022 pasó a ser de 64 bits
Este es el punto donde más se atasca hoy en día el desarrollo COM / ActiveX.
En Visual Studio 2022, devenv.exe es exclusivamente de 64 bits. 1
Por eso, en la experiencia de diseño de WinForms, Visual Studio no puede cargar directamente componentes de 32 bits. Microsoft también deja explícito que, como Visual Studio 2022 es un proceso de 64 bits, no puede cargar componentes .NET / COM / ActiveX de 32 bits. 2
Es decir, lo que antes funcionaba más o menos bajo estas premisas:
- El proyecto es x86.
- El ActiveX referenciado también es x86.
- El propio Visual Studio es de 32 bits.
en VS2022 se convierte en esta contradicción:
- La app en tiempo de ejecución puede funcionar en x86.
- Pero el Diseñador se ejecuta en el Visual Studio de 64 bits.
El resultado es un estado difícil de diagnosticar en el que la ejecución sigue viva pero solo el Diseñador falla. Esto ocurre porque el host en tiempo de ejecución es el proceso de la aplicación, mientras que el host en tiempo de diseño es el proceso de Visual Studio: como los hosts son distintos, también lo es la bitness. 2
3.2 Cambiar a AnyCPU no siempre resuelve el problema
Este es otro malentendido frecuente.
AnyCPU no es magia que vuelva neutrales también las dependencias.
Según explica Microsoft, incluso un componente que parece AnyCPU genera problemas en el tiempo de diseño de Visual Studio 2022 si, más adelante en la cadena, referencia un COM / ActiveX fijado a 32 bits. 2
Por eso, si el error no desaparece al pasar a AnyCPU, conviene sospechar más rápido de:
- Si hay alguna dependencia nativa de 32 bits más adelante en ese ensamblado.
- Si el ActiveX / OCX está fijado a x86.
- Si hay código que solo se carga durante el Diseñador.
3.3 La trampa de System32 y SysWOW64
En Windows x64, el nombre da una impresión que no coincide con la realidad, lo que suele generar confusión.
Microsoft Learn explica que, en Windows x64, %windir%\System32 está pensado para aplicaciones de 64 bits. El lado de 32 bits es redirigido a otra ubicación mediante el redirector del sistema de archivos de WOW64. 12
Con el registro ocurre algo similar: el redirector de registro de WOW64 muestra vistas lógicas distintas para 32 bits y para 64 bits. 11
Por eso, al diagnosticar en su propio equipo, es más seguro dejar explícito qué bitness de regsvr32 se está usando.
# Para registrar un DLL / OCX de 64 bits
C:\Windows\System32\regsvr32.exe vendor.ocx
# Para registrar un DLL / OCX de 32 bits en Windows x64
C:\Windows\SysWOW64\regsvr32.exe vendor.ocx
Lo problemático es cuando se registra con el regsvr32 del lado equivocado. El registro en sí tiene éxito, pero no es visible desde el proceso objetivo.
El resultado es una situación como esta:
regsvr32se ejecutó con éxito.- Pero la aplicación sigue mostrando
0x80040154. - Al mirar el registro, parece que está ahí.
- Pero se está mirando otra vista.
3.4 regsvr32 no permite registrar cualquier cosa
Este es otro punto donde abundan los malentendidos.
Los servidores COM in-proc nativos normalmente exportan DllRegisterServer / DllUnregisterServer para admitir el autorregistro. 3
regsvr32 es la herramienta que se usa con ese tipo de DLL / OCX. 4
Por otro lado, cuando se quiere usar un ensamblado de .NET Framework desde COM, lo habitual es Regasm.exe. Microsoft Learn también explica que para registrar un ensamblado que se usará con COM se debe usar Regasm.exe. 514
Además, en la exposición COM de .NET 5+ / .NET 6+ / .NET 8+ el panorama cambia un poco: al compilar con <EnableComHosting>true</EnableComHosting> se genera un *.comhost.dll, y ese archivo es el que se registra con regsvr32. 6
En resumen, a grandes rasgos queda así:
| Qué se quiere exponer | Medio de registro habitual |
|---|---|
| DLL / OCX nativo en C++ | regsvr32 |
| Ensamblado de .NET Framework expuesto como COM | Regasm.exe |
| Exposición COM en .NET 5+ / 6+ / 8+ | El .comhost.dll generado, con regsvr32 |
Regasm.exe se ejecuta desde el Developer Command Prompt / Developer PowerShell. Las tres formas más habituales son estas: 14
:: 1) Registrar las clases públicas del ensamblado
regasm myTest.dll
:: 2) Generar también la biblioteca de tipos y registrarla (para VBA / VB6 u otros que requieran un TLB)
regasm myTest.dll /tlb:myTest.tlb
:: 3) Sin modificar el registro directamente, volcar el contenido a un archivo .reg para revisarlo
regasm myTest.dll /regfile:myTest.reg
Para anular el registro se usa /unregister. Una biblioteca de tipos registrada con /tlb se anula especificando /tlb y /unregister juntos.
regasm myTest.dll /tlb:myTest.tlb /unregister
Hay dos restricciones en las que es fácil caer aquí. 14
- Un ensamblado que no se coloca en la GAC necesita
/codebase. Como/codebasegraba en el registro la ruta del archivo en el momento del registro, si más adelante se mueve el ensamblado, el inicio fallará. Microsoft también recomienda encarecidamente que los ensamblados registrados con/codebaselleven nombre seguro (strong name). /regfileno se puede combinar con/unregisterni con/tlb. Además, lo que/regfilevuelca son solo las entradas de las clases administradas: no generaTypeLibIDniInterfaceID. Pensar que «con distribuir el.regbasta para el registro» se queda corto.
Cuando se mezclan estas diferencias, el patrón habitual es este:
- Se ejecuta
regsvr32sobre un DLL administrado. - Como es lógico, no se encuentra
DllRegisterServer. - Y entonces se malinterpreta que «el DLL está roto».
En el mundo donde se necesita biblioteca de tipos (VBA / VB6 / algunos escenarios de enlace anticipado), la generación y el registro del TLB son además un problema aparte. En .NET Framework se puede generar y registrar la biblioteca de tipos con Regasm.exe /tlb, y Microsoft también explica que «registrar los tipos» y «registrar la biblioteca de tipos» son actividades distintas. 15
3.5 Cuando se quiere usar un in-proc de 32 bits desde un proceso de 64 bits ── DllSurrogate
En 3.3 y 3.4 se hablaba de si «el lugar y el medio de registro encajan». Aquí se añade una salida más para cuando el registro es correcto pero la bitness no encaja.
Por delante, la conclusión.
- Aunque
regsvr32haya tenido éxito, un proceso de 64 bits no puede cargar elInprocServer32de 32 bits. No es un defecto del registro, sino la premisa misma de in-proc. - Si quiere usar ese DLL desde el lado de 64 bits sin escribir un EXE nuevo, el remedio mínimo es añadir un valor
AppIDal CLSID y escribir en esa claveAppIDunDllSurrogatecon la cadena vacía. 16 - Lo que se inicia entonces no es el
dllhost.exedel lado del cliente, sino eldllhost.exede la bitness del DLL al que apuntaInprocServer32.
Por qué no es posible manteniéndolo in-proc
Lo que se recogía en la tabla de síntomas del capítulo 2, «No se puede llamar a un OCX de 32 bits desde una app de 64 bits», tiene la misma cara que 0x80040154, pero su contenido es distinto.
InprocServer32 es, literalmente, la designación del «servidor que se ejecuta dentro del proceso que llama». Que un proceso de 64 bits cargue el DLL de 32 bits escrito ahí no ocurre, por mucho que se corrija el registro. Que en 3.3 las vistas del registro estén separadas se debe también, en el fondo, a esta misma restricción.
Por eso, a partir de aquí ya no se trata de «arreglar el registro», sino de «sacarlo a otro proceso».
Qué hace el sustituto
DllSurrogate es la designación que sirve para montar un servidor DLL in-proc en un proceso sustituto (surrogate) y exponerlo como Local Server. 17
Si el valor se deja como cadena vacía, se usa el sustituto predeterminado que viene con Windows (dllhost.exe). 18
Si el DLL es de 32 bits, se inicia un sustituto de 32 bits, y el DLL se carga dentro de él in-proc, igual que hasta ahora. El cliente de 64 bits toca el objeto desde fuera de ese proceso, a través de un proxy.
Como esto se malinterpreta con facilidad, conviene dejarlo claro.
El sustituto no elimina la diferencia de bitness. Lo único que desaparece es la restricción de «tener que ir montado en el mismo proceso». La llamada pasa a ser out-of-proc, y se añaden los costes del marshaling y de la comunicación entre procesos. Como premisa, hace falta además que los tipos que se intercambian admitan marshaling (o bien IDispatch, o bien un proxy / stub registrado, o bien un tipo que el marshaler estándar pueda manejar).
Y una cosa más. Lo que se maneja bien con un sustituto son los objetos de automatización que se completan con llamadas a métodos y eventos. Un OCX visual, de los que se pegan en un formulario para que dibujen, parte de la premisa de tener su ventana dentro del proceso del host, así que no se resuelve solo con este remedio.
flowchart TB
accTitle: Si la activación acaba en in-proc o en el sustituto
accDescr: Diagrama que muestra que, cuando se solicita con un CLSCTX que incluye in-proc, si el CLSID de la misma vista tiene InprocServer32 se carga primero in-proc; que si no lo tiene y existe el registro de un servidor EXE, este se inicia antes que el sustituto; que si no hay registro de EXE y existe un DllSurrogate vacío se inicia el dllhost de la bitness del lado del DLL; y que si no hay ninguno de ellos no se puede activar.
req["El cliente solicita la activación"] --> ctx["Solicitud con un CLSCTX que incluye in-proc"]
ctx --> q1{"¿El CLSID de la misma vista tiene InprocServer32?"}
q1 -->|"Sí"| inproc["Se carga in-proc, igual que hasta ahora"]
q1 -->|"No"| q2{"¿Existe el registro de un servidor EXE?"}
q2 -->|"Sí"| exe["El EXE se inicia antes que el sustituto"]
q2 -->|"No"| q3{"¿Existe un DllSurrogate vacío?"}
q3 -->|"Sí"| host["Se inicia el dllhost de la bitness del lado del DLL"]
q3 -->|"No"| err["No se puede activar: 0x80040154"]
Figura 1: La activación se bifurca primero según si hay un InprocServer32 de la misma bitness visible; solo si no lo hay pasa al servidor EXE y después a un DllSurrogate vacío.
El conjunto mínimo de registro
Microsoft Learn enumera así las condiciones para que un servidor DLL se monte en un sustituto. 16
- La clave CLSID tiene un valor
AppIDy existe la claveAppIDcorrespondiente - En la llamada de activación está puesto
CLSCTX_LOCAL_SERVER, y en la clave CLSID no hayLocalServer32/LocalServer/LocalService - La clave CLSID tiene
InprocServer32 - El DLL al que apunta
InprocServer32existe realmente - Bajo la clave
AppIDhay un valorDllSurrogate
Si ese DLL va a funcionar en solitario dentro de un único sustituto, la forma de escribirlo que recomienda Microsoft es usar para el AppID el mismo GUID que el CLSID. 16
Para el registro bastan 3 líneas. Lo de abajo corresponde al caso de usar un COM DLL de 32 bits desde un host de 64 bits, y el GUID es un marcador de posición. Se da por supuesto que InprocServer32 ya se ha registrado antes con el regsvr32 del lado SysWOW64 (véase 3.3).
:: Ejecutar en un símbolo del sistema con permisos de administrador
set CLSID={11111111-2222-3333-4444-555555555555}
set APPID={11111111-2222-3333-4444-555555555555}
:: 1) Añadir el AppID al CLSID. Bajo CLSID, 32 y 64 están separados, así que se indica la vista explícitamente
:: En el caso inverso, usar un COM DLL de 64 bits desde un host de 32 bits, aquí se lee /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /v AppID /t REG_SZ /d "%APPID%" /f /reg:32
:: 2) Crear la clave AppID correspondiente. Classes\AppID es compartida entre 32 y 64, así que no hace falta indicar la vista
reg add "HKLM\SOFTWARE\Classes\AppID\%APPID%" /f
:: 3) Dejar DllSurrogate como un REG_SZ vacío. Si se omite /d, se crea con la cadena vacía
reg add "HKLM\SOFTWARE\Classes\AppID\%APPID%" /v DllSurrogate /t REG_SZ /f
Por qué se deja como cadena vacía
DllSurrogate es un REG_SZ, y el valor en sí es la ruta del sustituto. Si se deja como cadena vacía (o NULL), se usa el sustituto predeterminado del sistema; si se escribe una ruta, se intentará iniciar el sustituto personalizado que haya en esa ruta. 1718
Es decir, escribir ahí la ruta de dllhost.exe con la intención de ser amable resulta contraproducente: dejarlo vacío es en sí mismo la indicación de «usa el predeterminado».
Cabe señalar que HKLM\SOFTWARE\Classes\AppID es, desde Windows 7 / Windows Server 2008 R2, una clave compartida entre 32 bits y 64 bits, en la que no hay distinción de vista. 19
Bajo CLSID, en cambio, las vistas sí están separadas, de modo que, si el servidor es de 32 bits, el valor AppID hay que ponerlo en el lado del CLSID de la vista de 32 bits. Cuando no surte efecto, revise siempre estos dos puntos como un conjunto.
Esto parece contradecir lo de 3.3 y 6.3, «si solo está registrado en el lado de 32 bits, desde un proceso de 64 bits sale 0x80040154», pero aquello iba de in-proc. En la activación out-of-proc, cuando ni el cliente ni el servidor expresan una preferencia de bitness, COM busca un servidor con la bitness que encaja con el cliente y, si no lo hay, inicia el servidor de la otra bitness. 20
Por eso este procedimiento funciona dejando el lado del CLSID en la vista de 32 bits. No hace falta copiar InprocServer32 a la vista de 64 bits.
La bitness del dllhost.exe que se inicia
Este es el punto que más se malinterpreta.
La bitness del sustituto la determina no el lado del cliente, sino el DLL al que apunta InprocServer32. Como el DLL se carga in-proc dentro del sustituto, es lo lógico.
| COM DLL que se carga dentro | dllhost.exe que se inicia |
|---|---|
| 32 bits | %SystemRoot%\SysWOW64\dllhost.exe |
| 64 bits | %SystemRoot%\System32\dllhost.exe |
Si se mira la línea de comandos en el Administrador de tareas o en Process Explorer, el proceso aparece con la forma dllhost.exe /Processid:{...}. El GUID que figura ahí no es el CLSID, sino el AppID. Si se busca creyendo que es el CLSID no se encuentra, así que tenga cuidado.
Si hay LocalServer32, gana el EXE
Microsoft Learn deja explícito que, cuando hay LocalServer / LocalServer32 / LocalService, el inicio del servidor EXE o del servicio tiene siempre prioridad sobre montar el DLL en un sustituto. 16
Es decir, el sustituto es «la salida para cuando no hay un servidor EXE». Aunque se añada DllSurrogate a un CLSID que ya tiene registrado un servidor EXE, esa vía no se usará. Y lo que aquí tiene prioridad la tiene únicamente frente al sustituto, no frente a in-proc.
Conviene tener presente también el efecto sobre los llamadores existentes, y así se va sobre seguro.
Aunque se añada DllSurrogate, un cliente que crea el objeto con un CLSCTX que incluye in-proc sigue como hasta ahora, in-proc, mientras la bitness encaje. Porque, al pasar varios CLSCTX unidos con OR, se prueban en el orden de la enumeración (in-proc → local → remote) y, en la fase in-proc, la clave InprocServer32 se usa si existe. 20
Es el caso de las llamadas que pasan tanto in-proc como local, como CLSCTX_ALL o CLSCTX_SERVER. A la inversa, los puntos que llaman solo con CLSCTX_INPROC_SERVER no pasan al sustituto aunque se añada DllSurrogate.
Y visto desde un cliente de 64 bits, el InprocServer32 que se metió en la vista de 32 bits no existe en la vista de 64 bits. La fase in-proc se atraviesa sin más, con «no hay clave correspondiente», así que se pasa directamente a la activación del lado local (el sustituto). No es que esté fallando al intentar cargar un DLL de otra bitness. 2019
Cómo comprobarlo
Para saber si ha quedado registrado, lo seguro es volver a leerlo indicando la vista de forma explícita.
reg query "HKLM\SOFTWARE\Classes\CLSID\{11111111-2222-3333-4444-555555555555}\InprocServer32" /ve /reg:32
reg query "HKLM\SOFTWARE\Classes\CLSID\{11111111-2222-3333-4444-555555555555}" /v AppID /reg:32
reg query "HKLM\SOFTWARE\Classes\AppID\{11111111-2222-3333-4444-555555555555}" /v DllSurrogate
No omita la primera línea. Como reg add crea la clave indicada si no existe, si se teclea mal el GUID o se ha registrado en otra vista, queda una clave CLSID vacía en la que solo está el valor AppID, y la segunda línea pasa igualmente. Solo cuando se comprueba también que InprocServer32 está en esa misma vista se puede decir que se ha verificado de verdad.
En DllSurrogate, lo correcto es que salga con el valor vacío. Después, se crea el objeto desde un cliente de 64 bits y se mira si se inicia el dllhost.exe del lado SysWOW64.
- Sigue saliendo
0x80040154→ revise si el valorAppIDestá puesto en el lado del CLSID de la vista de 32 bits, y si el DLL al que apuntaInprocServer32existe realmente - El
dllhost.exese inicia, pero la llamada se cae → compruebe las premisas del marshaling (IDispatch/ proxy-stub / marshaler estándar) - Se inicia otro EXE → en ese CLSID quedan
LocalServer32u otros
Hasta aquí, el tema del «registro»
Si basta con el sustituto o si conviene escribir un EXE auxiliar propio en el lado de 32 bits no es una cuestión de registro, sino de cómo se elige la configuración. Eso está ordenado en el apartado 5.2, «Llevar un OCX de 32 bits al lado de 64 bits», de ActiveX / OCX: cómo tratarlos hoy — tabla de decisión para mantener, envolver o reemplazar.
4. Problemas con los permisos de administrador
4.1 Registrar en el post-build y que solo funcione como administrador
En una compilación C++ de Visual Studio, es perfectamente normal invocar regsvr32.exe desde un build event o un custom build step. Microsoft Learn incluso presenta el registro con regsvr32.exe como ejemplo de post-build event. 21
Ahora bien, que se pueda hacer y que sea seguro hacerlo son cosas distintas.
Al ejecutar regsvr32 con un usuario estándar, puede no ser posible escribir en el registro o en System32, lo que produce 0x80070005. El KB de Microsoft también identifica la causa como la falta de permisos de administrador. 10
Lo que suele ocurrir aquí es este tipo de accidente:
- Al iniciar Visual Studio con permisos normales, la compilación pasa.
- Pero solo falla el registro del post-build.
- Se pasa por alto el registro de error en el log.
- Como queda el registro antiguo de una vez anterior, en el equipo propio funciona por casualidad.
- En un entorno limpio, evidentemente, no funciona.
En este tipo de proyectos, lo básico es separar la compilación del registro.
- La compilación solo genera los binarios.
- El registro se hace en un install step / script / instalador explícito.
- En CI, el «paso que requiere registro» va en un job separado del de compilación.
Solo con esta separación se reducen bastante los accidentes.
4.2 Mezclar registro per-user y per-machine rompe las cosas
Si se recuerda solo que «COM mira HKCR», aquí es donde uno se atasca.
En realidad, tal como indica Microsoft Learn, COM mira primero HKEY_CURRENT_USER\Software\Classes y solo después maneja la información de todo el equipo. 3
Además, HKEY_CLASSES_ROOT es la vista combinada (merged view) de HKLM\Software\Classes y HKCU\Software\Classes. 722
Es decir, es normal que ocurra esto:
- Se registró manualmente con el usuario del desarrollador A.
- Funciona con la cuenta de A.
- No funciona con el desarrollador B.
- Tampoco funciona con una cuenta de servicio.
- El comportamiento cambia al ejecutarlo como administrador.
Microsoft también indica que las aplicaciones que requieren permisos de administrador deberían registrar los objetos COM de los que dependen, durante la instalación, en el almacén de configuración COM per-machine. 87
Por eso, en el trabajo de desarrollo conviene dejar clara esta distinción:
- ¿Es un registro de desarrollo que solo usa el propio usuario?
- ¿Es un registro de producción que usan todos los usuarios de esa máquina?
- ¿Es un registro al que hacen referencia un servicio o una aplicación elevada?
Si esto queda ambiguo, se acaba en situaciones como «por alguna razón solo funciona como administrador» o «funciona en la extensión de Explorer pero no en el servicio».
4.3 Ejecutar Visual Studio siempre «como administrador» no es una solución
Es cierto que hay situaciones en las que resulta necesario. Sin embargo, mantener Visual Studio siempre iniciado como administrador puede acabar ocultando, mediante la elevación del propio IDE, un problema que en realidad debería resolver el instalador o un script de registro.
Es más, el propio Visual Studio cambia el tratamiento de las extensiones per-user cuando se ejecuta elevado. Microsoft Learn explica que existe una configuración por la cual, al ejecutar Visual Studio elevado, se deshabilitan las extensiones per-user. 23
Por eso, como práctica operativa, resulta menos problemático a la larga:
- Hacer el desarrollo habitual con permisos normales.
- Realizar solo los pasos que requieren registro en un Developer Command Prompt / PowerShell / instalador explícitamente elevado.
- Si «solo se reproduce como administrador», documentar esa premisa como parte de la especificación.
5. Problemas específicos de ActiveX / OCX
5.1 La licencia de tiempo de diseño y la de tiempo de ejecución son distintas
Un problema discretamente molesto en OCX / ActiveX es el de las licencias.
En especial, en los controles ActiveX antiguos, la licencia de tiempo de diseño (design-time license) y la licencia de tiempo de ejecución (run-time license) pueden estar separadas. La documentación de ActiveX de MFC también explica el mecanismo que distingue diseño y ejecución mediante archivos de licencia y claves de licencia. 2425
En este terreno ocurre lo siguiente:
- Se puede usar en tiempo de ejecución.
- Pero al intentar colocarlo en un formulario, aparece «no hay licencia».
- Se puede colocar en el equipo de desarrollo A.
- No se puede colocar en el equipo de desarrollo B.
Errores como «no se encuentra la licencia de este componente» o «no hay una licencia adecuada» no son raros en los ActiveX antiguos. 26
5.2 Al montarlo en WinForms, ya hay un contenedor de por medio
Al usar ActiveX en WinForms, Windows Forms no hospeda el ActiveX tal cual.
Tal como indica la documentación de Aximp.exe en Microsoft Learn, el ActiveX Control Importer genera un contenedor (wrapper) para WinForms a partir de la biblioteca de tipos COM, y lo trata como un control basado en AxHost. 2728
Es decir, el problema no está en una sola capa, sino repartido en varias:
- El OCX / ActiveX original en sí.
- La biblioteca de tipos.
- El interop / wrapper generado.
- El Diseñador / tiempo de ejecución de WinForms.
Por eso ocurren cosas como estas:
- Al sustituir el OCX del proveedor, cambió la firma de los eventos.
- Al volver a añadir la referencia, se regeneró el wrapper y aparecieron muchísimas diferencias.
- El resultado de la generación del interop varía ligeramente entre equipos de desarrollo.
Que aparezca en Choose Toolbox Items no significa que todo esté bien.
Es más seguro verificar por separado si el Diseñador puede colocarlo, si los eventos llegan en tiempo de ejecución y si, en el destino de distribución, el conjunto incluido el wrapper funciona.
5.3 Subestimar STA / MTA y el bucle de mensajes provoca bloqueos
Antes de nada, se definen solo tres términos.
| Término | Significado |
|---|---|
| Apartamento (apartment) | Un grupo lógico que reúne objetos COM y subprocesos (hilos) bajo las mismas reglas de simultaneidad. Un objeto solo puede pertenecer a un apartamento |
| STA (single-threaded apartment) | Un apartamento al que pertenece un único subproceso. Las llamadas a ese objeto siempre se ejecutan en ese único subproceso |
| Marshaling | El mecanismo con el que COM hace de puente en las llamadas al pasar punteros de interfaz entre apartamentos, pasando por un proxy y un stub |
COM debe inicializarse con CoInitializeEx en cada subproceso que lo use. Microsoft Learn también deja explícito que cada subproceso que use COM debe llamar individualmente a CoInitializeEx. 29
Además, en un STA (single-threaded apartment) se necesita un bucle de mensajes. 2930
Por qué se bloquea todo si no hay bucle de mensajes
Si aquí solo se memoriza la conclusión, luego no se puede aplicar el conocimiento, así que conviene abrir un nivel más el mecanismo.
COM crea, por cada STA, una ventana oculta de la clase de ventana OleMainThreadWndClass. Cuando se llama a ese objeto desde otro apartamento, la llamada no se convierte en una llamada a función directa, sino que llega como un mensaje de ventana dirigido a esa ventana oculta. Cuando el subproceso del STA extrae el mensaje y lo despacha, el procedimiento de ventana correspondiente llama al método de la interfaz asociado. 30
Es decir, el bucle de mensajes no es «una buena práctica que conviene tener», sino el propio mecanismo que entrega las llamadas.
sequenceDiagram
accTitle: Por qué se bloquea una llamada sin bucle de mensajes en un STA
accDescr: Diagrama de secuencia que muestra cómo una llamada desde otro subproceso se convierte en un mensaje de ventana, cómo el subproceso STA la extrae mediante el bucle de mensajes y la entrega al objeto, y cómo, si el bucle se detiene, el llamador queda esperando indefinidamente
participant Caller as Llamador desde otro subproceso
participant Queue as Cola de mensajes de la ventana oculta
participant STA as Subproceso STA
participant Obj as OCX / ActiveX
Caller->>Queue: La llamada al método se apila como un mensaje
STA->>Queue: El bucle de mensajes la extrae
Queue->>Obj: El procedimiento de ventana llama al método correspondiente
Obj-->>Caller: Se devuelve el valor de retorno
Note over STA,Obj: Si el bucle de mensajes está detenido,<br/>esta extracción no ocurre y el llamador queda esperando
Figura 2: Una llamada desde otro subproceso llega como mensaje a la ventana oculta y solo alcanza al objeto cuando el bucle de mensajes la extrae.
Por eso, bloquear el subproceso STA provoca un bloqueo total. Escribir código como usar Task.Wait() o Task.Result en el subproceso de UI, o esperar con WaitOne, detiene la extracción de mensajes, de modo que dejan de entregarse los callbacks de COM y las llamadas entre apartamentos, y se produce un interbloqueo (deadlock). 30
Por la misma razón, no se debe copiar tal cual a otro subproceso el puntero de interfaz de un objeto STA. Si se copia, la llamada que debería entregarse al subproceso STA se ejecuta directamente en el otro subproceso, y se produce una concurrencia que el objeto no espera. Al cruzar apartamentos, hay que hacer marshaling con CoMarshalInterThreadInterfaceInStream y CoGetInterfaceAndReleaseStream. 30
En particular, los OCX / ActiveX orientados a UI suelen asumir STA, así que se convierte en un fallo molesto de este tipo:
- Funciona en el subproceso de UI.
- Se bloquea al enviarlo a
Task.Runo a un ThreadPool. - Los eventos no vuelven.
- Solo se reproduce a veces.
Además, en un STA rige también la premisa de que no se debe copiar el puntero de interfaz tal cual a otro subproceso, y de que, si hace falta, hay que hacer marshaling. 2930
Este tipo de fallo no es tan «amable» como 0x80040154: solo se manifiesta como se bloquea, no vuelve, a veces se cae, así que consume tanto tiempo como los problemas de registro.
6. Orden de diagnóstico que funciona en la práctica
En la práctica, es más rápido diagnosticar en este orden que profundizar desde el principio.
6.1 Primero, fijar «qué bitness tiene cada proceso»
Lo primero que hay que mirar es esto:
- Cuál es el host (Diseñador de Visual Studio / aplicación propia / Office / Access / Explorer / entorno compatible con navegador).
- Si ese host es de 32 bits o de 64 bits.
- Si el DLL / OCX en cuestión es de 32 bits o de 64 bits.
- Si es in-proc o out-of-proc.
Si se empieza a mirar el registro sin haber fijado estos cuatro puntos, no queda claro qué vista hay que consultar y la investigación gira en el vacío. Fije esto primero.
6.2 A continuación, confirmar «el tipo de registro»
Lo siguiente que hay que verificar es con qué se debe registrar cada cosa correctamente:
- DLL / OCX nativo →
regsvr32 - Exposición COM de .NET Framework →
Regasm.exe - Exposición COM de .NET 5+ / 6+ / 8+ →
.comhost.dll - Un DLL que directamente no tiene self-registration → no es
regsvr32
Solo con esta clasificación se evitan bastantes intentos fallidos.
6.3 Después, comprobar «dónde quedó registrado»
No basta con mirar solo HKCR. Hay que revisar:
HKCU\Software\ClassesHKLM\Software\Classes- Si hace falta, la vista de registro de 32 bits / 64 bits
- El ProgID / CLSID / TypeLib en cuestión
InprocServer32/LocalServer32- El ThreadingModel
- La ruta física del DLL al que se hace referencia
Con «está en HKCR» no basta: solo tiene sentido cuando además se sabe desde quién, con qué bitness y qué vista se está mirando. 711
Las rutas de clave que hay que revisar
Al abrir HKEY_CLASSES_ROOT\CLSID\{...}, solo se muestra lo visible según la bitness de la herramienta que se está ejecutando y el usuario actual. Para el diagnóstico, conviene mirar por separado las cuatro ubicaciones reales previas a la fusión. Todo lo que cuelga de CLSID es objeto de redirección WOW64, y la ubicación física del lado de 32 bits es Wow6432Node, debajo de Classes. 19
| Qué se quiere ver | Ruta de registro |
|---|---|
| Toda la máquina / vista de 64 bits | HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{CLSID} |
| Toda la máquina / vista de 32 bits | HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSID\{CLSID} |
| Solo ese usuario / vista de 64 bits | HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{CLSID} |
| Solo ese usuario / vista de 32 bits | HKEY_CURRENT_USER\SOFTWARE\Classes\Wow6432Node\CLSID\{CLSID} |
| Obtener el CLSID a partir del ProgID | Valor predeterminado de HKEY_CLASSES_ROOT\{ProgID}\CLSID |
El regedit.exe de Windows 10 / 11 es un proceso de 64 bits, así que con introducir directamente las rutas anteriores se pueden abrir ambas vistas sin más. Pegando la ruta en la barra de direcciones se llega directamente a esa posición.
Cabe señalar que la ruta que incluye Wow6432Node es una ubicación física, y Microsoft recomienda no tocarla directamente desde el código de una aplicación. No hay problema en consultarla durante una investigación manual, pero desde un script o una aplicación es más seguro usar la especificación de vista que se describe más adelante. 11
Comprobación mediante comandos
Con reg.exe se puede indicar explícitamente la vista mediante /reg:32 y /reg:64.
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
Si se quiere ver las cuatro ubicaciones a la vez desde PowerShell, se pasa la vista a RegistryKey.OpenBaseKey. Como el resultado no depende de la bitness del propio PowerShell, este método es más fiable para el diagnóstico.
# Se indica en un solo lugar el ProgID que se quiere investigar
$progId = 'Vendor.Control.1'
# 1) Obtener el CLSID a partir del ProgID (HKCR es la vista combinada de HKLM y HKCU)
$clsid = (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\$progId\CLSID" -Name '(default)' -ErrorAction Stop).'(default)'
"ProgID $progId -> CLSID $clsid"
# 2) Comprobar de forma exhaustiva las 4 ubicaciones (2 hives x 2 views)
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} : sin registrar' -f $hive, $view
}
else {
'{0,-12} {1,-11} : {2} (ThreadingModel={3})' -f $hive, $view, $key.GetValue(''), $key.GetValue('ThreadingModel')
$key.Dispose()
}
$base.Dispose()
}
}
El resultado de estas 4 líneas es, directamente, la respuesta del diagnóstico.
- Las 4 ubicaciones dan «sin registrar» → realmente no está registrado. Revise si el medio de registro coincide con la clasificación del apartado 6.2.
- Solo aparece en
Registry32→ solo está registrado en el lado de 32 bits. Desde un proceso de 64 bits dará0x80040154. - Solo aparece en
CurrentUser→ solo es visible para ese usuario. No se ve desde otro usuario, una cuenta de servicio o un proceso elevado. - Aparece la ruta pero no funciona → compruebe si el archivo al que apunta el valor predeterminado de
InprocServer32existe realmente y si su bitness coincide con la del host.
6.4 Por último, comprobar «si algo queda oculto por los permisos»
Por último, se confirma si el problema es realmente de permisos, o si los permisos solo están dejando ver un registro distinto.
- ¿Cambia el comportamiento entre usuario estándar y administrador?
- ¿Qué cambia al elevar Visual Studio?
- ¿Se reproduce también con una cuenta de servicio o con otro usuario?
- ¿Se cumple también en un entorno limpio instalado mediante el instalador?
Si solo se ha comprobado en un único equipo de desarrollo, es muy fácil equivocarse en este punto.
7. Prácticas operativas que, decididas de antemano, reducen los accidentes
En el desarrollo y mantenimiento de COM / ActiveX / OCX, a veces resulta más eficaz cómo se decide la operativa que las técnicas de implementación.
7.1 Decidir de antemano la política de bitness
Lo primero es decidir esto:
- ¿Se va a mantener x86 fijo?
- ¿Se toma x64 como estándar?
- ¿Se dará soporte a ambos?
- ¿Ese componente tiene que ser obligatoriamente in-proc?
En particular, si el OCX del proveedor está fijado a x86, ignorar ese hecho y convertir solo la aplicación a x64 acaba causando problemas más adelante. Este tema se relaciona, en una configuración más amplia, con Caso práctico de puente COM: llamar a un DLL de 64 bits desde una aplicación de 32 bits.
7.2 Decidir la estrategia de registro
Tampoco conviene improvisar el registro sobre la marcha.
- Se usa en toda la máquina → registro per-machine mediante el instalador.
- Solo lo usa ese usuario → usar per-user de forma deliberada.
- Se queda cerrado dentro de la propia aplicación → considerar registration-free COM.
- Solo hace falta para desarrollo → confinarlo en un script de configuración de desarrollo explícito.
Registration-free COM permite mantener la información de activación en un manifiesto en lugar de en el registro, por lo que es un medio eficaz para reducir el infierno del registro. Tanto el registration-free COM del lado Win32 como el RegFree COM del lado .NET cuentan con documentación oficial. 31632
Como mecanismo, cuando COM procesa CoCreateInstance u otras llamadas similares, el orden es: primero busca en el contexto de activación, y si no hay información allí, consulta el registro. El manifiesto es el archivo que declara el contenido de ese contexto de activación. 31
Se necesitan dos manifiestos, y ambos se colocan en la misma carpeta que el exe.
El primero es el manifiesto de ensamblado del lado del componente (VendorCtl.manifest). En él se escribe qué archivo provee qué CLSID. 33
<?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>
El segundo es el manifiesto de aplicación del lado de la app (MyApp.exe.manifest). En él solo se escribe la dependencia respecto al ensamblado anterior.
<?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>
Los puntos donde es fácil equivocarse al escribirlos son estos: 33
- El
assemblyIdentitydel lado dependiente debe coincidir exactamente con elassemblyIdentitydel lado del componente. Si se desvía aunque sea uno solo dename,versionoprocessorArchitecture, no se resuelve. - El nombre del ensamblado y el nombre del DLL deben ser distintos. Cuando el manifiesto se coloca como archivo separado, el nombre del ensamblado y el del manifiesto deben ser distintos del nombre del DLL.
- Los nombres de elementos y atributos distinguen mayúsculas de minúsculas. Si se escribe
comClasscomoComClass, no se lee. processorArchitecturees la bitness en sí misma. No se puede resolver un OCX x86 desde una aplicaciónamd64. Asegúrese de que coincida siempre con la política de bitness del apartado 7.1.- Si se incrusta un OCX, puede hacer falta el conjunto de atributos
miscStatus. La información equivalente a la claveMiscStatusdel registro debe transcribirse en el manifiesto comomiscStatus/miscStatusContent, entre otros.
7.3 No dejar los artefactos generados fuera del control de versiones
Algo habitual en el mundo OCX / ActiveX es la dispersión de las dependencias:
- El OCX en sí.
- Los DLL de los que depende.
- El TLB.
- El
.lic. - El DLL de interop.
- El wrapper de AxHost.
- Los scripts de registro.
- El host de ejemplo.
Si todo esto existe solo en el equipo local de una persona, con toda seguridad habrá un accidente unos meses después.
Como mínimo, es más seguro dejar registrado en el mismo lugar que el código:
- Qué versión se está asumiendo.
- Qué se instala y en qué orden.
- Con qué comando se registra.
- Para cuál de x86 / x64 está destinado.
8. Consultas con las que este enfoque encaja bien
En este tema, incluso solo el diagnóstico y el orden de criterios, antes de entrar de lleno en una reforma integral, suelen aportar valor por sí solos.
Por ejemplo, encaja especialmente bien con consultas como estas:
- Quiero desglosar la causa de
0x80040154o0x80070005en bitness, registro y permisos. - Al pasar a Visual Studio 2022 se rompió el Diseñador, y quiero ver hasta dónde se puede rescatar.
- Quiero conservar el OCX del proveedor y acercar lo que lo rodea a .NET o C#.
- Quiero decidir hasta dónde alargar la vida de los activos fijados en x86, y a partir de qué punto aplicar bridge / wrap / replace.
- Quiero dejar de depender de
regsvr32manual y rediseñar la instalación / el despliegue.
Para la decisión entre reemplazar, envolver o conservar, también puede servir de referencia Cómo tratar hoy ActiveX / OCX - tabla de decisión entre conservar, envolver y reemplazar.
9. Resumen
Cuando surgen problemas en el desarrollo de componentes COM u OCX / ActiveX, la causa suele reducirse a estas cuatro:
- La bitness no encaja.
- El método de registro es incorrecto.
- El ámbito de registro (HKCU / HKLM, vista de 32 / 64 bits) está desajustado.
- Se da por normal un estado que solo se ve por casualidad gracias a los permisos.
Con el paso a 64 bits de Visual Studio 2022, los diseños antiguos que «funcionaban de algún modo» se han vuelto mucho más propensos a salir a la superficie. 12 Precisamente por eso, al trabajar con COM / OCX / ActiveX, el atajo consiste en alinear las premisas del entorno antes de escribir código.
Más que cuántas veces se ejecute regsvr32, ordenar estos puntos resuelve el problema mucho más rápido:
- Qué proceso actúa como host.
- Qué bitness tiene ese proceso.
- Dónde debería registrarse.
- Si ese registro realmente exige permisos de administrador.
- Si se está distinguiendo entre el Diseñador y el tiempo de ejecución.
Referencias
-
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 es un proceso de 64 bits y no puede cargar directamente .NET / COM / ActiveX de 32 bits; limitaciones del out-of-process designer. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Classes and Servers — Registro de COM, HKCU / HKCR, autorregistro (self-registration) y
DllRegisterServer. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, regsvr32 — Sintaxis y función de
regsvr32. ↩ ↩2 -
Microsoft Learn, Registro de ensamblados en COM — El registro COM de .NET Framework se hace con
Regasm.exe. ↩ ↩2 -
Microsoft Learn, Exposición de componentes de .NET Core a COM —
EnableComHosting, el.comhost.dllgenerado,regsvr32,EnableRegFreeCom. ↩ ↩2 ↩3 -
Microsoft Learn, Merged View of HKEY_CLASSES_ROOT — HKCR es la vista combinada (merged view) de HKLM y HKCU. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, HKEY_CLASSES_ROOT Key — Se recomienda que las aplicaciones que requieren permisos de administrador se registren en la configuración COM per-machine. ↩ ↩2
-
Microsoft Learn, COM Error Codes (Generic) (Winerror.h) —
REGDB_E_CLASSNOTREG (0x80040154), entre otros. ↩ -
Microsoft Learn, You receive 0x80070005 error when you try to register a DLL by using Regsvr32.exe — Ejemplo típico de fallo en el registro de un DLL por falta de permisos. ↩ ↩2
-
Microsoft Learn, Registry Redirector — Las vistas de registro de 32 y 64 bits en WOW64, el hecho de que la ubicación física de
HKLM\SoftwareseaWow6432Node, y que las aplicaciones no deben tocar directamente la ruta física. ↩ ↩2 ↩3 ↩4 ↩5 -
Microsoft Learn, File System Redirector —
%windir%\System32en Windows x64 y la redirección del sistema de archivos de WOW64. ↩ -
Microsoft Learn, Introducción a las consideraciones de compatibilidad de programas de 32 bits en versiones de 64 bits de Windows — Redirección de archivos y de registro mediante WOW64. ↩
-
Microsoft Learn, Regasm.exe (Assembly Registration Tool) — Función de
Regasm.exey opciones como/tlb. ↩ ↩2 ↩3 -
Microsoft Learn, Packaging a .NET Framework Assembly for COM — La biblioteca de tipos y
Regasm.exe /tlb. ↩ -
Microsoft Learn, Registering the DLL Server for Surrogate Activation — Las condiciones para montarse en un sustituto, que si hay
LocalServer/LocalServer32/LocalServicetiene prioridad el inicio del servidor EXE o del servicio, y la configuración que usa para elAppIDel mismo GUID que el CLSID. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, DllSurrogate — Que el
DllSurrogateque cuelga deAppIDes unREG_SZ, y que con la cadena vacía se usa el sustituto predeterminado del sistema, mientras que si se escribe una ruta se usa el sustituto personalizado de esa ruta. ↩ ↩2 -
Microsoft Learn, Using the system-supplied surrogate — Que al especificar la cadena vacía o
NULLse inicia el sustituto predeterminado del sistema, y el tratamiento del modelo de subprocesos y del tiempo de vida del proceso dentro del sustituto. ↩ ↩2 -
Microsoft Learn, CLSCTX enumeration — Que al pasar varios
CLSCTXunidos con OR se prueban en el orden de la enumeración, que en la fase in-proc se usa la claveInprocServer32si existe, y que cuando ni el cliente ni el servidor expresan una preferencia de bitness se elige el servidor con la bitness que encaja con el cliente y, si no lo hay, se inicia el de la otra bitness. ↩ ↩2 ↩3 -
Microsoft Learn, Understanding Custom Build Steps and Build Events — Ejemplo de uso de
regsvr32.exeen un post-build event. ↩ -
Microsoft Learn, El registro de Windows para usuarios avanzados —
HKCU\Software\ClassesyHKLM\Software\Classes, y el comportamiento de HKCR. ↩ -
Microsoft Learn, Find, install, and manage extensions for Visual Studio — Tratamiento de las extensiones per-user al ejecutar en modo elevado. ↩
-
Microsoft Learn, MFC ActiveX Controls: Licensing an ActiveX Control — Licencia de tiempo de diseño / tiempo de ejecución,
.LIC. ↩ -
Microsoft Learn, Application Settings, MFC ActiveX Control Wizard — Generación de la licencia de tiempo de ejecución y el archivo
.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) — Convierte ActiveX en un wrapper para WinForms. ↩
-
Microsoft Learn, AxHost Class — El wrapper basado en AxHost que genera el ActiveX Control Importer. ↩
-
Microsoft Learn, Inicialización de la biblioteca COM —
CoInitializeEx, la inicialización por cada subproceso, el bucle de mensajes de STA. ↩ ↩2 ↩3 -
Microsoft Learn, Apartamentos de un solo subproceso (Single-Threaded Apartments) — El bucle de mensajes de STA, el marshaling,
ThreadingModel. ↩ ↩2 ↩3 ↩4 ↩5 -
Microsoft Learn, Creación de objetos COM sin registro (Registration-Free COM) — COM sin necesidad de registro mediante el contexto de activación (activation context). ↩ ↩2
-
Microsoft Learn, Interoperabilidad COM sin necesidad de registro — La interoperabilidad COM sin registro (registration-free) de .NET Framework. ↩
-
Microsoft Learn, Assembly Manifests — Los atributos de
assembly/assemblyIdentity/dependency/file/comClass, la necesidad de que elassemblyIdentitydel lado REF coincida con el del lado DEF, el tratamiento de mayúsculas y minúsculas en los nombres, y los atributos del tipomiscStatus. ↩ ↩2
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
Qué son COM, ActiveX y OCX - Diferencias y relación explicadas
Explicamos qué son COM, ActiveX y OCX: diferencias, relación, vínculo con OLE, dónde se usan y cómo abordarlos hoy desde una perspectiva ...
ActiveX / OCX: cómo tratarlos hoy — tabla de decisión para mantener, envolver o reemplazar
Al encontrar un ActiveX / OCX, cómo decidir entre mantenerlo, envolverlo o reemplazarlo, considerando 32 bits / 64 bits, registro, depend...
Qué es COM — por qué el diseño de Windows COM sigue siendo hermoso hoy
Explica qué es COM: el diseño de interfaces de Windows COM, IUnknown, GUID y la compatibilidad binaria, y por qué sigue vigente hoy.
¿Qué es un objeto OLE? — Incrustación, vinculación y las trampas de los documentos empresariales
La función que incrusta una tabla de Excel en Word es, en realidad, un objeto OLE. El artículo explica en clave práctica la diferencia en...
Cómo funcionan el portapapeles y arrastrar y soltar — tratar correctamente la transferencia de datos OLE en aplicaciones empresariales
Una tabla de Excel se deshace al pegarla y deja de pegarse al cerrar el origen: el portapapeles coloca el mismo contenido en varios forma...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Migración de ActiveX
Decisiones para conservar, encapsular o sustituir componentes COM / ActiveX / OCX.
Servicios relacionados con este tema
El artículo está directamente relacionado con los siguientes servicios.
Reutilización y migración de activos existentes
Reutilización y migración de activos COM / ActiveX / OCX y dependencias de 32 o 64 bits.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿Cómo se investiga el error 0x80040154 (Class not registered)?
- Oficialmente es REGDB_E_CLASSNOTREG, literalmente un error de «clase no registrada», pero eso no siempre significa que esté completamente sin registrar. También ocurre cuando solo está registrado en la vista de registro del otro bitness, o solo en el lado HKCU, visible únicamente para ese usuario. El camino más rápido es fijar primero si el proceso host es de 32 o 64 bits, después comprobar si el medio de registro es el correcto, y por último verificar en qué combinación de HKCU / HKLM y vista de 32 / 64 bits quedó registrado.
- ¿Por qué no funciona aunque se registre con regsvr32?
- regsvr32 está pensado para servidores COM in-proc nativos (DLL / OCX) que exportan DllRegisterServer, y no es un comando mágico que registre cualquier cosa. La exposición COM de un ensamblado de .NET Framework se hace con Regasm.exe, y en .NET 5 en adelante el flujo consiste en registrar con regsvr32 el .comhost.dll generado mediante EnableComHosting. Además, en Windows x64 hay que usar el regsvr32 de System32 para el lado de 64 bits y el de SysWOW64 para el de 32 bits; si se registra en el lado equivocado, el registro tiene éxito pero queda invisible para el proceso objetivo.
- ¿Por qué en Visual Studio 2022 solo falla el Diseñador?
- Porque devenv.exe, en Visual Studio 2022, es un proceso de 64 bits y no puede cargar directamente componentes COM / ActiveX de 32 bits. Aunque la aplicación en tiempo de ejecución pueda funcionar en x86, el Diseñador se ejecuta en el proceso de 64 bits de Visual Studio, lo que produce la contradicción de que la ejecución sigue viva pero solo el Diseñador falla. Aunque se cambie a AnyCPU, el problema persiste si, más adelante en la cadena, se depende de un COM / ActiveX fijado a 32 bits.
- ¿Por qué funciona al ejecutarlo como administrador pero no con permisos normales?
- En el fondo, esto suele ser un desajuste en el ámbito de registro o en el diseño de la instalación, más que un problema de permisos en sí. COM mira primero HKCU\Software\Classes, y HKEY_CLASSES_ROOT es la vista combinada (merged view) de HKLM y HKCU. Cuando se registra manualmente con el usuario del desarrollador A, es normal que funcione con la cuenta de A pero no con otros usuarios ni con cuentas de servicio. Lo básico es distinguir entre el registro per-user de desarrollo y el registro per-machine de producción que hace el instalador, y separar la compilación del registro.
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.