LimeLM
wyBuild
Support forum
wyDay blog
wyDay Home

Using TurboFloat with Visual Basic for Applications (VBA)

VBAThis article will give step-by-step instructions on how to add floating-licensing to your VBA app. There's also a full example app that you can download and play with without needing to add licensing to your app.

By the end of this article you'll have working floating-licensing integrated with your application, and thus the ability to sell individual copies of your software.

This article shows you how to add floating-licensing to your VBA app using TurboFloat. To add hardware-locked licensing to your VBA app see the "Using TurboActivate with VBA" article.

Sign up or login and download the native TurboFloat library

Before you can do anything, you need to login to your LimeLM account (or sign up). Then download TurboFloat for Windows or macOS. It contains the native library and source code examples needed to integrate floating licensing in your VBA app:

Adding floating licensing to your appTurboActivate.dat and Version GUID

After you've created a new product, go to the version page of the product you will be adding licensing to. You will need to do 2 things:

  1. Download the TurboActivate.dat file for the product version.
  2. Make a note of the Version GUID.

You'll be including the TurboActivate.dat file in the same folder as the TurboFloat.dll file and you'll use the Version GUID in your code as part of integrating TurboFloat within your app.

Example project

Included in TurboFloat Library package is a simple VBA example project "YourVBAProject.xlam". You can find it in the "API\VBA" folder after you've downloaded and extracted the TurboFloat Library package. We recommend playing around with the example app first, before you start integrating TurboFloat into your application.

The following files are what you'll be including in your app:

A note about some VBA bugs

Visual Basic 6 / VBA is an ancient language that will seemingly never die (and never improve). So, to use TurboFloat with VBA you need to be aware of the following bugs:

Bug #1: You can only use ports between 0 and 32,767

VBA doesn't support "unsigned short" values, meaning you can only use Ports between 0 and 32,767 (anything about 32,767 will cause an overflow).

Bug #2: VBA / VB6 don't support proper threading

Because VBA & VB6 don't support threading there's one feature that won't be available in this language that is available in modern languages. When the "tf_LeaseChange" is called by TurboFloat the "TF_CB_FEATURES_CHANGED" will never be returned for the status. Even if there are new custom license fields delivered by the TurboFloat Server, you'll never be notified that they came in.

Special instructions for Windows

Use STDCALL files

If your VBA app will be running on Windows then you'll need to use the "stdcall" version of TurboFloat (in the "stdcall" folder in the "TurboFloat Library package").

Also, if you're targeting the 64-bit version of Windows (e.g. you're app is an extension that will run in 64-bit version of Microsoft Office) then you'll need to include the x64 version of TurboFloat (from the "stdcall\x64" folder), but rename it to "TurboFloat.x64.dll". This way you can include both the x86 and x64 versions of TurboFloat and target both versions of Office without having to release 2 separate versions of your app.

Set the file locations

Next, you'll need to tell TurboFloat where to find the TurboFloat.dll, TurboFloat.x64.dll, and TurboActivate.dat files. For Windows we recommend keeping these files all in the same folder and then opening the "TurboFloatHelper.bas" and modifying the GetTFDirectory() to return the path where these files will be found. By default, on Windows, GetTFDirectory() returns "ThisWorkbook.path", which is the location of the *.xlam file.

Special instructions for Mac OS X

The Mac OS X versions of Microsoft Office can't load the libTurboFloat.dylib from arbitrary locations. This means you'll need to make an installer for your add-in, and that installer will install the VBA extension somewhere and also install the "libTurboFloat.dylib" and "TurboActivate.dat" files to a pre-defined location. You'll then be able to hard-code these predefined locations inside your app.

1. Hardcode location of libTurboFloat.dylib

To hardcode the location of the libTurboFloat.dylib file, modify the TurboFloat.cls file with the path you'll be using in your installer:

#If Mac Then
Private Declare Function TF_GetHandle Lib "/Library/Application Support/Microsoft/YourApp/libTurboFloat.dylib" (ByVal VersionGUID As String) As Long
...

Apply that same hardcoded location to all the "Private Declare Function" statements in the Mac section of the #If block.

2. Hardcode location of TurboActivate.dat

Next, you'll need to tell TurboFloat where to find the TurboActivate.dat file. Open the "TurboFloatHelper.bas" and modify the GetTFDirectory() to return the path where the TurboActivate.dat file will be found.

Office 2016 (and newer) for Mac OS X have a new "sandboxing" feature. What this means is that on Mac OS X, you need to put the "libTurboActivate.dylib" and "TurboActivate.dat" files in a location that Office 2016 can grant permission to use its sandbox. One good location to put those files would be here: /Library/Application Support/Microsoft/YourApp/, and replacing "YourApp" with your application name.

Then, at the start of your application, you need to grant permission for the files that TurboFloat will use. So, in the example "YourVBAProject.xlam", the "Workbook_Open" function looks like this:

Private Sub Workbook_Open()

    Dim pdetsFilename As String
    pdetsFilename = GetTFDirectory() & Application.PathSeparator & "TurboActivate.dat"

    'Note: Did you open the TurboFloatHelper module and change the GetTFDirectory() function
    ' to return the correct path to your TurboActivate.dat file? If not, you'll almost certainly
    ' get an error.

    #If Mac And MAC_OFFICE_VERSION >= 15 Then
        Dim fileAccessGranted As Boolean
        Dim filePermissionCandidates

        'Create an array with file paths for which permissions are needed
        filePermissionCandidates = Array("/Library/Application Support/Microsoft/YourApp/libTurboFloat.dylib", pdetsFilename)

        'Request access from user
        fileAccessGranted = GrantAccessToMultipleFiles(filePermissionCandidates)

        ' show an error message to the user and exit immediately
        If Not fileAccessGranted Then
            MsgBox "Failed get access to necessary files."
            ExitAppNow
            Exit Sub
        End If
    #End If

    ' Create the TurboFloat instance, and if it fails, exit the addin immediately
    If Not InitializeTurboFloat(True) Then
        ExitAppNow
    End If
End Sub

Step-by-step walkthrough

We're going to walk you through adding floating licensing to your app by using our VBA example application (YourVBAProject.xlam). If you haven't downloaded it already you can get the example app inside the TurboFloat Library package.

Step 1. Start the TurboFloat Server

Before you can continue, you need to start a TurboFloat Server instance. We recommend spinning-up a TurboFloat Server instance on our infrastructure using LicenseChest. Alternatively, your customers can host the TurboFloat Server on their own infrastructure by activating and installing the TurboFloat Server locally.

We recommend using our hosted TurboFloat Server option because it's fast, stable, up-to-date, and incredibly easy to deploy.

Step 2. Request "license lease"

In the example VBA application the "ThisWorkbook" is where the "main logic" of the app is. The "Workbook_Open()" function is the entry point for the application. And because it's the entry point of the application it's where we'll be requesting the "license lease" from the TurboFloat Server.

To setup the TurboFloat instance in your app you'll need to add a function like this to your "ThisWorkbook":

' Only call this function once. If you call it many times unexpected behavior will happen.
Public Function InitializeTurboFloat(ByVal showPrompt As Boolean) As Boolean

    ' create the new TurboFloat instance
    Set globalTfInstance = New TurboFloat

    ' tell your app to handle errors in the section
    ' at the end of the sub
    If showPrompt Then
        On Error GoTo TFProcError
    Else
        On Error GoTo FailCleanup
    End If

    'TODO: goto the version page at LimeLM and paste this GUID here
    Call globalTfInstance.Init("18324776654b3946fc44a5f3.49025204", vbNullString, Not showPrompt)

    ' if we're not showing the prompt then it's presumed
    ' that we're not trying to request a license lease, so, just skip it
    If Not showPrompt Then
        GoTo ProcExit
    End If

    ' request the lease
    Call globalTfInstance.RequestLease


AfterRequest:
    ' If you've reached this point then your app has
    ' successfully acquired a license lease. If you
    ' have custom license field values that you want
    ' to use, you can get them now.
    ' See: https://wyday.com/limelm/help/license-features/

    'Dim fieldValue As String
    'fieldValue = globalTfInstance.GetFeatureValue("your field name", "default value")
    'TODO: do something with the fieldValue

ProcExit:
    InitializeTurboFloat = True
    Exit Function

TFProcError:

    If Err.Number = TF_E_INET Or _
       Err.Number = TF_E_INET_TIMEOUT Or _
       Err.Number = TF_E_WRONG_SERVER_PRODUCT Or _
       Err.Number = TF_E_SERVER_UUID_MISMATCH Or _
       Err.Number = TF_E_USERNAME_NOT_ALLOWED Then

        ' Give the user an option to try another server if they
        ' couldn't connect to the first one, or if the first one
        ' is for a different product.
        Dim inetErr As frmFloatInternetErr
        Set inetErr = New frmFloatInternetErr

        If inetErr.ShowDialog(globalTfInstance) = vbOK Then
            Resume AfterRequest
        Else
            ' Return false and cleanup memory
            Resume FailCleanup
        End If

    ElseIf Err.Number = TF_E_SERVER Then

        ' if it's a server error, then prompt for the server
        Dim srvConf As ServerConfig
        Set srvConf = New ServerConfig

        If srvConf.ShowDialog(globalTfInstance) = vbOK Then
            Resume AfterRequest
        Else
            ' Return false and cleanup memory
            Resume FailCleanup
        End If
    End If


    MsgBox "Failed to get a lease from the floating license server: " & Err.Description

FailCleanup:

    Set globalTfInstance = Nothing

    ' Tell the customer that it failed
    InitializeTurboFloat = False

End Function

In that function you'll need to replace the version GUID with your Version GUID you copied earlier. Then, in the Workbook_Open event handler for the add-in, you'll actually create the new instance of the TurboFloat class:

Private Sub Workbook_Open()
    ' Create the TurboFloat instance, and if it fails, exit
    ' the addin immediately
    If Not InitializeTurboFloat(True) Then
        ExitAppNow
    End If


    ' Your Code can go here -- after the lease has
    ' successfully been received.

End Sub

Of course this calls the helper function "ExitAppNow":

' Exit the application now
Public Sub ExitAppNow()
    On Error Resume Next
    ThisWorkbook.Close
    End
End Sub

Step 3. Handle the "LeaseChange" event

The native TurboFloat library handles all the details about renewing leases, retrying, etc. All you have to do is handle the cases where TurboFloat talks to your app and tells it something has changed (license lease failing to be renewed or new license field data). To do this you need to handle the LeaseChange event:

Public Sub tf_LeaseChange(ByVal status As TF_LeaseStatus)

    If status = TF_CB_FEATURES_CHANGED Then
        'TODO: if you're using feature values in your app
        '      you might want to reload them now.

        ' VBA / VB6 will never see this, because it lacks proper
        ' multi-threading.

    Else ' TF_CB_EXPIRED, TF_CB_EXPIRED_INET, and everything else

        ' Immediately disable the app and/or show a modal
        ' form that gives the user the option to Save /
        ' Save As the data (if applicable) and let the
        ' user re-try connecting to the server.
        
        'TODO: maybe do something here
        

        'Note: Don't just close your app. Your users will be
        '      rightfully ticked-off if you do something like that.

        ' Instead, we're showing a modal dialog that lets the
        ' user try to get a new lease from the server. Or,
        ' if they can't, then save their data.

        Dim frmLeaseExp As LeaseExpired
        Set frmLeaseExp = New LeaseExpired

        If frmLeaseExp.ShowDialog(globalTfInstance) = vbCancel Then
            ' close the app immediately
            ThisWorkbook.ExitAppNow
            Exit Sub
        End If

        ' We've gotten a new lease, so re-enable the app.
        'TODO: maybe do something here
    End If
End Sub

Step 4. Finishing touches

Included in the example VBA app are 3 forms that you might want to copy to your own application:

  1. LeaseExpired.frm: a form to show if the license lease has expired.

    Lease expired

  2. frmFloatInternetErr.frm: a form to show if there's an internet error (that is, a server is specified, but your app couldn't connect to the server).

    Internet error

  3. ServerConfig.frm: a form to allow the end-user to enter details about where their TurboFloat Server is located.

    Config TurboFloat Server location

    This simple dialog gives your customers easy entry of the details needed to connect to their TurboFloat Server instance, whether they host it on our infrastructure using LicenseChest or they host it locally.

Step 5. Dropping the lease when your app closes

After you've successfully requested a lease from the TurboFloat Server, the TurboFloat library integrated in your app takes care of renewing the leases automatically and silently. You'll only ever get a notification of something going wrong in the handler for the LeaseChange event that we covered in Step 3.

When your app is closing you should "drop" the lease using the DropLease() method, and cleanup the memory using the Cleanup() method. By default our TurboFloatHelper.bas file handles this with the "Auto_Close" function:

' This is called when the workbook is closed
Sub Auto_Close()
    On Error Resume Next

    ' try-to recreate the instance because "End" might have been called
    ' (thus killing the instance even if there's an active lease).
    If (globalTfInstance Is Nothing) Then
        ThisWorkbook.InitializeTurboFloat (False)
    End If

    ' If there's an active TurboFloat instance, it's presumed there's
    ' an active TFS lease
    If Not (globalTfInstance Is Nothing) Then
        ' try to drop the lease
        globalTfInstance.DropLease
    End If
End Sub

What this does is tell the TurboFloat Server that you're through using the lease in this instance of your app, and another instance of your app on another computer or another session can now use that "free slot".

If you can't drop the lease (because your app can't connect to the internet, or for any other reason), and you choose to exit your app anyway, then the "lease" on the TurboFloat Server will be a "zombie". The lease will expire eventually on the TurboFloat Server, and thus the free slot will open back up.

Step 6: Testing the lease change event

Testing requesting leases, dropping them, and everything else in the TurboFloat Library is intuitive: just call the function and it does the thing you want it to do. Testing the lease change event is, however, slightly less intuitive. Here's how you can test the lease change event:

  1. If your app is open and has a lease, close your app and make sure it drops the license lease from the TurboFloat Server.

  2. Stop the TurboFloat Server instance.

  3. Open the TurboFloat Server config file, and edit <lease .../> element and set it to "30". This will set the lease length to 30 seconds.

  4. Save the changes you made to the configuration file.

  5. Start your TurboFloat Server instance again.

  6. Start your app again, and make sure it successfully gets a license lease from the TurboFloat Server.

  7. Now, stop the TurboFloat Server instance, but leave your app running.

  8. Within the next 30 seconds the lease change event will be called because the TurboFloat Library was not able to renew the license lease automatically.