C# Regfree COM事件从其他线程失败

C# Regfree COM事件从其他线程失败,c#,.net,com,vb6,regfreecom,C#,.net,Com,Vb6,Regfreecom,我有一个COM visible.NET类,它公开事件并从VB6使用。在过去的几天里,我一直在尝试让它与regfree COM一起工作,但没有成功 从原始线程触发事件时,VB6事件以regfree模式运行 注册typelib时,从另一个线程触发VB6事件时运行。(regasm/tlb/codebase后跟regasm/codebase/unregister,后者不注销tlb) 在regfree模式下从另一个线程触发时,它会抛出一个异常,因此VB6事件代码永远不会执行 System.Reflec

我有一个COM visible.NET类,它公开事件并从VB6使用。在过去的几天里,我一直在尝试让它与regfree COM一起工作,但没有成功

  • 从原始线程触发事件时,VB6事件以regfree模式运行
  • 注册typelib时,从另一个线程触发VB6事件时运行。(regasm/tlb/codebase后跟regasm/codebase/unregister,后者不注销tlb)
在regfree模式下从另一个线程触发时,它会抛出一个异常,因此VB6事件代码永远不会执行

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
我可以想到两种情况:1)清单缺少与tlb注册相关的内容,或2)创建新线程时激活上下文丢失。不幸的是,我不知道如何找出是哪种情况,甚至可能是由其他原因造成的

下面是一个显示我的问题的基本示例

清单(VB6可执行文件)

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
选项显式
Dim以Events DotNetObject作为示例类
私有子CMDU单击()
calldotnet0
端接头
私有子cmdOtherThread_Click()
CallDotNet 1
端接头
私有子cmdSameThread_Click()
CallDotNet 2
端接头
专用子表单_加载()
Set DotNetObject=New ExampleClass
端接头
专用子CallDotNet(测试模式长度相同)
将返回值变长
ReturnValue=DotNetObject.Test(TestMode)
如果返回值为1,则MsgBox“返回值为”&返回值
端接头
私有子DotNetObject_TestEvent()
MsgBox“事件已引发。”
端接头

使用多线程时,必须封送调用。这需要额外的信息,这些信息由
comInterfaceExternalProxyStub
typelib
元素提供。我曾经尝试过这些,但直到现在才找到正确的组合

清单更改(C#DLL)


一旦我在正确的轨道上,我发现了几个指向正确方向的指针。下面是我遇到的最好的描述。在我的示例中,还使用了IDispatch

摘自“COM组件的免注册激活:演练”

这些要素提供了本应在本手册中提供的信息 登记处。comInterfaceExternalProxyStub元素提供 足够的信息进行类型库封送,并且 适用于从IDispatch派生的COM接口(其中 包括所有自动化接口)。在这些情况下,ole32.dll提供 使用的外部代理存根(即,位于 装配)。如果您的COM组件仅实现分派或双重 接口那么这就是您应该使用的元素


当tlb注册时,它似乎起作用。您希望在
DotNetObject\u TestEvent
中的
MsgBox
哪个线程上执行?(必须在STA上创建VB6对象——从其他单元调用将涉及封送,这就是STA的全部要点)@wqw:澄清一下,我的问题是,在regfree模式下,它会引发异常,因此无法访问messagebox。与使用winforms的快速修复相比,我更喜欢自动编组。我将更新这个问题。类型库是否可能仅仅是为了在线程之间进行编组而需要的?我似乎记得,如果您通过DCOM使用组件,则需要将类型库与客户端一起分发,否则DCOM层将不知道如何进行编组。@Herman:似乎可以。在
Form_Load
中,您在主线程(
Set DotNetObject=New ExampleClass
)上创建C对象,因此不使用封送处理,也不能从单独的线程引发事件。您必须在一个单独的线程上创建coclass(新的ExampleClass),封送接口,然后将其接收。@wqw:如果tlb注册到现在,VB6事件实际上是一致执行的。“似乎有效”是指这是一种幸运的情况,但这是一种不正确的方法吗?我将尝试在另一个线程上创建对象。
IExampleClassEvents
dual interface还是dispinterface?VB6只能接收源DISP接口。请注意,对于具有不同CLSID的dual和dispinterface,存在不同的代理/存根:对于dual,PSOAInterface={00020424-0000-0000-C000-0000000000 46};对于dispinterface,PSDispatch={00020420-0000-0000-C000-0000000000 46}。您的示例正在使用第二个示例,因此VB6只能使用后期绑定IDispatch调用进行封送,以便在应用程序中工作。(最好在
IExampleClass
中使用psoanterface)接口是IDispatch(如我的问题示例中所示)。Regasm还在注册表中使用此CLSID。这个界面是很久以前制作的,我不记得为什么我选择了IDispatch。也许InteropForms工具包就是这样做的,我以它为例。我的接口非常笨重,所以性能不是一个真正的问题,使用dual/iunknown还有其他好处吗?不,封送器已经给您带来了足够的开销:-)Internet上充满了用于regfree封送的代理/存根示例(包括CLSID),但没有明确说明何时使用哪一个。目前还不清楚是否有两个内置代理,guid中也有细微的差别(只有一位数)。这些代理既不兼容也不可交换,即不能在DispInterface上使用PSOAInterface——最终会出现运行时错误。我把这些信息发布在这里是为了子孙后代。是的,这是一个很好的观点,当我在寻找CLSID指向哪个接口时,我不得不再看一眼。我的建议是regasm/tlb,然后从注册表中获取值。@herman:iDispatch是COM的标准双接口(意思是iUnknown和iDispatch)。VB6 COM需要一个双接口,因为如果只使用iUnknown,就不能支持l
<?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>
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();
        }
    }
}
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
  <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>