Interfaces, CoClasses, and Aliases

twinBASIC supports these features as native language syntax where in VBx they were only supported via Type Libraries.

Defining Interfaces

twinBASIC supports defining COM interfaces using BASIC syntax, rather than needing an type library with IDL and C++. These are only supported in .twin files, not in legacy .bas or .cls files. They must appear before the Class or Module statement, and will always have a project-wide scope. The generic form for this is as follows:

[InterfaceId ("00000000-0000-0000-0000-000000000000")]
*<attributes>*
Interface <name> Extends <base-interface>
    *<attributes>*
    <method 1>
    *<attributes>*
    <method 2>
    ...
End Interface

Methods can be any of the following: Sub, Function, Property Get, Property Let, or Property Set, with arguments following the standard syntax, and with the standard attributes available. These cannot be modified with Public/Private/Friend. End <method> is not used, as these are prototype definitions only.

Available Attributes for Interfaces

  • [Description("text")] - Provides a description in information popups, and is exported as a helpstring attribute in the type library (if applicable).
  • [Hidden] - Hides the interface from certain Intellisense and other lists.
  • [Restricted] - Restricts the interface methods from being called in most contexts.
  • [OleAutomation(True/False)] - Controls whether this attribute is applied in the typelibrary. This attribute is set to True by default.
  • [ComImport] - Specifies that an interface is an import from an external COM library, for instance, the Windows shell.
  • [ComExtensible(True/False)] - Specifies whether new members added at runtime can be called by name through an interface implementing IDispatch. This attribute is set to False by default.

Available Attributes for Methods

  • [Description("text")] - Provides a description
  • [PreserveSig] - For COM interfaces, normally methods return an HRESULT that the language hides from you. The [PreserveSig] attribute overrides this behavior and defines the function exactly as you provide. This is necessary if you need to define it as returning something other than a 4-byte Long, or want to handle the result yourself, bypassing the normal runtime error raised if the return value is negative (this is helpful when a negative value indicates an expected, acceptable failure, rather than a true error, like when an enum interface is out of items).
  • [DispId(number)] - Defines a dispatch ID associated with the method.

Example

[InterfaceId("E7064791-0E4A-425B-8C8F-08802AAFEE61")]
[Description("Defines the IFoo interface")]
[OleAutomation(False)]
Interface IFoo Extends IUnknown
    Sub MySub(Arg1 As Long)
    Function Clone() As IFoo
    [PreserveSig]
    Function MyFunc([TypeHint(MyEnum)] Arg1 As Variant) As Boolean
End Interface

(Where MyEnum is a standard Enum ... End Enum block.)

Defining CoClasses

In addition to interfaces, twinBASIC also allows defining coclasses – creatable classes that implement one or more defined interfaces. Like interfaces, these too must be in .twin files and not legacy .bas/.cls files, and must appear prior to the Class or Module statement. The generic form is:

[CoClassId("00000000-0000-0000-0000-000000000000")]
*<attributes>*
CoClass <name>
    [Default] Interface <interface name>
    *[Default, Source] Interface <event interface name>*
    *<additional Interface items>*
End CoClass

Each coclass must specify at least one interface but may have several more. It can optionally mark an interface as default or a source. It is typical and highly recommended that an interface be marked with [Default] attribute and in cases where it has events to also specify [Default, Source] to indicate the default interface used for events. Each represents a contract that the class will provide an implementation of the given interface. Note that at this time, twinBASIC does not yet support defining dispinterface interfaces (aka, dispatch-only interface) the usual form of source interfaces for events.

Attributes for Coclasses

  • [Description("text")] - Provides a description in info popups and other places.
  • [ComCreatable(True/False)] - Indicates that this coclass can be created with the New keyword. This is True by default.
  • [AppObject] - Indicates the class is part of the global namespace. You should not include this attribute without a full understanding of the meaning.
  • [Hidden] - Hides the coclass from appearing in certain places.
  • [CoClassCustomConstructor("fully qualified path to factory method")] - Allows custom logic for creating and returning a new instance of the coclass’ implementation.

Example

[CoClassId("52112FA1-FBE4-11CA-B5DD-0020AFE7292D")]
CoClass Foo
   [Default] Interface IFoo
   Interface IBar
End CoClass

Where IFoo and IBar are interfaces defined with the Interface syntax described earlier.

Custom Constructor Example

[InterfaceId("016BC30A-A8E0-4AAF-93AE-13BD838A149E")]
Public Interface IFoo
    Sub Foo()
End Interface

[InterfaceId("2A20E655-30A4-4534-86BC-6A7E281C425D")]
Public Interface IBar
    Sub Bar()
End Interface

[CoClassId("7980D953-10BF-478C-93BB-DD0093315D96")]
[CoClassCustomConstructor("FooFactory.CreateFoo")]
[COMCreatable(True)]
Public CoClass Foo
    [Default] Interface IFoo
    Interface IBar
End CoClass

' The implementation do not have to be exposed. The coclass is a sufficient description
' and we should implement the interfaces that the coclass exposes.
Private Class FooImpl
    Implements IFoo
    Implements IBar

    Public Sub Foo() Implements IFoo.Foo
        Debug.Print "Foo ran"
    End Sub

    Public Sub Bar() Implements IBar.Bar
        Debug.Print "Bar ran"
    End Sub
End Class

Public Module FooFactory
    ' The signature must be "preserved", returning a HRESULT
    ' and the new instance via the "out" parameter.
    ' Note that we new up the FooImpl but return the Foo coclass.
    Public Function CreateFoo(ByRef RHS As Foo) As Long
        Set RHS = New FooImpl
        Return 0 ' S_OK
    End Function
End Module

Public Module Test
    Public Sub DoIt()
        Dim MyFoo As Foo
        ' create a new instance of coclass Foo
        ' this implicilty calls the custom constructor
        ' in the FooFactory.
        Set MyFoo = New Foo
        MyFoo.Foo
    End Sub
End Module