Advanced language features

These language features are not commonly used on most projects and are for advanced users.

External methods

An External Method adds a reference to an external method which can be a method on a DLL (on Windows), a dylib (on macOS) or a shared library (on Linux). You can also use the Declare command to create an external method.

External Methods are not supported on iOS. Use the Declare command instead.

../../_images/advanced_language_features_external_method_ptr.png

External methods are added using Insert > External Method for a class or module. In the Inspector for the External Method, you can provide the necessary information, including:

  • Method Name

The name of the method. This is the name you will use to call the method.

  • Parameters

The parameters required by the method.

  • Return Type

The return type of if the method is a function.

  • Scope

The scope of the method matches the scoping rules for items in a module or class.

  • Lib

The library containing the method.

  • Alias

This is optional and refers to the actual method name in the library. You only need to provide it if you are using a different Method Name than what the method is called in the library.

  • Soft

Select Soft to only check if the method exists at runtime, rather than when the app launches.

  • Objective-C

Select Objective-C to add an external method to an Objective-C method. This changes the Alias field to say "Selector" where you can specify the selector to call the method.

There are two attributes that can be used with external methods:

  • Platform - Indicates which platform the method can be used on. Valid values are: mac, Win and Linux.

  • Handlenames - A comma-delimited list of Integer parameter names that should take either an Integer or a Ptr.

See OSHandle when creating external methods that can take an Integer or a Ptr.

For more information on creating External Methods, refer to the Declare command in the Documentation.

Structures

A Structure is a compound value type. It consists of a series of fields that are grouped together as a single block. You control the size and order of the fields. A structure provides a convenient alternative to a MemoryBlock as they are also often used to group together information for external function calls.

The values of structures have specific sizes giving the structure its own specific size.

You typically only use structures when you have very specific memory or performance requirements or when you need to interface with an outside API that requires a structure. In most cases, you will want to use Classes with appropriate properties for your data management.

Structures are added to project items such as modules, classes and windows. To add a structure, select the Add button on the toolbar and select Structure from the menu (or use the Insert menu in the main menu bar). This displays the Structure Editor where you specify the fields in the structure and their sizes.

Use the “+” button to add a new field. Fields are added by entering their name followed by the type in this form:

Name As Type

So to enter an Integer you would do:

Age As Integer

Note that when you enter a field, it shows the size. This is the amount of bytes that the field uses in the structure. Integers use 4 bytes (in 32-bit apps), for example.

You can prefix a name with an underscore (_) in order to hide it from auto-complete.

Array of bytes

The preferred way to deal with strings of text in a Structure is to create an array of Bytes, which you can do like this:

Name(50) As Byte

Strings

Not supported on iOS. Use an Array of Bytes instead.

Strings are a special case because you have to specify the exact size of the string in bytes. Unlike normal strings, a string in a structure always takes up the specified size in the structure and you cannot exceed it. Also, strings in structures do not contain encoding information. The syntax is:

Name As String * size

So to have a String with a size of 50:

Name As String * 50

When you are creating a structure to pass to an external method, be sure that your sizes precisely match the sizes specified by the API of the method.

Usage

When you are creating a structure to pass to an external method, be sure that your sizes precisely match the sizes specified by the API of the method.

../../_images/advanced_language_features_structure_editor.png

You can reference structure members using dot notation:

Var cust As CustomerStruct
cust.Age = 60

Structures are useful for organizing data, but they are not object-oriented and are limited in many ways (such as with string sizes). For your project's internal data management, you should almost always use a class over a structure.

However, structures are small and memory efficient. In addition to being useful when used with external methods and Declares, they may also be useful in specific situations where memory must be managed carefully.

Blank and comment lines

When creating your structure, you can leave specific lines blank (nothing for the description) or you can enter a comment for the description. These lines are properly ignored.

Structure alignment

See Structure for information on structure alignment.

64-bit notes

The Integer data type varies in size for a 32-bit or 64-bit app. For a 32-bit app, an Integer is 4 bytes. For a 64-bit app, an Integer is 8 bytes. In the Structure Editor, 32-bit sizes are shown by default. If you want to see 64-bit sizes, turn the "Show 64 bit Sizes" property to ON in the advanced Inspector for the structure.

Delegates

A Delegate data type is an object representing a specific method. Delegates are an advanced feature that decouple interface from implementation in a similar way to events or interfaces. This decoupling allows you to treat a method implementation as a variable that is changeable based on runtime conditions. They represent methods that are callable without knowledge of the target object. You can change the function the delegate points to on the fly.

A Delegate can be declared in either a module or a class. You use the Insert→Delegate menu command or the Add button on the toolbar in the Code Editor to create a Delegate entry, which appears under the containing object. A delegate must have a name and can have optional parameters and a return type. To use a delegate, you have to point it to an address for a method. You can do this using the AddressOf or WeakAddressOf commands. Only methods that match the parameters and return type (the signature) of the delegate may be assigned to the delegate.

When the Delegate contains the address of a method, you can call the method using the Invoke method of the Delegate.

To try out a quick example, create a Delegate on a window and call it MethodCaller. Also on the window, create two methods: TestMethod and AnotherMethod. Each with this code:

MessageBox(CurrentMethodName)

Since these two methods have the same signature they can be assigned to a Delegate and called using the Invoke method. This code in the Pressed event handler of a Button calls a different method depending the state of a CheckBox:

Var callMethod As MethodCaller
If MethodCheck.Value Then
  callMethod = AddressOf TestMethod
Else
  callMethod = AddressOf AnotherMethod
End If

callMethod.Invoke

You can also create a delegate using an external function that returns a pointer. That code would look like this:

Var fp As Ptr = ExternalFunctionThatReturnsAPointer
Var callMethod As New MethodCaller(fp)

See also

AddressOf, Declare, Structure commands