We're sorry to say that we cannot continue development for internal reasons. This repository is now archived.
This free, open-source .NET library allows you to license your non-free applications through activation keys.
Free. Easy to use. No license files. Everything is XML-documented in the code, so IntelliSense can show you all info about any member.
If you want to know about all aspects of using this library, or you want to test it, consider the directory Licensing.ActivationKeys.Tests
containing the unit tests created for this library.
The easiest way to install this library is using NuGet. Right-click on your project name is Visual Studio's Solution Explorer, select option "Manage NuGet packages", find package "SNBS.Licensing.ActivationKeys" and install it. Or use the Install-Package
command:
Install-Package SNBS.Licensing.ActivationKeys
You can also clone this repository (or download it using the green "Code" button), compile it and add a reference to the compiled assembly in your project.
You can test this library by using the ready code examples in folder examples
. Download Admin.cs
and Client.cs
, compile them into console apps and:
- Run
Admin.cs
and create a license using its CLI. - Take down the key of the created license.
- Apply it using
Client.cs
. - View the current license using
Client.cs
. It will display the parameters you initially entered inAdmin.cs
's CLI.
You don't even need to set up a database. The examples connect to my own one. It's good for quick start, but for commercial use you'd better host a database yourself, e.g. on FreeSQLDatabase. Not an advertisement.
Say, we have an app that isn't completely free, and we want to sell licenses for it and activate it through activation keys.
-
First, we need a database to store licenses. This library supports MS SQL Server and MySQL. You may use any database hosting from Windows Azure to FreeSQLDatabase (uses MySQL 5.0.12). Not an advertisement. After creating your database will of course be empty, but this library will automatically set it up. Get a connection string and go to the next step.
-
Then we need to start a
LicensingClient
in the main method. It will decide whether to run the full app version or a message "Not licensed". The decision will depend on the license configuration in registry (see below how to configure a license easily) and on its usability (LicensingClient
looks up in the database to find out whether the configured activation key is valid). Please note thatLicensingClient
opens a registry key in the constructor, and thus it needs admin permissions. If they aren't provided, aRegistryAccessException
will be thrown. The inaccessible registry key will be stored in the exception data under keyInaccessibleRegistryKey
.
using SNBS.Licensing;
// ...
public static void Main(string[] args) {
LicensingClient.Start(
"YourConnectionString", "YourProductName", false, null,
client => {
// Start the full version
},
(client, usability) => {
// What you do when the app isn't licensed
}
);
}
-
Let's analyze this code. Method
Start
is static. In the first parameter, it takes the connection string to your database. Please note that if the connection string provided is invalid, or the database structure is invalid (the valid structure is above), aDatabaseException
will be thrown. In the second parameter you pass the name of your project — it is used to store the license information in the registry (licenses for different products store in different places). -
The third parameter is of type
bool
. It specifies whether theLicensingClient
should try to connect to MySQL (if it'sfalse
, the client will try to connect to MS SQL Server). If it'strue
, you should also set the fourth parameter (of typeVersion?
) to the version of MySQL. If MS SQL Server is used, this parameter should benull
. If the third parameter istrue
, but the fourth one isnull
, anArgumentException
is thrown. -
The third parameter has type
Action<LicensingClient>
and is ran when your product has a valid license. TheLicensingClient
instance passed to it can be used to fetch the license, reactivate/deactivate your product and validate activation keys (without using them). -
The fourth parameter has type
Action<LicensingClient, LicenseUsability>
and is ran where there's no license or an invalid license (configured in the registry for the current product). TheLicensingClient
passed can be used for the same things as described in paragraph 4.LicenseUsability
is an enumeration describing reasons why a license is usable/not usable. Its values are:Usable
,Expired
,NotFound
,TooManyDevices
(each license can be used by a limited number of devices, set when it was created) andNoConfiguredLicense
. They should be intuitive. (The difference betweenNotFound
andNoConfiguredLicense
—NotFound
means a license is configured, but it doesn't exist in the license database.NoConfiguredLicense
means there's no license at all.) Note that the valueNoConfiguredLicense
cannot be returned by any method, exceptGetCurrentLicense()
, and valueTooManyDevices
can only be returned by methodActivateProduct
(see below).
This was the most common usage of the library, but there are other ways, e.g. you can create a LicensingClient
yourself (specify connection string, product name, use MySQL or not and the version of MySQL, as in the previous example):
using (var client = new LicensingClient("YourConnectionString", "YourProductName", false, null)) {
var usability = client.GetCurrentLicense().Usability;
if (usability != LicenseUsability.Usable) {
ShowMessage("Your license " +
(usability == LicenseUsability.Expired) ? "has expired" :
(usability == LicenseUsability.NotFound) ? "was canceled" :
(usability == LicenseUsability.NoConfiguredLicense) ? "configuration was corrupted" :
"was corrupted");
}
}
When you create a LicensingClient
using the constructor, it automatically connects to the licenses database. Method GetCurrentLicense()
retrieves the currently used activation key (stored in the registry) and looks up in the database to verify it. The returned type is structure LicenseInfo
. It should be obvious that it contains detailed information about one license. Its properties are:
Key
of typestring?
;Expiration
of typeDateTime?
(only date is stored,DateTime
instead ofDateOnly
was used because of the Entity Framework's mapping mechanism);Type
of typeLicenseType?
(enumeration containing valuesTrial
,General
,Professional
);Usability
of typeLicenseUsability
(all other properties will benull
if this one isn't equal toLicenseUsability.Usable
).
Let's improve the previous example. Generally, applications should ask the end user to activate them if the current license is not usable. The corresponding method of LicensingClient
is called ActivateProduct()
. It returns LicenseInfo
containing the information about the newly activated license (of course, it's activated only if it's usable).
Ignore warnings about null
references when using properties of LicenseInfo
. If you're sure the Usability
property equals to LicenseUsability.Usable
, you can safely suppress them using pragma
.
using (var client = new LicensingClient("YourConnectionString", "YourProductName")) {
var usability = client.GetCurrentLicense().Usability;
if (usability != LicenseUsability.Usable) {
ShowMessage("Your license " +
(usability == LicenseUsability.Expired) ? "has expired" :
(usability == LicenseUsability.NotFound) ? "was canceled" :
(usability == LicenseUsability.NoConfiguredLicense) ? "configuration was corrupted" :
"was corrupted");
string key = AskUser("Enter an activation key");
var info = client.ActivateProduct(key);
if (info.Usability == LicenseUsability.Usable) {
ShowMessage("License successfully activated! Expires at " + info.Expiration?.ToShortDateString());
} else {
ShowMessage("An error occurred when trying to activate. The license " +
(info.Usability == LicenseUsability.Expired) ? "has expired" :
(info.Usability == LicenseUsability.NotFound) ? "was canceled" :
(info.Usability == LicenseUsability.TooManyDevices) ? "was used by too many devices" :
"was corrupted");
}
}
}
There are other (non-common used) members documented as XML in the code. (See also the unit tests from directory Licensing.ActivationKeys.Tests
. They are a good documentation.)
LicenseValidator
can be used to retrieve information (LicenseInfo
) about a license, without trying to apply it on the current device. Its only method is ValidateLicense
(except methods of System.Object
).
The usage isn't complicated.
- Let's validate a license. I will use MySQL here, to show this feature:
using (var validator = new LicenseValidator("Server=sql7.freesqldatabase.com; Database=sql7594998; Uid=sql7594998; Pwd=l2TZZAQ5hB", true,
new Version(5, 0, 12))) {
var info = validator.ValidateLicense("AAAAA-AAAAA-AAAAA-AAAAA-AAAAA");
if (info.Usability == LicenseUsability.Usable) {
ShowMessage("This license is valid");
}
}
-
If you run the code above, it will connect to my own database hosted on FreeSQLDatabase. Not an advertisement. This service is used because it's free and easy to use. If you have your own database hosted there, replace values in the connection string with the values in email they will send you.
-
LicenseValidator
doesn't deal with registry, so admin permissions aren't needed. -
The constructor takes connection string to licenses database, a
bool
value telling whether to use MySQL and the version of MySQL (if it is used), just like theLicensingClient
constructor, but without specifying product name. -
Method
ValidateLicense
takes a license key in its only argument and returnsLicenseInfo
representing that license.
It's good when we can validate and apply licenses, but a class that would perform CRUD (Create, Read, Update, Delete) operations is also needed. It is LicensingAdmin
. Its instances can be created just like LicensingClient
instances, but without specifying product name and without a Start
method.
- Let's create a license.
using (var admin = new LicensingAdmin("YourConnectionString", false, null)) {
var info = admin.CreateLicense(DateTime.Today.AddDays(20), LicenseType.Trial, 1);
ShowMessage("The newly created license is " + info.Key);
}
-
Analysis. Method
CreateLicense()
receives three parameters. The first one is the type of the needed license (a value of theLicenseType
enumeration). The second one is aDateTime
object representing the expiration date. The third one is the maximum number of devices (short
) that can use the license. -
The returned object is
LicenseInfo
representing the new license. The most common use in this case is taking the (randomly generated) key of the new license.
Of course, LicensingAdmin
can also update and delete licenses.
using (var admin = new LicensingAdmin("YourConnectionString", false, null)) {
var info = admin.UpdateLicense("AAAAA-AAAAA-AAAAA-AAAAA-AAAAA", null, LicenseType.Professional, 10);
ShowMessage("The license " + info.Key + " is now of type " + info.Type.ToString() + " and can be used by (maximum) " + info.MaxDevices.ToString() + " devices.");
}
Method UpdateLicense
receives four arguments. The first one is key of the license to update.
The second, third and fourth parameters are, correspondingly, DateTime?
(expiration), LicenseType?
(type) and short?
(maximum number of devices using the license). They are the new license parameters. If you want to leave a parameter as it was, pass null
in the corresponding argument.
The returned type is LicenseInfo
that stores updated information about the license.
Let's imagine we no longer need the updated license. It should be deleted:
using (var admin = new LicensingAdmin("YourConnectionString", false, null)) {
var info = admin.DeleteLicense("AAAAA-AAAAA-AAAAA-AAAAA-AAAAA");
ShowMessage("The license " + info.Key + " was deleted.");
}
The only argument is key of the license to delete. The returned type is LicenseInfo
storing information about the deleted license.
Classes LicensingClient
, LicenseValidator
and LicensingAdmin
deal with the same database, but offer different functions. That's why I've defined explicit casts between them:
using (var admin = new LicensingAdmin("YourConnectionString", false, null)) {
// Use it
var validator = (LicenseValidator)admin;
var info = validator.ValidateLicense("AAAAA-AAAAA-AAAAA-AAAAA-AAAAA");
}
You can convert the mentioned classes to each other however you like, but you cannot convert LicenseValidator
to LicensingClient
and LicensingAdmin
to LicensingClient
(because LicensingClient
needs product name for creation, which cannot be taken from other classes). But you can use LicensingClient
constructors for that:
var admin = new LicensingAdmin("YourConnectionString", false, null);
var client = new LicensingClient(admin, "YourProductName");
The code above will retrieve the connection string and information about usage of MySQL from the LicensingAdmin
instance (you can also pass a LicenseValidator
instance).
It is very important to dispose LicensingClient
, LicenseValidator
and LicensingAdmin
after use because the connection to the licenses database is closed only when Dispose()
is called. As the mentioned classes implement IDisposable
, you can use the using
operator instead of calling Dispose()
yourself.
All methods of LicensingClient
, LicenseValidator
and LicensingAdmin
that access the licenses database have TAP-asynchronous versions (they have suffix Async
and return Task
or Task<TResult>
). You may use them like this:
using (var client = new LicensingClient("YourConnectionString", "YourProductName", false, null)) {
var info = await client.ActivateProductAsync("AAAAA-AAAAA-AAAAA-AAAAA-AAAAA");
}
The unit tests need an empty LocalDB database. First, let this library create it, then navigate to folder Licensing.ActivationKeys.Tests/Queries
in this repository and run query PopulateSQLServer.sql
in it to your database if your DBMS is MS SQL Server. If it's MySQL, run query PopulateMySQL.sql
in the same folder. Follow instructions in comments in the tests!