INTERFACE / END INTERFACE Block (Direct)

Purpose | Declare a direct object interface and its member Methods/ Properties.
Syntax | INTERFACE interfacename [$GUID] [AS EVENT] [AS HIDDEN] {METHOD | PROPERTY} name [([arguments])] [AS type]] END INTERFACE

Remarks

The first line in an Interface Block must be an INHERIT statement. INHERIT specifies the base class or the user interface upon which this new interface is built. It defines the base methods available, the optional user methods which are available, and the calling conventions which will apply. In the current version of PowerBASIC, the following may be used:

INHERIT IUnknown

This defines a Custom Interface with only direct access to the interface methods. OBJRESULT (an hResult value) is not supported. Return values are typically passed in CPU/FPU registers, just like a user defined FUNCTION. This is the format most often used for internal objects, as it offers access to more data types than the other forms. You may substitute the word CUSTOM for IUNKNOWN, as they are synonyms.

INHERIT IAutomation

This defines an Automation Interface with only direct access to the interface methods. OBJRESULT (an hResult value) is always supported. Return values are passed as a hidden, last parameter (automatically, by PowerBASIC). Parameters and return values are limited to COM data types. A User Defined Type used as a return value or parameter will be converted to a BYVAL DWORD. This is the format most often used for COM objects which do not require access to the IDispatch interface. You may substitute the word AUTOMATION for IAUTOMATION, as they are synonyms.

INHERIT IDispatch

This defines a Dual Interface, which offers both direct access and Dispatch access to the interface methods. OBJRESULT (an hResult value) is always supported. This interface inherits from IAutomation, so the calling conventions are identical to IAutomation when used for direct access. You may substitute the word DUAL for IDISPATCH, as they are synonyms.

INHERIT <UserClass>, <UserInterface>

This defines an inherited user-written interface, so the new interface implements the base class IUnknown, IDispatch, etc.) and all of the Methods and Properties, as well. It's necessary to specify both the class and the interface name to be inherited, because it's possible to have multiple implementations of any particular interface.

INTERFACE / END INTERFACE statements enclose the METHOD and PROPERTY definitions which constitute a class. There are two forms of the INTERFACE / END INTERFACE block. When it appears outside of a CLASS block, it is simply a declaration of the interface, much like DECLARE statements are used for functions:

INTERFACE name [$GUID] [AS EVENT]

INHERIT IUnknown

METHOD MyMethod(xyz AS LONG)

PROPERTY GET MyProp() AS STRING

END INTERFACE

The above form is used to declare an interface which is implemented in another .EXE or .DLL, but will be accessed here through COM services. It may also be used for added self-documentation of internal classes. If it appears within a CLASS block, it is the implementation of the Methods/Properties for the Class. The interface implementation must precisely match any prior interface declaration.

CLASS name [$GUID] [AS COM]

INTERFACE name [$GUID] [AS HIDDEN]

INHERIT iUnknown

METHOD MyMethod(xyz AS LONG)

[statements]

END METHOD

PROPERTY GET MyProp() AS STRING

[statements]

END PROPERTY

END INTERFACE

END CLASS

The name and optional $GUID are supplied by the programmer to uniquely identify the interface. The first entry in every INTERFACE block must be the base class upon which it is built. Every interface must ultimately inherit from IUnknown, which is a requirement.

By default, a class is considered private, so that the methods are accessible only from within the EXE or DLL where it is defined. The AS COM attribute to the CLASS statement makes the class available externally, to virtually any process which is COM-aware.

The optional AS HIDDEN attribute to the INTERFACE statement prevents the interface from being documented when the type library is created. When marked as hidden, any and all uses of the interface are hidden, even if they appear in multiple classes.

With an internal class, the $GUID on CLASS and INTERFACE statements may be freely omitted, as PowerBASIC can readily identify them by name. With a published COM class, you should insert a specific GUID of your choice. If omitted, a random GUID will be created by the compiler, but it will change every time you compile the program. This will be difficult to synchronize with other programs which wish to identify and access your object.

The following code defines a dual interface whose methods are available for both direct access and Dispatch access. This is the form you will typically use for COM objects, since it offers the best compatibility with varied client modules.

INTERFACE DispatchIface

INHERIT IDispatch

METHOD MethodDef()

[statements]

END METHOD

END INTERFACE

You should note that the IDispatch interface itself inherits from IUnknown, so that both interfaces are ultimately available. As an additional required base class, the IDispatch declaration is built into the PowerBASIC Compiler.

Every method and property in a dual interface needs a positive, long integer value to identify it. That integral value is known as a DispID (Dispatch ID), and it's used internally by COM services to call the correct function on a Dispatch interface. You can specify a particular DispID by enclosing it in angle brackets immediately following the Method/Property name in an Interface definition block.

INTERFACE DualIface

INHERIT IDispatch

METHOD MethodOne <76> ()

METHOD MethodTwo <77> ()

END INTERFACE

If you don't specify a DispID, PowerBASIC will assign a random value for you. This is fine for internal objects, but may cause a failure for published COM objects, as the DispID could change each time you compile your program. It is particularly important that you specify a DispID for each Method/Property in a COM Event Interface.

Inherited User-Written Interfaces

PowerBASIC offers Implementation Inheritance of user-written interfaces. That is, an interface can inherit all of the code in the methods and properties of a selected interface. You can then add additional methods and properties to the new interface. When you inherit a user-written interface, you must specify both the class name and the interface name, since COM allows you to have multiple implementations of any particular interface.

You can override an inherited method or property by coding a replacement which is preceded by the word OVERRIDE. It's possible to one or many override procedures, but they must appear in the same sequence as the ones they replace.

CLASS MyClass

INTERFACE MyFace

INHERIT IDispatch

METHOD aaa()

' code...

END METHOD

METHOD bbb()

' code...

END METHOD

METHOD ccc()

' code...

END METHOD

METHOD ddd()

' code...

END METHOD

END INTERFACE

END CLASS

CLASS TheClass

INTERFACE TheFace

INHERIT MyClass, MyFace

OVERRIDE METHOD bbb()

' new code...

END METHOD

OVERRIDE METHOD ddd()

' new code...

END METHOD

METHOD xxx()

' code...

END METHOD

END INTERFACE

END CLASS

Note that in the above example, the new interface "TheFace" first inherits all four methods from "MyFace" (aaa,bbb,ccc,ddd). However, because of the OVERRIDE statements, both bbb() and ddd() are replaced by newer versions of the methods. Because of the nature of Virtual Function Tables, the OVERRIDE procedures must remain in the original sequence. That is, bbb() must precede ddd(), and both must precede any added methods, such as xxx().

Because of the nature of code replacement necessary in implementation inheritance, the interface to be inherited must always physically precede the new, child interface.

See also | INTERFACE (IDBind), CLASS, INSTANCE, ISINTERFACE, LET (with Objects), ME, METHOD, MYBASE, PROPERTY, What does an Interface look like?, What is inheritance?