如何制作假的VBA.Collection

时间:2014-11-05 20:24:00

标签: .net com

我已经通过与VB6互操作这么多(不必要的)痛苦,甚至不好笑,所以我完成了尝试按VB6规则玩,我已经完成了寻找旧的VB6 COM dlls。只是厌倦了它。

不幸的是,我不得不来回与VB6交谈。这需要来回传递VBA.Collection。

我已经为vb.net修改了一个实现,并且必须修复添加的密钥,因为它在丢失时为空。

Public Class VBACollection
  Implements VBA._Collection

  Private _items As New Dictionary(Of Object, Object)

  Public Sub Add(ByRef Item As Object, Optional ByRef Key As Object = Nothing, Optional ByRef Before As Object = Nothing, Optional ByRef After As Object = Nothing) Implements VBA._Collection.Add
    ' Ignoring the Before and After params for simplicity
    Key = If(Key, Item)
    _items.Add(Key, Item)
  End Sub

  Public Function Count() As Integer Implements VBA._Collection.Count
    Return _items.Count
  End Function

  Public Function GetEnumerator() As System.Collections.IEnumerator Implements VBA._Collection.GetEnumerator, System.Collections.IEnumerable.GetEnumerator
    Return _items.Values.GetEnumerator()
  End Function

  Public Function Item(ByRef Index As Object) As Object Implements VBA._Collection.Item
    Return _items(Index)
  End Function

  Public Sub Remove(ByRef Index As Object) Implements VBA._Collection.Remove
    _items.Remove(Index)
  End Sub
End Class

但是,这不适用于VB6。 VB6说:

  

" Class不支持自动化或不支持预期   接口"

这里讨论的类是我的类,它使用VBACollection而不是VBA.Collection。 VBACollection与VBA.Collection不是一个相同的替身。我想找出原因并尝试假装COM接受它。

如果我能成功完成此操作,我将不再需要处理VBA.dll或VBA.Interop.dll以及无处不在的构建时错误,告诉我在编译时无法编译昨天工作等等。

我想让VB6客户端相信我拥有真正的vba集合。我不在乎它注册的地方;我不需要我的注册,但我想我需要同样的指导。

除非另一篇文章说明如何摆脱在项目中引用VBA.dll,否则这不是重复的。我只想在那个必须引用VBA._Collection的项目中找到它。如果有一种方法可以摆脱这种情况,那就更好了。

2 个答案:

答案 0 :(得分:1)

嗯,你应该通过实现VBA._Collection接口进入大球场。错误消息表明您忘记了<ComVisible>。确保使用Project + Add Reference,Browse选项卡添加正确的引用,选择c:\ windows \ syswow64 \ msvbvm60.dll

实现它的最佳方法是使用VB.NET Collection类,它最接近地再现了VBA Collection的非常不寻常的行为:

Imports System.Runtime.InteropServices

<ComVisible(True)> _
<ClassInterface(ClassInterfaceType.None)> _
Public Class CollectionImpl
    Implements VBA.Collection
    Private impl As New Collection

    Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return impl.GetEnumerator()
    End Function

    Public Sub Add(ByRef Item As Object, Optional ByRef Key As Object = Nothing, Optional ByRef Before As Object = Nothing, Optional ByRef After As Object = Nothing) Implements VBA._Collection.Add
        impl.Add(Item, CStr(Key), Before, After)
    End Sub

    Public Function Count() As Integer Implements VBA._Collection.Count
        Return impl.Count
    End Function

    Public Function GetEnumerator1() As IEnumerator Implements VBA._Collection.GetEnumerator
        Return impl.GetEnumerator()
    End Function

    Public Function Item(ByRef Index As Object) As Object Implements VBA._Collection.Item
        Return impl.Item(Index)
    End Function

    Public Sub Remove(ByRef Index As Object) Implements VBA._Collection.Remove
        If TypeOf Index Is String Then
            impl.Remove(CStr(Index))
        Else
            impl.Remove(CInt(Index))
        End If
    End Sub
End Class

未经测试,我没有安装工具了。应该在球场上:)

答案 1 :(得分:0)

好消息。您可以在.Net项目中伪造VBA.Collection而不使用任何COM-interop引用。

我赞同最后的答案:https://social.technet.microsoft.com/Forums/en-US/b4574645-6f63-49d3-a225-a6c138945ca9/return-vbacollection-from-vbnet-to-vb6-via-com-interop?forum=vbinterop

首先,只需将vb.net中的参数声明为VBA.Collection,就可以从VB6收到Object到VB.Net。不需要特殊的编组。

包含这些类型的.net程序集需要是COM可见的,需要注册COM互操作;但同样,不需要参考。

然后将一个返回到VB6,以下接口需要在方法的签名上。返回值。您无需引用VBAmsvbvm60.dll即可使用此功能。

Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices.CustomMarshalers
Imports System.Reflection
Imports System.Runtime.CompilerServices

''' <summary>
''' IVBACollection mimics VBA.Collection without needing any COM Interop reference
''' </summary>
''' <remarks></remarks>
<ComImport(), TypeLibType(CShort(&H1050)), Guid("A4C46780-499F-101B-BB78-00AA00383CBB"), DefaultMember("Item")> _
Public Interface IVBACollection
  Inherits IEnumerable

  <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime), DispId(0)> _
  Function Item(<[In](), MarshalAs(UnmanagedType.Struct)> ByRef Index As Object) As <MarshalAs(UnmanagedType.Struct)> Object

  <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime), DispId(1)> _
  Sub Add(<[In](), MarshalAs(UnmanagedType.Struct)> ByRef Item As Object, _
                    <[In](), MarshalAs(UnmanagedType.Struct)> Optional ByRef Key As Object = Nothing, _
                    <[In](), MarshalAs(UnmanagedType.Struct)> Optional ByRef Before As Object = Nothing, _
                    <[In](), MarshalAs(UnmanagedType.Struct)> Optional ByRef After As Object = Nothing)

  <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime), DispId(2)> _
  Function Count() As Integer

  <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime), DispId(3)> _
  Sub Remove(<[In](), MarshalAs(UnmanagedType.Struct)> ByRef Index As Object)

  <MethodImpl(MethodImplOptions.InternalCall, MethodCodeType:=MethodCodeType.Runtime), DispId(-4)> _
  Shadows Function GetEnumerator() As <MarshalAs(UnmanagedType.CustomMarshaler, MarshalType:="", MarshalTypeRef:=GetType(EnumeratorToEnumVariantMarshaler), MarshalCookie:="")> IEnumerator

End Interface

如果你需要创建一个VBA.Collection并将其发送回来,你只需创建并填充这个类的一个实例并将其传递回VB6,这很好地消耗它。请记住,您的签名需要为IVBACollection,而不是VBACollection

Public Class VBACollection
  Implements IVBACollection

  Private _Collection As New Microsoft.VisualBasic.Collection()

  Public Sub Add(ByRef Item As Object, _
                 Optional ByRef Key As Object = Nothing, _
                 Optional ByRef Before As Object = Nothing, _
                 Optional ByRef After As Object = Nothing) Implements IVBACollection.Add

    Dim sKey As String = Nothing
    If Not TypeOf Key Is System.Reflection.Missing And Key IsNot Nothing Then
      sKey = Key.ToString
    End If

    Dim oBefore As Object = Nothing
    If IsNumeric(Before) Then
      oBefore = CInt(Before)
    ElseIf Not TypeOf Before Is System.Reflection.Missing And Before IsNot Nothing Then
      oBefore = Before.ToString
    End If

    Dim oAfter As Object = Nothing
    If IsNumeric(After) Then
      oAfter = CInt(After)
    ElseIf Not TypeOf After Is System.Reflection.Missing And After IsNot Nothing Then
      oAfter = After.ToString
    End If

    _Collection.Add(Item, sKey, oBefore, oAfter)

  End Sub

  Public Function Count() As Integer Implements IVBACollection.Count
    Return _Collection.Count
  End Function

  Public Function Item(ByRef Index As Object) As Object Implements IVBACollection.Item
    If IsNumeric(Index) Then
      Return _Collection.Item(CInt(Index))

    ElseIf _Collection.Contains(Index.ToString) Then
      Return _Collection.Item(Index.ToString)

    Else
      Err.Raise(5, Description:="Item '" + Index.ToString + "' not in collection.")
      Return ErrorToString(5)
    End If
  End Function

  Public Sub Remove(ByRef Index As Object) Implements IVBACollection.Remove
    If IsNumeric(Index) Then
      _Collection.Remove(CInt(Index))
    Else
      _Collection.Remove(Index.ToString)
    End If
  End Sub

  Default Public ReadOnly Property Defaultprop(ByVal Index As Object) As Object
    Get
      Return Item(Index)
    End Get
  End Property

  Public Function GetEnumerator() As System.Collections.IEnumerator Implements IVBACollection.GetEnumerator, System.Collections.IEnumerable.GetEnumerator
    Return _Collection.GetEnumerator
  End Function

End Class