Channels ▼
RSS

Licensing in .NET


Licensing in .NET

Download the code for this issue

The Microsoft .NET Framework defines a licensing model that allows developers to prevent the use of their classes unless a valid license can be granted. This licensing model is particularly well suited to redistributable components, which are typically sold to developers and subsequently redistributed as a part of the licensed developer's application. Noncomponent classes can easily take advantage of the infrastructure as well.

In this article, we'll enable licensing support for a simple redistributable component. We'll investigate the .NET Framework components involved in licensing, and see how Visual Studio .NET automates much of the licensing process for components. Finally, we'll write code enabling custom license key validation, as well as support for evaluation licenses.

Licensing a Component

Using the .NET licensing model, building a component that requires a valid license key to be present before it can be used is straightforward. You simply add a licensing-specific attribute to your class definition, and add a call to a license validation method from your class constructor.

Listing 1 shows the definition of perhaps the simplest licensed component you can have. The constructor makes a call to LicenseManager.Validate, which asks the licensing framework to request a valid license for the class. The down-and-dirty work of finding and validating a license key, and subsequently granting a license, is done by a component called a "license provider." Microsoft supplies a simple license provider, LicFileLicenseProvider, which we associate with the component in this example using the [LicenseProvider] attribute on the class. We'll look at what's involved with creating custom license providers shortly.

If the license validation is successful, Validate simply returns and allows the component to be created and used normally. If, however, a license cannot be granted, Validate raises a LicenseException exception, preventing the component from being used.

If you build the component given in Listing 1 and then attempt to add it to a host application (by referencing its assembly, adding it to the Visual Studio Toolbox, and dropping it onto the design surface), you'll get the message box shown in Figure 1. As the authors of the licensed component, this is the behavior that we want: Our class requires that a license be granted before it is used, but we haven't yet provided any evidence that one should be granted. Technically, what has happened is that LicFileLicenseProvider could not find a valid license key for our component, so a LicenseException was raised inside the LicenseManager.Validate method.

License providers such as LicFileLicenseProvider grant licenses only when a valid license key can be located. A license key is a string, and may be a serial number, an installation code, or any other string recognized by the license provider. Locating a license key is the responsibility of the license provider, and it may do so in whatever manner it chooses. In the case of LicFileLicenseProvider, a simple text file is expected to exist for each licensed class, containing nothing but the license key string. The text file needs to have the name <classname>.lic and be located in the same directory as the licensed class's assembly (<classname> is the namespace-qualified name of the licensed class). Other license providers could obtain license keys from the registry, a web service method, or however else they might choose.

Validating a license key is the license provider's primary responsibility. LicFileLicenseProvider isn't very sophisticated when it comes to this: A valid license key is simply a string of the form "<classname> is a licensed component." We'll see how to override this validation logic soon.

Listing 2 shows the license key (.lic) file for SimpleLicensedComponent, which contains just one line:

SimpleLicensedComponent is a licensed component.

Placing this file in the directory containing our simple component's assembly allows it to be used normally, just as if it weren't licensed. The host application does not need to do anything special to create the licensed class — the licensing framework handles it all automatically. (Note that during development of a licensed component, you'll want to have the .lic file for your component in your project's obj\Debug directory, since that is where the component's assembly lives. Place another copy in the obj\Release directory if you're testing release builds of the component as well.)

When the host application is compiled and distributed to another computer, the license key file is not copied along with the component's assembly. You might expect that this would result in a licensing error when the application attempts to create the component at run time. Instead, the component is created successfully. The .NET licensing framework, the license provider, Visual Studio .NET, and a utility called the "license compiler" all work together during the build process to "remember" that a license had been granted for the component at design time.

The License Compiler

The .NET Framework SDK ships with a utility called the "license compiler" (lc.exe), which simplifies the distribution of applications that use licensed classes. The license compiler embeds the required license keys into a host application's assembly so that they can be found on whatever machine the host application is run. This obviates the need to distribute license key files with the application.

The license compiler is invoked after a host application has been built. It is provided with the name of the host application's assembly, and a list of the licensed classes that the application uses. The license compiler creates an instance of each of these licensed classes and, in cooperation with the classes' license providers, extracts a run-time license key from each.

The license keys obtained by the license compiler are written to a binary file named <assembly>.licenses, where <assembly> is the name of the host application assembly. This .licenses file is then embedded as a resource in the host application's assembly using the services of the .NET assembly linker (al.exe).

It turns out that the plumbing necessary to allow the license compiler access to the license keys is quite involved. Suffice it to say that the license provider is required to call a special method exposed by the license compiler when invoked at design time. LicFileLicenseProvider handles this automatically when it (or a class derived from it) is used. Later we'll see an example that needs to deal with this directly.

If you use Visual Studio .NET, you typically don't need to interact directly with the license compiler. That's because Visual Studio automatically tracks the use of licensed components and invokes the license compiler seamlessly during the build process. You can see this by adding a licensed component to your project by dragging it to the designer surface. Visual Studio adds the name of the component class to a file named "licenses.licx" in the host application's project directory (enable the "Show All Files" option in the Solution Explorer to see licenses.licx listed). The classes listed in licenses.licx are passed to the license compiler during the build.

Note that if you create an instance of a licensed class without using the Visual Studio designer (i.e., if the licensed class is not a component, or it is otherwise created nonvisually), you'll need to manually add that class name to the licenses.licx file. If you're not using Visual Studio .NET for development, you'll need to maintain your own licenses.licx file and invoke lc.exe manually as part of your build process. See the .NET Framework SDK documentation in this case for more detailed information on using the license compiler.

Deriving New License Providers

LicFileLicenseProvider is not a very sophisticated license provider, but it is the only one supplied by the .NET Framework. Fortunately, it's easy to derive a new class from LicFileLicenseProvider that overrides the default license key validation logic. It's also possible to write a license provider from scratch, which allows for considerable flexibility in implementing sophisticated licensing schemes.

When deriving a new license provider from LicFileLicenseProvider, you must override both the IsKeyValid and GetKey methods. IsKeyValid is called to validate a license key string that the base class has located. GetKey is called at design time to get the license key string that will be remembered for validation at run time. Listing 3 shows the definition of DerivedLicenseProvider, which demonstrates overriding these methods.

In DerivedLicenseProvider, we've replaced the base class's implementation of IsKeyValid with logic that expects a license string to be composed of two parts: the licensed class's full name and the hash code of a secret string appended to the full name. While far from secure, this scheme makes valid license keys a little less obvious from the outside. GetValidLicenseKey contains the code to generate a valid license key for a class of a given type.

When deriving from LicFileLicenseProvider, overriding GetKey is the method by which you provide (at design time) the license key that will be remembered for use at run time.

You might wonder why it's necessary to implement GetKey. After all, the base class is the one that found the license key in the first place, so can't it just remember it itself? The reason seems to be that while in most cases design-time and run-time license keys will be the same, it's not a strict requirement. You might want to implement GetKey such that the remembered license key contains not just a serial number for your component, but also information about the licensed user, version information from the development machine, or something else. Because the license key string is remembered as a resource embedded in the host application's assembly, inspecting an application that uses your component could reveal the identity of the licensee.

In any case, you must override GetKey when deriving from LicFileLicenseProvider. Otherwise, the default base class implementation will return "xyz is a licensed component," which will probably not be recognized as valid by your custom validation logic.

After building our derived provider, we can test it by changing the [LicenseProvider] attribute on our SimpleLicensedComponent:

 [LicenseProvider(
  typeof(DerivedLicenseProvider))]
public class SimpleLicensedComponent ...

Because license providers run at design time in the context of Visual Studio, debugging is a little awkward. To do it, load a solution containing your license provider, a component licensed with it, and a host application (i.e., a Windows Forms application) for your component in Visual Studio. Next, open a second instance of Visual Studio and debug the first by using Tools | Debug Processes... from the main menu. You should now be able to set breakpoints at convenient places in your license provider (i.e., at IsKeyValid), which should be triggered when you drag the licensed component onto the design surface of the test host application.

Note that Visual Studio will need to be able to find the license provider's assembly at design time. You'll save yourself a lot of hassles if you include a custom license provider in the same assembly as the class(es) it licenses.

Creating a License Provider from Scratch

Sometimes deriving from LicFileLicenseProvider isn't adequate for implementing the licensing scheme you want to use. For example, if you want to use a registry value to hold your component's license key instead of a license file, you're out of luck with LicFileLicenseProvider. Implementing your own license provider from scratch isn't terribly difficult, but you do have to deal with several issues that we've so far avoided by using LicFileLicenseProvider.

A custom license provider must derive from the abstract System.ComponentModel.LicenseProvider class. This class defines just a single method, GetLicense, which needs to be implemented by the derived class. GetLicense is given information about the class for which a license is being requested and the context of the call, and responds by granting a license to the caller.

GetLicense has the following signature:

License GetLicense(
 LicenseContext context, Type type, 
 object instance, bool allowExceptions
 )

The type and instance parameters refer to the object for which a license is being requested. They are the parameters passed to LicenseManager.Validate by the licensed object itself. allowExceptions specifies whether the routine should raise an exception when a failure to grant a license occurs (as opposed to just returning NULL). The context parameter is more complicated. It refers to a LicenseContext object that provides information about whether the class is being created at design time or at run time, and provides utility methods that can be used by the provider.

The return value is of type System.ComponentModel.License, which is the abstract class from which all licenses must be derived. A minimal implementation of a License class must provide a Dispose method and a read-only LicenseKey property, but other properties and methods can often be useful. We'll see this in a moment.

Listing 4 shows a base class for a custom license provider that breaks down the implementation of GetLicense into three steps. First, a license key is located. Second, the found license key is validated. Finally, a License object is created and returned to the caller. GetLicense in CustomLicenseProvider coordinates these three steps, each of which is implemented by one of the virtual methods GetDesignTimeLicenseKey, IsLicenseKeyValid, and CreateLicense.

CustomLicenseProvider is implemented as an abstract base class in order to deal with the intricacies of design time versus run time, raising exceptions when appropriate and so forth, without dictating the policy for locating, validating, and granting licenses. It thus serves as a more convenient base class than LicenseProvider, and a more flexible one than LicFileLicenseProvider.

The first part of CustomLicenseProvider.GetLicense deals with locating a license key. At design time, a license key should be retrieved from a file, registry key, or other source on the development machine as implemented by GetDesignTimeLicenseKey. The license key obtained by this method at design time is saved away for run-time use by the SetSavedLicenseKey method of the context parameter.

At design time, the context parameter refers to an object supplied by Visual Studio .NET. This object implements SetSavedLicenseKey in such a way as to facilitate the creation of the licenses.licx file and the <assembly>.licenses file that is eventually added as an assembly resource. The context object has a private System.Collections .Hashtable containing all of the licenses that have been saved with SetSavedLicenseKey, and it is simply by serializing this hash table to disk that the <assembly>.licenses file is created.

When GetLicense is called at run time, the context parameter is supplied by the .NET run time. Instead of using GetDesignTimeLicenseKey to find the license key on the development machine, our custom license provider uses context.GetSavedLicenseKey. GetSavedLicenseKey retrieves the license key from the hash table that the run-time context had previously deserialized from the assembly resource. Note that the second parameter to GetSavedLicenseKey is NULL to specify that the entry assembly (that is, the assembly used to start the application) should be used; it appears that specifying another assembly for this parameter is not supported.

Despite the underlying complexity of license key serialization, CustomLicenseProvider implements its share of the work with just a few lines of code. Using the UsageMode property of the LicenseContext to indicate whether we are being invoked at design time or run time, the license key is retrieved from the appropriate location:

switch ( context.UsageMode )
{
case LicenseUsageMode.Designtime:
  licenseKey = 
    GetDesignTimeLicenseKey(type);
  context.SetSavedLicenseKey(
    type, licenseKey );
  break;
case LicenseUsageMode.Runtime:
  licenseKey = 
    context.GetSavedLicenseKey( 
    type, null );
  break;
}

The second step of GetLicense is validation of the license key. This is done using IsLicenseKeyValid, which serves essentially the same purpose as the IsKeyValid method of LicFileLicenseProvider.

The third and final step of GetLicense is the actual granting of a license. When we derived from LicFileLicenseProvider, the License object representing the granted license was created by the base class, and we didn't have to deal with it. Here, we must rely on the CreateLicense method to create an appropriate license object given a class type and license key.

Note that at each error path in GetLicense, the allowExceptions parameter is consulted to determine whether the routine should return NULL or raise a LicenseException. While I've never seen a situation where allowExceptions is False, the documentation indicates that exceptions should not be raised if it is. Regardless, returning NULL results in the license manager raising a LicenseException.

Enabling Evaluation Support

Let's take the base class for the custom license provider shown in Listing 4 and derive a new provider that has support for evaluation license keys. Evaluation license keys allow a class to be used, but may degrade its functionality in some way to encourage the purchasing of a normal license for the class.

There are three methods we need to override when we derive from CustomLicenseProvider. For this new provider, I've implemented GetDesignTimeLicenseKey to consult a registry value based on the name of the type being licensed. IsLicenseKeyValid uses the same simple hash function as our implementation of IsKeyValid in the provider we derived from LicFileLicenseProvider, except that a string Evaluation will be considered valid as well.

To implement CreateLicense, we need to create an appropriate license class to represent a granted license. Listing 5 shows the class that we use with our evaluation license provider. The base class requires that we implement the read-only LicenseKey property and the Dispose method, while the IsEvaluation property is new. IsEvaluation is set to True when the located license key string is Evaluation. Listing 6 shows the complete evaluation license provider.

In order for a licensed class to behave differently based on whether a normal or evaluation license has been granted, it needs some way to get at the License object returned from GetLicense. This is done using an overload of LicenseManager.Validate. If you pass both the class type and an object instance (this) to Validate, you'll get back the same License object that was returned from GetLicense.

Listing 7 shows a simple component licensed with our evaluation license provider. TryAndBuyComponent uses the IsEvaluation property of the CustomLicense returned from LicenseManager.Validate to show an "evaluation mode" label when an evaluation license key has been used. Another component might disable some of its functionality or otherwise change its behavior in this case. Because License derives from IDisposable, TryAndBuyComponent calls Dispose when it is done using it.

Using this same technique, you could allow the full version of your commercial component to be widely distributed, packaged with an evaluation license to degrade its functionality in some way. When a developer tries the evaluation version of your component and decides to purchase it, you simply give them a nonevaluation license key that eliminates the evaluation restrictions. In this way, you don't need to maintain multiple distribution packages for your product.

A more sophisticated licensing scheme that allows for individual features to be enabled or disabled based on the license key used could be built. By adding properties to the License object used, the license provider can effectively tell the licensed component which features should be allowed.

Conclusion

Licensing classes in .NET has significant support from the Framework and Visual Studio. While the out-of-the-box licensing support is rather restrictive, it is extensible enough to allow for considerable flexibility in implementing sophisticated licensing schemes. w::d


Myk Willis is President of Wanderlust Software LLC, publisher of the licX Licensing Component for .NET. He can be reached through the company web site at http:// www.wanderlust-software.com/.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Comments:

Video