gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 09:08:56 29 4
gpt4 key购买 nike

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

  • 当从原始线程触发事件时,VB6 事件以 regfree 模式运行。
  • 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) list 缺少与 tlb 注册相关的内容,或者 2) 创建新线程时激活上下文丢失。不幸的是,我不知道如何找出是哪种情况,甚至可能是由其他原因引起的。

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

list (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>

list (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 元素提供。我已经尝试过这些,但直到现在才找到合适的组合。

list 更改(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。

Excerpt from "Registration-Free Activation of COM Components: A Walkthrough" http://msdn.microsoft.com/en-us/library/ms973913.aspx

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.

关于c# - Regfree COM 事件从其他线程失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23341197/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com