Data Sharing

Method Registration

To expose an io.Connect method that can be invoked by other interop-enabled apps, you need to register the method in io.Connect and provide its implementation.

Registering Methods

To register an io.Connect method, first create a method instance using CreateServerMethod and then invoke Register on the created method instance.

Below is an example of a subroutine which registers an io.Connect method named "MyVBAMethod":

Dim WithEvents MyVBAMethod As GlueServerMethod
...
Public Sub RegisterMethod()
    On Error GoTo HandleErrors

    Set MyVBAMethod = Glue.CreateServerMethod("MyVBAMethod", "", "", "")
    MyVBAMethod.Register
    Exit Sub

    HandleErrors:
        ' Handle exceptions.
        ...
End Sub

Method Implementation

The GlueServerMethod class exposes an event called HandleInvocationRequest. Provide a method implementation as its handler which will be executed when the registered method is invoked.

Below is an example of a subroutine handling an invocation request for a registered method.

Details about the example:

  • the registered method accepts a composite value as an argument with the following structure represented in JSON format:
{
    "operation": "AddSub",
    "operands": [5, 3],
    "metadata": {
        "reason": "testing"
    }
}
  • the example demonstrates how to extract elementary fields from a composite value (see Accessing Composite Value Fields) and how to build a composite return value (see Building Composite Values);

  • the example demonstrates how to send a response to the caller by using SendResult to send the return value to the caller and SendFailure to indicate failure and send an error message and an empty result value to the caller.

Private Sub MyVBAMethod_HandleInvocationRequest(ByVal request As IGlueInvocationRequest)
    On Error GoTo HandleErrors

    Dim Operation As String
    Dim Operand_1, Operand_2 As Integer
    Dim Reason As String

    ' Get the root of the composite argument.
    Dim Args
    Set Args = request.GetReflectData("")

    ' Extract the elementary fields from the composite argument.
    Operation = Args("operation")
    Operand1 = Args("operands")(0)
    Operand2 = Args("operands")(1)
    Reason = Args("metadata")("reason")

    ' Prepare the result as a composite value.
    Dim Result
    Set Result = Glue.CreateGlueValues

    If Operation = "AddSub" Then
        Dim ResultArray(0 To 1) As Integer
        ResultArray(0) = Operand_1 + Operand_2
        ResultArray(1) = Operand_1 - Operand_2

        ' Build the result as a composite value.
        Result("resultArray") = ResultArray
        Result("metadata")("info") = "Handled in VBA."
        ' Send the result back to the caller.
        request.SendResult Result
        Exit Sub
    End If

    HandleErrors:
        ' If an error occurs or "Operation" isn't recognised,
        ' send an error message and an empty result value.
        request.SendFailure "Invalid argument(s) provided.", Glue.CreateGlueValues
End Sub

Method Invocation

To invoke an io.Connect method, you need to obtain an instance of GlueMethodInvocator (proxy), build the argument values, initiate the invocation and provide a callback subroutine to handle the invocation result(s).

Invoking Methods

Below you can see examples of the steps you need to follow to invoke an io.Connect method. At the end of the section you can see a full method invocation example.

Method Invocator (Proxy)

Create a method proxy using CreateMethodInvocator. You can invoke a method multiple times reusing the same method invocator:

Dim WithEvents MyMethodProxy As GlueMethodInvocator

Public Sub InvokeMethod()
    ...
    ' Obtain a method invocator and reuse it for subsequent invocations.
    If MyMethodProxy Is Nothing Then
        Set MyMethodProxy = Glue.CreateMethodInvocator
    End If
    ...
End Sub

Building the Argument Values

Build the arguments as a composite value (see Building Composite Values for details):

Public Sub InvokeMethod()
    ...
    ' Create an empty root value.
    Dim Args
    Set Args = Glue.CreateGlueValues

    ' Add the arguments.
    Args("operation") = "AddSub"

    Dim OperandsArray(0 To 1) As Integer
    OperandsArray(0) = 5
    OperandsArray(1) = 3
    Args("operands") = OperandsArray

    Args("metadata")("reason") = "testing"
    ...
End Sub

Invoking the Method

Use InvokeAsync or InvokeSync to initiate the method invocation.

Below is an example of using InvokeAsync to invoke the registered io.Connect method (for details about the provided arguments, see InvokeAsync):

Public Sub InvokeMethod()
    ...
    MyMethodProxy.InvokeAsync "MyVBAMethod", "", Args, False, "", 3000
    ...
End Sub

Below is a full invocation example:

Dim WithEvents MyMethodProxy As GlueMethodInvocator

Public Sub InvokeMethod()
    On Error GoTo HandleErrors

    ' Obtaining a method invocator.
    If MyMethodProxy Is Nothing Then
        Set MyMethodProxy = Glue.CreateMethodInvocator
    End If

    ' Building the argument values.
    Dim Args
    Set Args = Glue.CreateGlueValues

    Args("operation") = "AddSub"

    Dim OperandsArray(0 To 1) As Integer
    OperandsArray(0) = 5
    OperandsArray(1) = 3
    Args("operands") = OperandsArray

    Args("metadata")("reason") = "testing"

    ' Invoking the method.
    MyMethodProxy.InvokeAsync "MyVBAMethod", "", Args, False, "", 3000
    Exit Sub

    HandleErrors:
    ' Handle exceptions.

End Sub

Targeting

When invoking io.Connect methods, you can target a specific app (server) that has registered the method, a group of servers or the server that has registered the method first. Use the targetRegex and all parameters of InvokeAsync or InvokeSync to specify which servers to target:

  • targetRegex - optional regular expression pattern which allows selecting the target apps which will service the method invocation. If provided, only targets with app name matching the regular expression will be considered. An empty string or Nothing will match all app names;

  • all - a Boolean value indicating whether the method invocation request should be sent to all available app targets or to one target only (the first that has registered the method);

You can use a combination of both parameters to filter the method servers you are interested in. Below is an example of invoking a registered method by targeting all apps with names that start with "clients":

Public Sub InvokeMethod()
    ...
    MyMethodProxy.InvokeAsync "MyVBAMethod", "clients.*", Args, True, "", 3000
    ...
End Sub

Handling Invocation Results

If you aren't interested in the invocation result, you still need to provide an empty implementation for the HandleInvocationResult event or, alternatively, declare the GlueMethodInvocator instance without WithEvents.

Below is an example of a subroutine handling an invocation result and demonstrating how to check the method invocation status and extract the return values:

  • The instance of IGlueInvocationResult passed to the event handler has a Results property which is an array of VBGlueResult containing details about the invocation results. An array is used because the invocation request can be sent to multiple target apps at once. The Results array always contains at least one element.
  • In the full invocation example the invocation request is sent to only one target app, therefore only one result is expected and extracted.
  • The Status property of a VBGlueResult is a GlueMethodInvocationStatus enumeration value.
  • For details on how to extract data from composite values, see Accessing Composite Value Fields.
Private Sub MyMethodProxy_HandleInvocationResult(ByVal result As IGlueInvocationResult)
    On Error GoTo HandleErrors

    Dim ResultArr() As VBGlueResult
    Dim FirstResult As VBGlueResult

    ' Get the first result.
    ResultArr = result.Results
    FirstResult = ResultArr(0)

    If Not FirstResult.Status = GlueMethodInvocationStatus_Succeeded Then
        ' Handle unsuccessful invocation.
        ...
        Exit Sub
    End If

    ' Extract the result fields from the composite value.
    Dim ResultArray
    ResultArray = FirstResult.GlueData.GetReflectData("resultArray")

    Dim AdditionResult, SubtractionResult As Integer
    AdditionResult = ResultArray(0)
    SubtractionResult = ResultArray(1)

    ' Do something with the result.
    ...

    Exit Sub

    HandleErrors:
    ' Handle exceptions.
    ...

End Sub

Discovery

Your app can discover registered Interop methods and streams and other apps (Interop servers) which offer them.

Listing All Methods & Streams

To find all registered io.Connect methods/streams, use the GetMethodNamesForTarget method and pass an empty string or Nothing as an argument:

Dim AllMethodNames() as String

AllMethodNames = Glue.GetMethodNamesForTarget("")

Listing Methods & Streams for Target

To target a specific server or a group of servers for which to get all registered methods/streams, pass a regex value as an argument to GetMethodNamesForTarget:

Dim AllMethodNamesForTarget() as String

AllMethodNamesForTarget = Glue.GetMethodNamesForTarget("client.*")

Listing All Interop Servers

To get a list of the names of all apps offering Interop methods/streams, use the GetTargets method:

Dim AllInteropServers() as String

AllInteropServers = Glue.GetTargets()

Streaming

Overview

Your app can publish events that can be observed by other apps, or it can provide real-time data (e.g., market data, news alerts, notifications, etc.) to other apps by publishing an Interop stream. Your app can also receive and react to these events and data by creating an Interop stream subscription.

Apps that create and publish to Interop Streams are called "publishers", and apps that subscribe to Interop Streams are called "subscribers". An app can be both.

Publishing Stream Data

To expose a data stream to which other apps can subscribe, you need to register a stream and provide implementations for handling the server side streaming events (subscription requests, added/removed subscribers). Once a stream has been successfully registered, the publishing app can start pushing data to it.

Creating Streams

Create a streaming method instance using CreateServerStream and invoke its Register method:

Dim WithEvents MyVBAStream As GlueServerStream

Public Sub RegisterStream()
    On Error GoTo HandleErrors

    If MyVBAStream Is Nothing Then
        ' The only required argument when creating a stream is a stream name.
        Set MyVBAStream = Glue.CreateServerStream("MyVBAStream", "", "", "")
        MyVBAStream.Register
    End If
    Exit Sub

    HandleErrors:
    ' Handle exceptions.
    ...

End Sub

Accepting or Rejecting Subscription Requests

GlueServerStream exposes an event called HandleSubscriptionRequest. Its handler is executed when an app attempts to subscribe to the stream. The handler receives a GlueSubscriptionRequest instance as an argument. You can use its GetReflectData method to extract the request arguments passed as a composite value by the subscriber. Use the Accept method of the request instance to accept the subscription on the default (unnamed) or a specific branch.

Below is an example of a subroutine handling a subscription request. The request is accepted or rejected based on the value of an argument that the subscriber has specified when sending the subscription request:

Private Sub MyVBAStream_HandleSubscriptionRequest(ByVal request As IGlueSubscriptionRequest)
    On Error GoTo HandleErrors

    ' Extract the subscriber-defined `subscriptionCode` field from the request arguments.
    Dim SubscriptionCode As String
    SubscriptionCode = request.GetReflectData("subscriptionCode")

    If SubscriptionCode = "rejectme" Then
        ' Reject the subscription request with a message.
        request.Reject "Invalid subscription code."
        Exit Sub
    End If

    ' Accept the request on the default branch.
    request.Accept "", Nothing
    Exit Sub

    HandleErrors:
    ' Handle exceptions.
    ...

End Sub

Added or Removed Subscriptions

Handling New Subscriptions

GlueServerStream exposes an event called HandleSubscriber. Its handler is executed when a new subscriber is added. The handler accepts two arguments - a VBGlueStreamSubscriber instance and a GlueData instance. The VBGlueStreamSubscriber instance contains information about the subscriber instance and you can use its Push method to send private data directly to it. The GlueData instance is a composite value that contains the subscription request arguments as specified by the subscriber.

Private Sub MyVbaStream_HandleSubscriber(ByVal subscriber As IVBGlueStreamSubscriber, ByVal requestData As IGlueData))
    On Error GoTo HandleErrors

    ' Create a root composite value and add data to it.
    Dim Data
    Set Data = Glue.CreateGlueValues
    Data("info") = "welcome"
    ' Push the data to the new subscriber.
    subscriber.Push Data
    Exit Sub

    HandleErrors:
    ' Handle exceptions.
    ...
End Sub

⚠️ Note that new subscribers won't automatically get the data that has been previously published to the stream. This handler is the place where you can send private updates to the new subscriber, if necessary.

Handling Removed Subscriptions

GlueServerStream exposes an event called HandleSubscriberLost. Its handler is executed when an existing subscriber unsubscribes from the stream.

Below is an example of an empty handler subroutine:

Private Sub MyVBAStream_HandleSubscriberLost(ByVal subscriber As IGlueStreamSubscriber)
    ' Possibly handle the lost subscriber here.
End Sub

Canceling a subscription can be initiated either by the subscriber app or the stream publisher app. This handler is invoked in both cases. Handling this event can be useful if you want to record or propagate it. If this event isn't significant for the app, you must provide an empty handler subroutine.

Default Event Handlers

You may declare the GlueServerStream instance without WithEvents. In this case the io.Connect COM library will use the following default internal implementations:

  • HandleSubscriptionRequest - all subscription requests will be accepted on the default (unnamed) branch;
  • HandleSubscriber - no operation;
  • HandleSubscriberLost - no operation;

Pushing Data

You can push data to a stream by using the PushVariantData method of a GlueServerStream instance. Data can be sent to all subscribers on the stream, or to a group of subscribers on a specific stream branch.

The example below demonstrates how to push data to all subscribers on a stream:

' Create a composite value and add data to it.
Dim Data
Set Data = Glue.CreateGlueValues
Data("info") = "Data broadcast to all subscribers."

' Push data to all subscribers.
MyVBAStream.PushVariantData Data, ""

You can also push data directly to a subscriber by using the Push method of a VBGlueStreamSubscriber instance (see Handling New Subscriptions).

Using Stream Branches

Using stream branches allows you to group subscribers by any criterion and target stream data at specific groups of subscribers. Branches are distinguished by their name (key). Each io.Connect stream has a default (unnamed) branch on which it accepts subscribers and to which it pushes data if no branch is specified.

To accept a subscription on a branch, specify the branch name when accepting the subscription. If the branch doesn't exist, it will be automatically created:

request.Accept "branch_01", Nothing

See also Accepting or Rejecting Subscription Requests.

To push data to a specific branch, specify a branch name when pushing data to the stream:

Dim Data
Set Data = Glue.CreateGlueValues
Data("info") = "Data targeted at subscribers on branch `branch_01`."

MyVBAStream.PushVariantData Data, "branch_01"

Consuming Stream Data

To receive data published on an io.Connect stream, an app has to create an instance of a GlueStreamConsumer, subscribe to a stream and provide the subroutines necessary for handling the incoming data and changes in the stream status.

Subscribing to Streams

Obtain a GlueStreamConsumer instance using CreateStreamConsumer, provide subscription arguments and send a subscription request using the Subscribe method of the GlueStreamConsumer instance. When the subscription request has been resolved or times out, the corresponding event handlers will be executed.

Dim WithEvents StreamConsumer As GlueStreamConsumer
...
Public Sub SubscribeToStream()
    On Error GoTo HandleErrors

    ' Obtain a stream consumer instance.
    If StreamConsumer Is Nothing Then
        Set StreamConsumer = Glue.CreateStreamConsumer
    End If

    ' Build the arguments to send with the subscription request.
    Dim Args
    Set Args = Glue.CreateGlueValues
    Args("subscriptionCode") = "hello"

    ' Send the subscription request.
    StreamConsumer.Subscribe "MyVBAStream", "", Args, False, "", 3000
    Exit Sub

    HandleErrors:
    ' Handle exceptions.
    ...

End Sub

As with invoking Interop methods, you can target specific servers providing the stream. See Targeting for more details.

Handling Subscriptions Client Side

Receiving Data

GlueStreamConsumer exposes an event called HandleStreamData. Its handler is executed when a stream publishing app pushes data to the stream. The handler accepts two arguments - a GlueMethodInfo instance containing information about the stream, and a GlueData instance containing the data pushed to the stream.

Below is an example of a subroutine handling incoming data from a stream:

Private Sub StreamConsumer_HandleStreamData(ByVal stream As IGlueMethodInfo, ByVal data As IGlueData)
    On Error GoTo HandleErrors

    ' Extract information about the data publisher.
    Dim StreamName as String
    Dim ApplicationName as String
    StreamName = stream.method.Name
    ApplicationName = stream.method.Instance.ApplicationName

    ' Extract data from the composite value.
    Dim Info As String
    Info = Data.GetReflectData("info")

    ' Do something with the data.
    ...
    Exit Sub

    HandleErrors:
    ' Handle exceptions.

End Sub

Subscription Activated Handler

GlueStreamConsumer exposes an event called HandleSubscriptionActivated. Its handler is executed when a subscription request has been dispatched to io.Connect. The main purpose for handling this event is to provide an instance of the GlueStreamSubscription which the app may later use to unsubscribe from the stream. To unsubscribe from the stream, use the Close method of the subscription instance.

Below is an example of a subroutine handling the event:

Dim StreamSubscription As IGlueStreamSubscription

Private Sub StreamConsumer_HandleSubscriptionActivated(ByVal subscription As IGlueStreamSubscription)
  ' Keep a reference to the subscription instance.
  Set StreamSubscription = subscription
End Sub

Stream Status Handler

GlueStreamConsumer exposes an event called HandleStreamStatus. Its handler is executed when the GlueStreamState of the associated stream subscription changes.

Below is an example of a subroutine handling the event:

Private Sub StreamConsumer_HandleStreamStatus(ByVal stream As IGlueMethodInfo, ByVal state As GlueStreamState, ByVal Message As String, ByVal dateTime As Double)
    On Error GoTo HandleErrors

    Select Case state
    Case GlueStreamState_Pending
        ' Subscription request is pending.
        ...
    Case GlueStreamState_Opened
        ' Subscription request has been accepted.
        ...
    Case GlueStreamState_Closed
        ' Subscription has been closed.
        ...
    End Select

    Exit Sub

    HandleErrors:
    ' Handle exceptions.

End Sub

You may provide an empty implementation if you aren't interested in performing any actions when the stream subscription status changes.

Stream Closed Handler

GlueStreamConsumer exposes an event called HandleStreamClosed. Its handler is executed when the stream is closed and the associated stream subscription has been terminated.

Here is an example of a subroutine handling the event:

Private Sub StreamConsumer_HandleStreamClosed(ByVal stream As IGlueMethodInfo)
  ' Perform actions when the stream is closed.
  ...
End Sub

You may provide an empty implementation if you aren't interested in performing any actions when the subscription has been terminated.