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.
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
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.
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.