com - GetObject y VB6 ActiveX exe
(2)
La ayuda de VB6 en GetObject dice "No se puede usar GetObject para obtener una referencia a una clase creada con Visual Basic" (¡la última frase!). Mi GUI VB6 expone objetos como un exe ActiveX, para que otros componentes los manipulen. Quiero que los otros componentes se conecten a la GUI que ya se está ejecutando, en lugar de iniciar una nueva instancia del exe. He encontrado que el uso de GetObject funciona, si usa esta sintaxis:
Set myobj = GetObject("", "ProjectName.ClassName")
Me preocupa que la ayuda diga que esto no debería funcionar, aunque he hecho muchas pruebas y hasta ahora no he encontrado ningún problema. ¿Algún experto COM por ahí que pueda decirme si voy a tener problemas en el futuro? ¿Y estaría bien con CreateObject de todos modos?
La configuración del exe de ActiveX es: grupo de subprocesos con solo un subproceso. La clase tiene instancias MultiUse. Es posible que estas configuraciones sean suficientes para evitar que CreateObject inicie una nueva instancia del exe de todos modos. ¿Es eso correcto?
La documentación es confusa, pero correcta. La página MSDN a la que hace referencia ayuda a explicar por qué su llamada a GetObject
no arroja un error:
Si pathname [ el primer argumento ] es una cadena de longitud cero (""), GetObject devuelve una nueva instancia de objeto del tipo especificado. Si se omite el argumento de la ruta de acceso, GetObject devuelve un objeto actualmente activo del tipo especificado. Si no existe ningún objeto del tipo especificado, se produce un error.
Es sutil, pero la implicación es que
GetObject "", "ProjectName.ClassName
es en realidad equivalente a
CreateObject "ProjectName.ClassName"
Es decir, pasar una cadena vacía al primer parámetro de GetObject
hace que funcione exactamente como CreateObject
, lo que significa que creará una nueva instancia de la clase, en lugar de devolver una referencia a una instancia que ya se está ejecutando.
Volviendo al extracto de MSDN, menciona que al omitir por completo el primer argumento de GetObject
, GetObject
devolverá una referencia a una instancia en ejecución, si existe. Tal llamada se vería así:
GetObject , "ProjectName.ClassName" ''Note nothing at all is passed for the first argument''
Sin embargo, si intenta hacer esto, obtendrá inmediatamente un error en tiempo de ejecución. Este es el caso de uso al que hace referencia la documentación cuando dice que GetObject
no funciona con las clases creadas con VB6.
La razón por la que esto no funciona se debe a la forma en que GetObject
realiza su magia. Cuando se omite el primer parámetro, intenta devolver una instancia de objeto existente consultando la Tabla de objetos en ejecución (ROT), una tabla de búsqueda en toda la máquina que contiene objetos COM en ejecución. El problema es que los objetos deben registrarse explícitamente en la Tabla de Objetos en ejecución por el proceso que los crea para poder acceder a otros procesos: el tiempo de ejecución de VB6 no registra las clases EXE de ActiveX en el ROT, por lo que GetObject
no tiene forma de recuperar una referencia a una instancia que ya se está ejecutando.
Quiero que los otros componentes se conecten a la GUI que ya se está ejecutando, en lugar de iniciar una nueva instancia del exe.
El truco consiste en recordar que en un EXE de ActiveX se puede configurar para que solo haya una instancia de la BIBLIOTECA ejecutándose. Es correcto que no puede alcanzar y simplemente arrancar una instancia determinada de una clase a través del límite del proceso. Sin embargo, el EXE de ActiveX se puede configurar para que cualquier instancia de clases pueda acceder a las variables de GLOBAL.
Cómo hacer exactamente esto se vuelve un poco complejo. Puede usar un EXE de ActiveX como un EXE normal, la diferencia principal es que TIENE que usar Sub Main. También puede verificar si funciona de manera independiente o no. Ahora estoy asumiendo que este es el caso con la aplicación de MarkJ.
Si este es el caso, lo que necesita es crear una clase de aplicación y configurarla para que cuando se cree (utilizando Class_Initialize) se rellene con las instancias actuales de formularios y colecciones.
Recomiendo encarecidamente que cree una DLL ActiveX (no EXE) que no tenga más que clases para implementar como interfaces. En lugar de ir de ir
''Class ThisGUIApp
Public MainForm as Form
Usted crea una interfaz que tiene todas las propiedades y métodos necesarios para acceder a los elementos de la forma principal. Entonces vas
''Class ThisGUIApp
Public MainForm as IMainForm
Private Sub Class_Initialize
Set MainForm = frmMyMainForm
End Sub
''Form frmMyMainForm
Implements IMainForm
Esto se hace porque si bien puede enviar un Formulario a través del proceso de solicitud, las cosas se ponen feas cuando intenta acceder a sus miembros y controles. Si asignó a través de una interfaz, la conexión es mucho más sólida. Además, documenta mejor el tipo de cosas que intenta hacer.