I have been selling an Add-In for Microsoft Excel on another website for a number of years now. It has sold OK and recently has been selling quite a bit. I never really invested the time or effort to create any sort of licensing function with it, so pretty much whenever someone purchased it they were free to install it on as many computers as they wanted and I’d be none the wiser. I’m sure this resulted in quite a bit of lost revenue over the years, so I decided to do something about it.
If you have purchased any of my WordPress plugins, I use the Easy Digital Downloads Software Licensing extension to manage how many sites a plugin can be installed on. The way it works is a customer purchases the plugin or some other download file like an Excel Add-In, and they are provided with a license key that is valid for a period of time after the date of purchase. On the download file, there is a license key validation that takes place in order for the software to be used and updated.
Now originally the Software Licensing extension was designed for use with WordPress plugins but it has a complete API that allows interaction with other digital products including software licensing for Excel Add-In.
Software Licensing for Excel Add-In
Interacting with the Easy Digital Downloads Software Licensing for Excel Add-In presented a few challenges because as far as I could tell it wasn’t ever really attempted. At least I couldn’t find any documentation on it, and the Easy Digital Downloads support staff didn’t have any specific advice on how to go about setting it up. That’s not to say that their support isn’t top-notch because they certainly are! Excel Add-Ins just aren’t their focus. That’s why I’m writing this guide.
My goals for the licensing component of the add-in were:
- Force the user to activate a valid license key that was not in use elsewhere in order to use the add-in.
- Allow the user to deactivate a license in the event that they ever want to use the add-in on another computer.
- Store license information in the add-in and check whether or not it is valid when Excel starts up.
- Make the user experience as easy and intuitive as possible.
What you’ll need in order to follow along:
- A self-hosted website running WordPress.
- Install Easy Digital Downloads plugin on your website.
- Install the Software Licensing extension for Easy Digital Downloads on your website.
- A downloadable product in Easy Digital Downloads
- Optionally install the Discounts Pro extension for Easy Digital Downloads on your website. I used this to offer discounts based on the number of licenses that were purchased. You could use the software licensing extension to do something similar, but I wanted to keep each license limited to one activation at a time. If you opt out of this step you may need to tweak your code a little to get the checks right.
- A Windows version of Excel (this might work on Mac versions, but I haven’t tested it). I used Excel 2010 on Windows 10, although I believe this will work on any version of Excel after 2003.
The remainder of the tutorial assumes you have everything in the list above.
Software Licensing for Excel Add-In Step 1:
The Easy Digital Downloads Software Licensing documentation provides a few useful guides that help you connect to your shop to perform actions such as activate, deactivate, check, and get the version of your license. While you may be developing in other languages, all you’ll need for this example is the URL right at the top of their documentation.
http://YOURSITE.com/?edd_action={request type}&item_name=EDD+Product+Name&license=cc22c1ec86304b36883440e2e84cddff |
If you notice on their documentation there is a field in the link that I did not include &url=
. This is used to associate a license with website software like a WordPress plugin, but really has no purpose for desktop software so we can leave it off.
What we’ll need to do is create some code to check the license to see if it is valid or not, to activate the license, and to deactivate the license. In order to do this we will save some information in the add-in itself. Since an add-in is basically the same as any other workbook we can save data to a worksheet in the add-in without writing to any other active workbook. In my add-in I saved data to Sheet1 of the add-in in the following locations:
- Cell A1: the license key entered by the end user.
- Cell A2: the JSON object response from the website when it checks the license key’s status.
- Cell A3: a formula to extract the license status from the JSON object in A2.
- Cell A4: a formula to extract the number of activations left from the JSON object in A2. In my scenario, the only valid responses will be 1 for a license that is not active or 0 for a license that is active. You may have more if you choose to allow a license to be activated more than once.
To check the license:
'json response from Software Licensing in the following format '{"success":true,"license":"valid","item_name":"Download Product Name","expires":"lifetime","payment_id":"54224","customer_name":"John Doe","customer_email":"john@sample.com","license_limit":1,"site_count":1,"activations_left":0} Sub httpRequestCheck() 'use to check license once when workbook opens Dim oRequest As Object Const cUrl As String = "https://yoursite.com/?edd_action=check_license&item_name=Name of the Download Product&license=" URL = cUrl & ThisWorkbook.Worksheets("Sheet1").Range("A1").Value Set oRequest = CreateObject("WinHttp.WinHttpRequest.5.1") oRequest.Open "GET", URL oRequest.Send ThisWorkbook.Worksheets("Sheet1").Range("A2:A4").ClearContents ThisWorkbook.Worksheets("Sheet1").Range("A2").Value = oRequest.ResponseText 'The MID and FIND functions allow for searching through the json object for certain terms (license and activations_left) then return a certain number of characters after it. 'MID will start at the first character of the word in FIND, offset a number of characters (10 for license), then return a number of characters after (8 for the word inactive, 5 for valid, etc.). '2x Double quotes are needed to enter formulas this way, so "license" wouldn't work but ""license"" will. ThisWorkbook.Worksheets("Sheet1").Range("A3").Formula = "=IF(MID($A$2,FIND(""license"",$A$2,1)+10,8)=""inactive"",""inactive"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,5)=""valid"",""valid"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,7)=""invalid"",""invalid"","""")))" ThisWorkbook.Worksheets("Sheet1").Range("A4").Formula = "=MID($A$2,FIND(""activations_left"",$A$2,1)+18,1)" End Sub |
To activate the license:
Sub httpRequestActivate() 'Run the httpRequestCheck to see if the license has used up all of the activations it had available. 'If it has we add in_use to cell A5 so that we can avoid allowing the license to be activated on more computers than our license allows. 'Note after the first activation, the license will return as valid before the end of the license expiration even if it has been deactivated so we need to check for remaining activations this way as well. httpRequestCheck With ThisWorkbook.Worksheets("Sheet1").Range("A1") If .Cells(4, 1).Value = 0 Then 'Cells(4, 1).Value is the number of activations remaining. In my case, there will only be 1 or 0 but even if you allow more than one activation, 0 is where we should stop allowing it to be activated. 'This indicates that the license is in use on as many computers as the license will allow. We will check for this later. .Cells(5, 1).Value = "in_use" Else Dim oRequest As Object Const cUrl As String = "https://yoursite.com/?edd_action=activate_license&item_name=Name of the Download Product&license=" URL = cUrl & ThisWorkbook.Worksheets("Sheet1").Range("A1").Value Set oRequest = CreateObject("WinHttp.WinHttpRequest.5.1") oRequest.Open "GET", URL oRequest.Send 'When activating for the first time we need to activate the license and then use it, so we're basically sending the request twice. 'This step isn't necessary after the initial activation, so I suppose a check could be added to remove the redundancy. oRequest.Open "GET", URL oRequest.Send ThisWorkbook.Worksheets("Sheet1").Range("A2:A5").ClearContents ThisWorkbook.Worksheets("Sheet1").Range("A2").Value = oRequest.ResponseText ThisWorkbook.Worksheets("Sheet1").Range("A3").Formula = "=IF(MID($A$2,FIND(""license"",$A$2,1)+10,8)=""inactive"",""inactive"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,5)=""valid"",""valid"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,7)=""invalid"",""invalid"","""")))" ThisWorkbook.Worksheets("Sheet1").Range("A4").Formula = "=MID($A$2,FIND(""activations_left"",$A$2,1)+18,1)" End If End With End Sub |
To deactivate the license:
Sub httpRequestDeactivate() 'Use to deactivate license. Mostly the same as httpRequestActivate but without the check for if the max number of activations has been reached. Dim oRequest As Object Const cUrl As String = "https://yoursite.com/?edd_action=deactivate_license&item_name=Name of the Download Product&license=" URL = cUrl & ThisWorkbook.Worksheets("Sheet1").Range("A1").Value Set oRequest = CreateObject("WinHttp.WinHttpRequest.5.1") oRequest.Open "GET", URL oRequest.Send ThisWorkbook.Worksheets("Sheet1").Range("A2:A4").ClearContents ThisWorkbook.Worksheets("Sheet1").Range("A2").Value = oRequest.ResponseText ThisWorkbook.Worksheets("Sheet1").Range("A3").Formula = "=IF(MID($A$2,FIND(""license"",$A$2,1)+10,8)=""inactive"",""inactive"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,5)=""valid"",""valid"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,7)=""invalid"",""invalid"","""")))" ThisWorkbook.Worksheets("Sheet1").Range("A4").Formula = "=MID($A$2,FIND(""activations_left"",$A$2,1)+18,1)" End Sub |
Software Licensing for Excel Add-In Step 2:
Create a UserForm for your users to enter their license key. My UserForm consisted of a label instructing the user what they should do with the form, a text box for the license key to be entered into, two buttons (one to activate and another to deactivate the license), and a label with no caption that will be used to display whether or not a license was valid.
To set the Activate license button:
Private Sub LicenseKeyActivate_Click() 'license.Value is what the user entered into the text box on the UserForm. If license.Value = "" Then MsgBox "Please enter a license key.", vbExclamation, "Input Data" Exit Sub Else Application.ScreenUpdating = False With ThisWorkbook.Worksheets("Sheet1").Range("A1") .Cells(1, 1).Value = license.Value End With ThisWorkbook.Save httpRequestActivate Unload Me Application.ScreenUpdating = True httpRequestCheck If ThisWorkbook.Worksheets("Sheet1").Cells(5, 1).Value = "in_use" Then MsgBox "This license key is already in use. Please use another key or deactivate it on the other computer where it is being used." 'You should call a function to hide or disable your add-in here otherwise it can still be used until the user restarts Excel. Else With ThisWorkbook.Worksheets("Sheet1").Range("A1") If .Cells(3, 1).Value = "valid" And .Cells(4, 1).Value = 0 Then MsgBox "Your license key has been activated." 'Call your add-in here otherwise the user will need to close and re-open Excel. Else MsgBox "Your license key may not have been activated. Please close and restart Excel to re-check your license." End If End With End If End If End Sub |
To set the Deactivate license button:
Private Sub LicenseKeyDeactivate_Click() If license.Value = "" Then MsgBox "Please enter a license key.", vbExclamation, "Input Data" Exit Sub Else Application.ScreenUpdating = False With ThisWorkbook.Worksheets("Sheet1").Range("A1") .Cells(1, 1).Value = license.Value End With ThisWorkbook.Save httpRequestDeactivate Unload Me Application.ScreenUpdating = True httpRequestCheck With ThisWorkbook.Worksheets("Sheet1").Range("A1") If .Cells(4, 1).Value <> 0 Then MsgBox "Your license key has been deactivated. If you no longer intend to use this add-in on this computer please remove it from your list of Add-Ins to avoid license activation prompts at startup." 'You should call a function to hide or disable your add-in here otherwise it can still be used until the user restarts Excel. Else MsgBox "Your license key may not have been deactivated. Please close and restart Excel to re-check your license." 'You should call a function to hide or disable your add-in here otherwise it can still be used until the user restarts Excel. End If End With End If End Sub |
To grab the license data stored in the add-in:
Private Sub userform_initialize() With ThisWorkbook.Worksheets("Sheet1").Range("A1") license.Value = .Cells(1, 1).Value Label2.Caption = "Your license key is " & .Cells(3, 1).Value & " and has " & .Cells(4, 1).Value & " activations remaining." End With End Sub |
Software Licensing for Excel Add-In Step 3:
Finally, we need to check whether or not the license key has been activated and is valid when the add-in starts up. If it is we’ll show the add-in’s content and if not we’ll show the license activation UserForm we created earlier.
We need to tell the add-in how to show the UserForm we created. We can call this whenever it is needed.
Sub Activate_License() 'Show the license key activation UserForm (mine is creatively named LicenseKey) LicenseKey.Show End Sub |
When the add-in starts up we’ll tell it to run the httpRequestCheck that we set up earlier. Then we will need to check whether or not the license is valid and has not exceeded the max number of activations.
Private Sub Workbook_Open() httpRequestCheck With ThisWorkbook.Worksheets("Sheet1").Range("A1") ' the And .Cells(4, 1) = 0 is probably redundant, and if you are allowing more than one activation per license key you should remove that part. If .Cells(3, 1).Value = "valid" And .Cells(4, 1) = 0 And .Cells(5, 1) <> "in_use" Then 'call your add-in here Else 'if the license is not valid, the number of activations remaining is greater than 0, and it is not in use elsewhere we should display the license activation UserForm. Activate_License End If End With End Sub |
Software Licensing for Excel Add-In Step 4:
This step is optional. Since I only display the license UserForm when the add-in is first opened and never again if there is a valid license in use, I wanted to provide a way for the user to display the form again so they could deactivate the license if they choose to do so. My add-in creates a custom toolbar, so I added an icon to the toolbar that calls Activate_License. If you recall that contains the code to show my UserForm: LicenseKey.Show
.
Software Licensing for Excel Add-In Notes
This method is far from foolproof. If a customer really wanted to change this code around to disable the license checks they certainly could.
To prevent tampering with the code you could password protect your Excel Add-In code, but there are ways to crack those passwords. I won’t link to them here, but spend 5 minutes on Google searching for Excel password crackers and you’ll see what I mean.
If someone really wants to mess around and remove your license checks they probably can and there isn’t much you’re going to do about that. What this is designed to do is provide a way for the honest users of your hard work to validate that they purchased your code.
With that said, I have found that most of the time if someone is technically savvy enough to do all of that and cheap enough to not spring for the extra license they would likely create the add-in on their own. YMMV.
If this has helped you out, share a link to your software in the comments below. I’d love to check it out!
SCOTT ANDERS says
Thanks, Scott. I’ve been working on a very similar approach to an add-in I’ve developed. Since you implemented this, do you have any updates or additional advice?
Scott says
I haven’t had any updates since writing this article, although I have noticed since writing this that one downside does exist in the setup I described. The user needs to have an internet connection in order for the license to be verified at startup. I haven’t had a chance to update the license check to save the information for offline usage as well, though I’m sure it’s possible.
Istvan says
Very informative article. Thanks for sharing!
ihsan sahab says
thanks very much, improved
Roy says
You probably find an error using
Private Sub userform_initialize()
With ThisWorkbook.Worksheets(“Sheet1”).Range(“A1”)
license.Value = .Cells(1, 1).Value
Label2.Caption = “Your license key is ” & .Cells(3, 1).Value & ” and has ” & .Cells(4, 1).Value & ” activations remaining.”
End With
End Sub
It should be
Private Sub userform_initialize()
With ThisWorkbook.Worksheets(“Sheet1”)
license.Value = .Cells(1, 1).Value
Label2.Caption = “Your license key is ” & .Cells(3, 1).Value & ” and has ” & .Cells(4, 1).Value & ” activations remaining.”
End With
End Sub
Dakr says
Hi it’s been 4 years since this article was written. Any updates on how one might approach licensing Excel add ins? thanks.
Scott DeLuzio says
Honestly, I haven’t worked with Excel add-ins much since I first wrote this article. I would imagine that it would work much the same way as it had in the past. I don’t believe that anything introduced in either Excel or Easy Digital Downloads would prevent the methods described in this article from working. Have you attempted to implement it and had any problems?
Danesvar says
Hello,
I’m trying to license my add-in for sale without having to save the details into a sheet,
Any advice on how I should edit my code below?
Thank you
Option Explicit
‘ Global variables to store the activation status, product ID, and API key
Dim IsActivated As Boolean
Dim ProductID As String
Dim APIKey As String
‘ Set the product ID and API key constants
Const YOUR_PRODUCT_ID As String = “YOUR_PRODUCT_ID”
Const YOUR_API_KEY As String = “YOUR_API_KEY”
Sub SetupAddIn()
‘ Set the product ID and API key directly in the code
ProductID = YOUR_PRODUCT_ID
APIKey = YOUR_API_KEY
‘ Save the entered product ID and API key securely
SaveToConfigFile “ProductID”, ProductID
SaveToConfigFile “APIKey”, APIKey
End Sub
Function CheckLicenseKeyActivation() As Boolean
If Not IsActivated Then
Dim licenseKey As String
licenseKey = InputBox(“Enter your license key:”)
If IsValidLicenseKey(licenseKey) Then
IsActivated = True
SaveActivationStatus IsActivated
MsgBox “License activation successful. You can now use the add-in.”
Else
MsgBox “Invalid license key. Please contact support for assistance.”
End If
End If
‘ Return the activation status
CheckLicenseKeyActivation = IsActivated
End Function
Function IsValidLicenseKey(key As String) As Boolean
‘ Use the stored ProductID and APIKey
Dim url As String
Dim response As String
Dim json As Object
‘ Use the constants for the product ID and API key
url = “https://your-licensing-server.com/verify?product_id=” & YOUR_PRODUCT_ID & “&license_key=” & key
response = GetHTTPResponse(url, YOUR_API_KEY)
On Error Resume Next
Set json = CreateObject(“MSScriptControl.ScriptControl”)
json.Language = “JScript”
json.ExecuteStatement “var result = ” & response
On Error GoTo 0
‘ Check if the “success” property exists and is true
On Error Resume Next
Dim successValue As Variant
successValue = json(“success”)
On Error GoTo 0
If VarType(successValue) = vbBoolean Then
‘ “success” property exists and is a boolean
IsValidLicenseKey = successValue
Else
‘ “success” property does not exist or is not a boolean
IsValidLicenseKey = False
End If
End Function
Function GetHTTPResponse(url As String, api_key As String) As String
Dim http As Object
Set http = CreateObject(“MSXML2.XMLHTTP”)
http.Open “GET”, url, False
http.setRequestHeader “Authorization”, “Bearer ” & api_key
http.send
GetHTTPResponse = http.responseText
End Function
Sub SaveActivationStatus(activated As Boolean)
‘ Save activation status to a secure configuration file
SaveToConfigFile “ActivationStatus”, CStr(activated)
End Sub
Function LoadActivationStatus() As Boolean
‘ Load activation status from the secure configuration file
LoadActivationStatus = CBool(LoadFromConfigFile(“ActivationStatus”))
End Function
Sub SaveToConfigFile(key As String, value As String)
Dim filePath As String
‘ Prompt the user to choose the file path
filePath = Application.GetSaveAsFilename(“Save As”, FileFilter:=”Text Files (*.txt), *.txt”)
If filePath “False” Then
‘ Save the key and value to the chosen file path
‘ You would need to implement the actual saving mechanism here
‘ For example, using File I/O or other suitable methods
Else
MsgBox “Saving canceled by the user.”, vbExclamation
End If
End Sub
Function LoadFromConfigFile(key As String) As String
‘ Load the value from a secure storage mechanism
‘ For simplicity, you can use the Windows Registry or a more secure approach
‘ …
‘ For example, loading from Windows Registry:
On Error Resume Next
LoadFromConfigFile = VBA.Interaction.GetSetting(“YourCompanyName”, “YourAddInName”, key, “”)
On Error GoTo 0
End Function
Scott DeLuzio says
You’re asking for advice on how to edit the code, but I need to know what you’re trying to accomplish with the edit. Does the code work now, but you’re looking to optimize it? Does the code not work, and are you trying to figure out why? Something else?
Honestly, it’s been years since I’ve worked with Excel add-ins, and I haven’t touched anything remotely close to this type of licensing in a long time. This post only exists to guide people to a solution to license their Excel add-ins, but I’m no expert in implementing it. The best way to figure out something like this is trial and error – that’s how I got the code introduced in this post.