1

I've developed a VB.NET library (partially developed on C# as well) that heavily depends on inheriting from an abstract generic base class, and I'm trying to figure out the best practice for this. I Sadly have to do it using framework 3.5.

Public MustInherit Class MyBaseClass(Of T)
  Public Whatever As T
End Class

Public Class MyDerivedClass
  Inherits MyBaseClass(Of String)

  Private _myProperty As String

  Public Property MyProperty As String
    Get
      Return _myProperty
    End Get
    Set(value As String)
      _myProperty = value
    End Set
  End Property
End Class

I attach the .tlb file as a reference in VBA (using Excel), and I run the following code:

Dim m As New VBtoVBA.MyDerivedClass
m.MyProperty = "foo"

And I get the error "Run-time error 430: Class does not support Automation or does not support expected interface".

On the other hand, I change the first lines to:

Public MustInherit Class MyBaseClass
  Public Whatever As String
End Class

Public Class MyDerivedClass
  Inherits MyBaseClass

The VBA script works. Hence I assume the issue is with generics (as documented in other sources as well). Dropping the generic feature of my library is not possible, though. The "best" workaround I can think of is to write a third class that includes MyDerivedClass as a field, and works as a non-generic interface to it:

Public Class MyDerivedClassString

  Private _innerObj As New MyDerivedClass

  Public Property MyProperty As String
    Get
      Return _innerObj.MyProperty
    End Get
    Set(value As String)
      _innerObj.MyProperty = value
    End Set
  End Property

  Public Property Whatever As String
    Get
      Return _innerObj.Whatever
    End Get
    Set(value As String)
      _innerObj.Whatever = value
    End Set
  End Property

End Class

This way I can work with it pretty much as I'd like to in VBA:

m.Whatever = "wha"
MsgBox (m.Whatever)

By the way I think that there might be another (better) way to achieve the same result, and i really hope so since m library is made up of dozens of classes.

Many thanks.

Dave
  • 41
  • 3
  • 1
    Were I you, I'd explicitly define a COM interface in the VBNet project, make `MyDerivedClass` implement it (also, give a GUID to the interface and another to `MyDerivedClass`), and see if it helps. – Medinoc Apr 10 '15 at 14:05
  • 3
    Neither COM nor VBA has any notion of generics. Your class will simply not get exported at all. Thus the error. – Hans Passant Apr 10 '15 at 14:05
  • @Medinoc: thanks for the hint. I tried it without success, sadly. Hans: I was kinda trying to find a workaround. This – Dave Apr 10 '15 at 14:10
  • 1
    @Dave the workaround is to not expose generics. – Mathieu Guindon Apr 10 '15 at 14:11
  • @Dave even though this is a nice minimal example, we would need a more concrete example of what you need the generics for to be able to provide advice. – RubberDuck Apr 10 '15 at 14:11
  • I just re-read your question Dave. The last bit, about introducing a third class, that's called the Facade pattern and is quite common when exposing .Net assemblies to com interop. You may want to look into it. – RubberDuck Apr 10 '15 at 14:16
  • @HansPassant: From the code example, the exposed class itself isn't generic, it merely inherits an instantiated generic type, so it should be possible to expose it non-generically (at worst, via an interface). But I could be mistaken, I don't know exactly what COM interop can or can't do. – Medinoc Apr 10 '15 at 14:20
  • @RubberDuck: it looks like it's exactly what I was unconsciously doing, thanks a lot. I have built a layered platform between DB and code (like a self-made entity framework) and the objects derive from a class that is generic depending on the key type (that can be another class in the cases of composite types). A code sample that makes sense would actually be huge. Many thanks btw! – Dave Apr 10 '15 at 14:22
  • 2
    @Med - that's true, but COM doesn't support implementation inheritance either. It needs to create interface types that match the class definitions, the base class' interface will be generic. Always better to declare the interface explicitly so the actual implementation of the class just doesn't matter. – Hans Passant Apr 10 '15 at 14:25
  • Oh, I had forgotten it always generates a "class" interface. – Medinoc Apr 10 '15 at 14:26
  • @Medinoc and hans: Do you think that that approach could be suitable to my case? I'm struggling to find a minimal working example of how that could interact with generics. – Dave Apr 10 '15 at 14:34
  • Your class should expose COM. See: https://msdn.microsoft.com/en-us/library/x66s8zcd.aspx I'd suggest to write interface to this class. – Maciej Los Apr 10 '15 at 14:36
  • Access to dll object (class) through the interface should looks like: `Dim m As VBtoVBA.IMyDerivedClass: Set m = New VBtoVBA.MyDerivedClass` – Maciej Los Apr 10 '15 at 14:38
  • Thanks @MaciejLos . The main shortcoming of interfaces in my case is that all the derived classes I have are substantially different, so that I'd have to write an interface for each class, basically. I think that it would be the same as the Facade approach, more or less. Am I right in saying so? – Dave Apr 10 '15 at 14:51
  • 1
    As per my experience, writing COM library for MS Office applications is bit... hard-coded. Note that one interface can be implemented by few classess. – Maciej Los Apr 10 '15 at 14:57
  • Alright, at least now I know that I'm not missing something crucial... Thank you all guys for the help! – Dave Apr 10 '15 at 15:12

1 Answers1

8

As i mentioned in comment writing library (dll) for MS Office applications is bit... hard-coded. This dll must exposes methods and properties to be able to use it in COM automation. To be able to achieve that, you need to write interface(s):

Namespace VBtoVBA
    Public Interface IMyDerivedClass
        Property MyProperty As String
    End Interface
End Namespace

then in DerivedClass

Public Class MyDerivedClass
    Inherits MyBaseClass(Of String)
    Implements IMyDerivedClass

    Private _myProperty As String

    Public Property MyProperty As String Implements IMyDerivedClass.MyProperty

Now, go to Project Properties window 1) choose Application tab - click on Assembly Information button and in the next window select Make assembly COM visible checkbox (apply setting using OK button),

Assembly information

2) choose Compile tab - select Register for COM interop checkbox

Register for COM

3) Save project and Build dll

4) Now, go to VBA Code editor in Office application -> References menu. In Reference window add reference to yourDllName.tlb

Now, you can use your dll in Office application ;)

I tested code:

Option Explicit

Sub DoSomething()
Dim m As VBtoVBA.MyDerivedClass

Set m = New VBtoVBA.MyDerivedClass

m.MyProperty = "Something"

MsgBox m.MyProperty


End Sub

and it works as well ;)

Maciej Los
  • 8,468
  • 1
  • 20
  • 35