multihilos hilos c# .net com vb6 regfreecom

c# - hilos - El evento COM sin registro falla de otro hilo



hilos en c# windows forms (1)

Con multi-threading las llamadas tienen que ser ordenadas. Esto requiere información adicional, que es proporcionada por el elemento comInterfaceExternalProxyStub y comInterfaceExternalProxyStub . Había experimentado con esos, pero no encontré la combinación correcta hasta ahora.

Cambios de manifiesto (C # DLL)

<file name="Example.Vb6RegFreeCom.dll" hashalg="SHA1"> <typelib tlbid="{FABD4158-AFDB-4223-BB09-AB8B45E3816E}" version="1.0" flags="hasdiskimage" helpdir="" /> </file> <comInterfaceExternalProxyStub name="IExampleClassEvents" iid="{2669EBDB-16D9-45C8-B0A3-ED2CEE26862C}" tlbid="{FABD4158-AFDB-4223-BB09-AB8B45E3816E}" proxyStubClsid32="{00020420-0000-0000-C000-000000000046}"> </comInterfaceExternalProxyStub> <comInterfaceExternalProxyStub name="IExampleClass" iid="{467EB602-B7C4-4752-824A-B1BC164C7962}" tlbid="{FABD4158-AFDB-4223-BB09-AB8B45E3816E}" proxyStubClsid32="{00020420-0000-0000-C000-000000000046}"> </comInterfaceExternalProxyStub>

Una vez que estaba en el camino correcto, encontré varios indicadores en la dirección correcta. La mejor descripción que encontré es a continuación. En mi ejemplo, también se utilizó IDispatch.

Extracto de "Activación sin registro de componentes COM: un tutorial" http://msdn.microsoft.com/en-us/library/ms973913.aspx

Estos elementos proporcionan información que, de lo contrario, estaría presente en el registro. El elemento comInterfaceExternalProxyStub proporciona suficiente información para que se produzca la clasificación de biblioteca de tipos y es apropiada para las interfaces COM que se derivan de IDispatch (que incluye todas las interfaces de Automatización). En estos casos, ole32.dll proporciona el proxy proxy externo utilizado (es decir, externo a los archivos en el ensamblado). Si sus componentes COM implementan solamente el envío o las interfaces duales, este es el elemento que debe usar.

Tengo una clase COM .NET visible que expone eventos y se usa desde VB6. Durante los últimos días, he estado tratando de hacer que esto funcione con regfree COM, pero sin éxito.

  • El evento VB6 se ejecuta en modo regfree cuando el evento se dispara desde el hilo original.
  • El evento VB6 se ejecuta cuando se dispara desde otro subproceso cuando se escribe el typelib. (regasm / tlb / codebase seguido de regasm / codebase / unregister, este último no anula el registro del tlb)

Al disparar desde otro subproceso en modo regfree arroja una excepción, por lo que el código de evento VB6 nunca se ejecuta.

System.Reflection.TargetException: Object does not match target type. at System.RuntimeType.InvokeDispMethod(String name, BindingFlags invokeAttr, Object target, Object[] args, Boolean[] byrefModifiers, Int32 culture, String[] namedParameters) at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams) at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData) at Example.Vb6RegFreeCom.IExampleClassEvents.TestEvent() at Example.Vb6RegFreeCom.ExampleClass.OnTestEvent(Action func) in ExampleClass.cs:line 78

Hay dos escenarios en los que puedo pensar: 1) al manifiesto falta algo relacionado con el registro tlb, o 2) el contexto de activación se pierde al crear el nuevo hilo. Lamentablemente, no sé cómo averiguar cuál es el caso, o tal vez incluso sea causado por otra cosa.

A continuación se muestra un ejemplo básico que muestra mi problema.

Manifiesto (ejecutable VB6)

<?xml version="1.0" encoding="utf-8"?> <assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <assemblyIdentity name="VB6COM" version="1.0.0.0" type="win32" /> <dependency xmlns="urn:schemas-microsoft-com:asm.v2"> <dependentAssembly codebase="Example.Vb6RegFreeCom.tlb"> <assemblyIdentity name="Example.Vb6RegFreeCom" version="1.0.0.0" publicKeyToken="B5630FCEE39CF455" language="neutral" processorArchitecture="x86" /> </dependentAssembly> </dependency> </assembly>

Manifiesto (C # DLL)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity name="Example.Vb6RegFreeCom" version="1.0.0.0" publicKeyToken="b5630fcee39cf455" processorArchitecture="x86"></assemblyIdentity> <clrClass clsid="{8D51802D-0DAE-40F2-8559-7BF63C92E261}" progid="Example.Vb6RegFreeCom.ExampleClass" threadingModel="Both" name="Example.Vb6RegFreeCom.ExampleClass" runtimeVersion="v4.0.30319"></clrClass> <file name="Example.Vb6RegFreeCom.dll" hashalg="SHA1"></file> <!-- <file name="Example.Vb6RegFreeCom.TLB"> <typelib tlbid="{FABD4158-AFDB-4223-BB09-AB8B45E3816E}" version="1.0" flags="" helpdir="" /> </file> --> </assembly>

C # (objetivo de la plataforma: x86)

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; using Timer = System.Threading.Timer; using FormsTimer = System.Windows.Forms.Timer; namespace Example.Vb6RegFreeCom { [ComVisible(true)] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] [Guid("467EB602-B7C4-4752-824A-B1BC164C7962")] public interface IExampleClass { [DispId(1)] int Test(int mode); } [ComVisible(true)] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] [Guid("2669EBDB-16D9-45C8-B0A3-ED2CEE26862C")] public interface IExampleClassEvents { [DispId(1)] void TestEvent(); } [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] [ComSourceInterfaces(typeof(IExampleClassEvents))] [Guid("8D51802D-0DAE-40F2-8559-7BF63C92E261")] public class ExampleClass: IExampleClass { public event Action TestEvent; public int Test(int mode) { var tempEvent = TestEvent; if (tempEvent == null) return -1; switch (mode) { case 0: tempEvent(); break; case 1: var staThread = new Thread(() => OnTestEvent(tempEvent) ); //if (!staThread.TrySetApartmentState(ApartmentState.STA)) MessageBox.Show("Failed to set STA thread."); staThread.Start(); break; case 2: var invoker = new Invoker(); var otherThread = new Thread(() => invoker.Invoke((Action)(() => OnTestEvent(tempEvent)))); otherThread.Start(); break; case 3: var timer = new FormsTimer(); timer.Tick += (_1, _2) => { timer.Dispose(); OnTestEvent(tempEvent); }; timer.Interval = 100; timer.Start(); break; default: return -2; } return 1; } internal static void OnTestEvent(Action func) { try { func(); } catch (Exception err) { MessageBox.Show(err.ToString()); } } } internal class Invoker : Control { internal Invoker() { this.CreateHandle(); } } }

VB6

Option Explicit Dim WithEvents DotNetObject As ExampleClass Private Sub cmdImmediate_Click() CallDotNet 0 End Sub Private Sub cmdOtherThread_Click() CallDotNet 1 End Sub Private Sub cmdSameThread_Click() CallDotNet 2 End Sub Private Sub Form_Load() Set DotNetObject = New ExampleClass End Sub Private Sub CallDotNet(TestMode As Long) Dim ReturnValue As Long ReturnValue = DotNetObject.Test(TestMode) If ReturnValue <> 1 Then MsgBox "Return value is " & ReturnValue End Sub Private Sub DotNetObject_TestEvent() MsgBox "Event was raised." End Sub