我已经通过与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的项目中找到它。如果有一种方法可以摆脱这种情况,那就更好了。
答案 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引用。
首先,只需将vb.net中的参数声明为VBA.Collection
,就可以从VB6收到Object
到VB.Net。不需要特殊的编组。
包含这些类型的.net程序集需要是COM可见的,需要注册COM互操作;但同样,不需要参考。
然后将一个返回到VB6,以下接口需要在方法的签名上。返回值。您无需引用VBA
或msvbvm60.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