2009-10-07 17 views
17

Estamos obligados a utilizar un control ActiveX de terceros.Control ActiveX sin formulario

El único problema es que la capa de nuestro software es una capa empresarial y no tiene acceso a una ventana o formulario. También se ejecuta en hilos separados (y debería funcionar desde cualquier hilo) que no sean STA.

En lugar de romper nuestra separación de la interfaz de usuario de la lógica de negocio, que utilizan esta solución para hacer que funcione:

Thread thread = new Thread((ThreadStart) 
delegate 
{ 
_myActiveX = new MyActiveXType(); 
_myActiveX.CreateControl(); 

//more initialize work 

Application.Run(); 
}); 
thread.SetApartmentState(ApartmentState.STA); 
thread.IsBackground = true; 
thread.Start(); 

Entonces cada vez que necesitemos para hacer referencia al control, nos llaman _myActiveX.BeginInvoke() o Invoke().

Al desechar esta clase (al salir de nuestra aplicación), desechamos el control y abortamos el hilo.

Mi pregunta es, ¿hay algún problema con esto? ¿Hay una mejor manera de manejar esto?

¿Existe una mejor manera de trabajar con un control ActiveX desde un entorno desconocido de subprocesos múltiples? Estamos tratando de escribir nuestra clase de una manera que envuelva el control, pero funcionará desde cualquier hilo.

ACTUALIZACIÓN: Como respuesta sugerida, preferiríamos usar el objeto COM estándar y no usar ningún control. Nuestro problema con eso era que obtendríamos el error "(Excepción de HRESULT: 0x8000FFFF (E_UNEXPECTED)" sobre el primer método o propiedad que llamamos al objeto COM. Este es un error bastante genérico que no obtenemos al usar el ActiveX , cualquier idea?

ACTUALIZACIÓN: Nuestro ocx es "CX25.ocx", utilizando tlbimp.exe obtenemos CX25Lib.dll. Con aximp.exe, obtenemos AxCX25Lib.dll y CX25Lib.dll. CX25Lib.dll no funciona en ambos casos. AxCX25Lib.dll funciona.

+0

El código anterior que menciono está funcionando. ¿Alguien ha tenido que usar un control ActiveX sin un formulario? ¿O alguien ha tenido problemas al usar el objeto COM estándar, pero notó que ActiveX funciona? – jonathanpeppers

+0

¿Encontró una solución para esto? Sospecho que el enfoque correcto depende de si espera o no devoluciones de llamada/eventos del control, ya que dependería de una bomba de mensajes de Windows para manejarlos. – Tormod

+1

Sí, el código anterior funciona. Lo hemos estado utilizando durante más de un año sin problemas. También lo usamos con múltiples bibliotecas COM. Terminamos envolviéndolo en una clase e implementamos Invoke/BeginInvoke para interactuar con el control, que es obligatorio. – jonathanpeppers

Respuesta

16

Supongo que esta es la forma correcta de hacerlo.

Hemos estado utilizando mi código anterior en entornos de prueba durante las últimas semanas sin problemas.

Si alguien tiene que usar un ActiveX sin un formulario, supongo que esta es una forma de hacerlo.

Solo asegúrese de llamar a _yourActiveXControl.CreateControl() directamente después del constructor de su objeto ActiveX. Esto simplificó muchos problemas que teníamos originalmente.

+0

Gracias por publicar esta información , parece que sería realmente útil para algunas personas.Solo un punto de datos, tengo un control ActiveX que no funcionó. Tuve que poner la cosa en un formulario que ejecuto en el hilo separado (pero nunca tuve que mostrar el formulario al menos). – user12861

+1

Gracias, una nota sobre esto a otras personas, si está comenzando un formulario desde otro formulario/ventana, debe hacer esto para que el control x activo funcione correctamente. El cliente X RDP activo no abriría el cuadro para redirigir las unidades, etc. en mi caso, por lo que cerraría el formulario. Poner 'CreateControl()' después de 'BeginInit()' en el control solucionó esto. No necesita hacer esto si tiene el formulario principal con este incrustado. – uNople

1

Si está llamando al control ActiveX desde una capa empresarial, significa que debe poder utilizarse sin una interfaz de usuario, por ejemplo, simplemente llamando a sus métodos públicos. ¿Por qué no solo? crear un RCW de interoperabilidad para la clase de control ActiveX y llamar directamente a sus métodos?

+0

¿Estás hablando de ejecutar tlbimp en el control .ocx? Lo intentamos y no funcionó. Obtendríamos "Fallo catastrófico (excepción de HRESULT: 0x8000FFFF (E_UNEXPECTED))" cada vez que lo intentáramos desde un objeto COM estándar. El control ActiveX funciona por alguna razón. – jonathanpeppers

+0

¿Has probado 'aximp.exe' en lugar de' tlbimp.exe'? Crea dos ensamblajes: uno normal RCW y uno WinForms proxy uno. Es posible que pueda usar el ensamblaje RCW solo en el código. Descargo de responsabilidad - No he intentado esto, solo adivinando basado en mi experiencia 'tlbimp.exe'. –

+0

Ver mi edición anterior. – jonathanpeppers

1

Mi solución es crear un WinForm oculto que aloja el control ActiveX

+0

Mi solución parece mucho más ligera. ¿Qué ganas usando una forma fuera de pantalla? – jonathanpeppers

+0

Tengo un programa de terceros que expone un control activex, no es una forma fácil de controlar el programa, me vuelvo al control activex. en realidad, probé tu solución, parece que también funciona. así que tomaré su solución. – Benny

+0

Esta fue la única solución para un control que estaba usando, así que gracias por publicarlo. – user12861

1

Sé que esto es una entrada antigua, pero yo recomendaría usar el TPL en nuestra era moderna.

Es mejor usar la biblioteca paralela de tarea en lugar de la antigua API de subprocesamiento debido a las características relacionadas con el manejo de excepciones, la cancelación, la continuación y la devolución de resultados.

He aquí un ejemplo:

using (var sta = new StaTaskScheduler(1)) 
{  
    var taskResult = await Task.Factory.StartNew(() => 
    { 
     var results = new List<ResultType>(); 

     using (var ax = new MyActiveXType()) 
     { 
      // important to call this just after constructing ActiveX type 
      ax.CreateControl(); 

      ax.SomeIterativeEvent += (s, e) => results.Add(e.SomeThing); 

      // if applicable, you can tear down the message pump 
      ax.SomeFinalEvent += (s, e) => Application.ExitThread(); 

      //more initialize work 

      // start message pump 
      Application.Run(); 

      return results; 
     } 
    }, CancellationToken.None, TaskCreationOptions.None, sta); 

    return taskResult; 
} 

Algunos puntos:

  1. StaTaskScheduler es un tipo que se encuentra en el ParallelExtensionsExtras nuget package. Lo necesitará para programar las tareas que se ejecutarán en un apartamento Single Threaded.

  2. Estoy pasando 1 al constructor de StaTaskScheduler para que solo me cree un solo hilo.

  3. Application.ExitThread() se llama para detener la bomba de mensajes, que a su vez permite que la ejecución pase por Application.Run() para que se pueda devolver algún resultado a la persona que llama.

Cuestiones relacionadas