【问题标题】:Pass VB6 object to .NET object via interop?通过互操作将 VB6 对象传递给 .NET 对象?
【发布时间】:2011-12-19 14:54:11
【问题描述】:

我有一个通过互操作显示 .NET DLL 表单的 VB6 应用程序。

我想要 .NET DLL 中的一个事件来显示 VB6 应用程序中的一个表单。

我的想法是让 VB6 应用程序将对表单的引用传递给 .NET DLL。例如:

[VB6]
Dim objNetDllObject As New NetDllObject
objNetDllObject.PassVb6Form(MyForm)
objNetDllObject.ShowForm

[C#]
object Vb6Form; 
private void PassVb6Form(object form) { Vb6Form = form; }
private void button1_Click(object sender, EventArgs e) { Vb6Form.Show(); }

这行得通吗?

我在其他地方读到,跨“进程边界”发送对象可能会导致问题。这是正确的吗?

【问题讨论】:

  • 这不是一个进程边界...它是同一个进程。是的,这很痛苦,但它可以工作。

标签: c# .net vb6 com-interop


【解决方案1】:

一种方法是在 .NET 中定义 COM 接口:

<System.Runtime.InteropServices.GuidAttribute("0896D946-8A8B-4E7D-9D0D-BB29A52B5D08"), _
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface IEventHandler
    Sub OnEvent(ByRef sender As Object, ByRef e As Object)
End Interface

在VB6中实现这个接口

Implements MyInterop.IEventHandler

Private Sub IEventHandler_OnEvent(ByRef sender As Variant, ByRef e As Variant)
    Dim id
    id = e.Entity.Id
    ' As long as e is COM Visible (not necessarily COM registered, this will work)
End Sub

然后在 .NET 中拥有一个带有静态 IEventHandler 集合的注册器:

<ComClass(ComRegistrar.ClassId, ComRegistrar.InterfaceId, ComRegistrar.EventsId>
Public Class ComRegistrar

   Private Shared ReadOnly _eventHandlers As New Dictionary(Of String, List(Of IEventHandler))


   ' This is called by .NET code to fire events to VB6
   Public Shared Sub FireEvent(ByVal eventName As String, ByVal sender As Object, ByVal e As Object)
        For Each eventHandler In _eventHandlers(eventName)
                eventHandler.OnEvent(sender, e)
        Next
   End Sub

   Public Sub RegisterHandler(ByVal eventName As String, ByVal handler As IEventHandler)
        Dim handlers as List(Of IEventHandler)
        If Not _eventHandlers.TryGetValue(eventName, handlers)
             handlers = New List(Of IEventHandler)
             _eventHandlers(eventName) = handlers
        End If
        handlers.Add(handler)
   End Sub

End Class

您的 .NET 代码将调用 FireEvent,如果 VB6 之前调用了 RegisterHandler,则将调用您的 VB6 IEventHandler。

【讨论】:

  • 我的方法不是更简单吗?我试图避免所有那些事件处理的东西。
  • 是的,更简单,但您需要使用后期绑定来进行回调。
  • 这种情况下后期绑定有什么风险?
  • 和往常一样:) ...方法不存在。你没有正确调用它。签名更改...等
  • 签名会如何变化?
【解决方案2】:

您还可以使用ComSourceInterfacesAttribute,它可以使您的代码更简单一些(或者至少从VB6 方面来看更自然)。你会有一些类似下面的代码:

C# 库:

namespace WindowsFormsControlLibrary1
{
    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;

    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IMyFormEvents
    {
        [DispId(1)]
        void MyEvent();
    }

    public delegate void MyEventEventHandler();

    [ComSourceInterfaces(typeof(IMyFormEvents))]
    public partial class MyForm : Form
    {
        public event MyEventEventHandler MyEvent;

        public MyForm()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (this.MyEvent != null)
            {
                this.MyEvent();
            }
        }
    }
}

还有 VB6 客户端:

Option Explicit

Private WithEvents f As WindowsFormsControlLibrary1.MyForm

Private Sub Command1_Click()
    Set f = New WindowsFormsControlLibrary1.MyForm
    Call f.Show
End Sub

Private Sub f_MyEvent()
    MsgBox "Event was raised from .NET"
    'Open another VB6 form or do whatever is needed
End Sub

如果您要在 VB6 应用程序中使用大量 .NET UI,我强烈推荐 Interop Forms Toolkit 以使事情变得更简单。 (它有助于生成类似这些示例的代码,以及处理其他一些常见场景)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-16
    • 1970-01-01
    • 1970-01-01
    • 2017-04-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多