Regfree COM 事件从其他线程失败 [英] Regfree COM event fails from other thread

查看:13
本文介绍了Regfree COM 事件从其他线程失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a COM visible .NET class which exposes events and is used from VB6. For the last couple of days I have been trying to get this to work with regfree COM, but without success.

  • 当事件从原始线程中触发时,VB6 事件会在 regfree 模式下运行.
  • 在注册类型库时从另一个线程触发 VB6 事件.(regasm/tlb/codebase 后跟 regasm/codebase/unregister,后者不会注销 tlb)

在 regfree 模式下从另一个线程触发时,它会引发异常,因此永远不会执行 VB6 事件代码.

When firing from another thread in regfree mode it throws an exception, thus the VB6 event code is never executed.

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)在创建新线程时丢失了激活上下文.不幸的是,我不知道如何找出是哪种情况,或者甚至可能是由其他原因引起的.

There are two scenarios I can think of: 1) the manifest is missing something related to the tlb registration, or 2) the activation context is lost when creating the new thread. Unfortunately, I don't know how to find out which is the case, or maybe it is even caused by something else.

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

Below is a basic example showing my problem.

清单(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>

清单(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#(平台目标: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

推荐答案

使用多线程必须对调用进行编组.这需要额外的信息,这些信息由 comInterfaceExternalProxyStubtypelib 元素提供.我曾尝试过这些,但直到现在才找到合适的组合.

With multi-threading the calls have to be marshalled. This requires extra information, which is provided by the comInterfaceExternalProxyStub and typelib element. I had experimented with those, but did not find the right combination until now.

清单更改(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>

一旦我走上了正确的轨道,我就发现了几个指向正确方向的指针.我遇到的最好的描述如下.在我的示例中,也使用了 IDispatch.

Once I was on the right track I found several pointers into the right direction. The best description I came across is below. In my example also IDispatch was used.

摘自免注册激活 COM 组件:演练"http://msdn.microsoft.com/en-us/library/ms973913.aspx

这些元素提供了原本会出现在注册表.comInterfaceExternalProxyStub 元素提供发生类型库编组的足够信息,它是适用于从 IDispatch 派生的 COM 接口(其中包括所有自动化接口).在这些情况下,ole32.dll 提供使用的外部代理存根(即,在部件).如果你的 COM 组件只实现 dispatch 或 dual接口,那么这是你应该使用的元素.

These elements provide information that would otherwise be present in the registry. The comInterfaceExternalProxyStub element provides enough information for type library marshalling to occur and it is appropriate for COM interfaces that derive from IDispatch (which includes all Automation interfaces). In these cases ole32.dll provides the external proxy-stub used (i.e., external to the files in the assembly). If your COM components implement only dispatch or dual interfaces then this is the element you should use.

这篇关于Regfree COM 事件从其他线程失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆