This 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.
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:
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:
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.
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:
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:
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).
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.
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.
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.
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.
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.
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
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.
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.
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
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
Included in the example VBA app are 3 forms that you might want to copy to your own application:
LeaseExpired.frm: a form to show if the license lease has expired.
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).
ServerConfig.frm: a form to allow the end-user to enter details about where their TurboFloat Server is located.
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.
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.
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:
If your app is open and has a lease, close your app and make sure it drops the license lease from the TurboFloat Server.
Stop the TurboFloat Server instance.
Open the TurboFloat Server config file, and edit <lease .../>
element and set it to "30". This will set the lease length to 30 seconds.
Save the changes you made to the configuration file.
Start your TurboFloat Server instance again.
Start your app again, and make sure it successfully gets a license lease from the TurboFloat Server.
Now, stop the TurboFloat Server instance, but leave your app running.
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.