Implementing Custom Licensing

The flexibility of the .NET Framework licensing support enables you to build in any kind of licensing that you want, so long as your code supports the notion of being able to return a Boolean that indicates whether or not a control is licensed.

The licensing system actually works in two different modes: at design time when you are working in the Visual Studio .NET IDE, and at runtime when the application is active. The .NET Framework designers were aware of the fact that the need for validating licenses is different at design time than it is at runtime and built consideration for that into the code. For example, you might have an ASP.NET control that validates licenses at runtime via a web service call. You don't want to be making web service calls every time you look at the control in a designer, so a more static design-time system can be used to validate the control within the IDE.

Introduction to the License Provider and License Manager

The core of .NET's licensing scheme centers around the LicenseManager class. Even if you have never written a line of code that deals with licensing, you can be sure that your code is being run through the LicenseManager class. Each time a class is instantiated in the .NET Framework, the LicenseManager is invoked to make sure that the class has a valid license. By default, classes don't implement licensing and so are considered always valid.

To mark a class (which can be a standard class, a Web Forms control, or a Windows Forms control) as one that requires some kind of licensing, you mark it with the LicenseProvider attribute. The LicenseProviderAttribute custom attribute class indicates the custom license provider class that will be responsible for determining whether the class is licensed at runtime and/or design time.

The LicenseProvider class is a class from which you inherit to create your own licensing scheme. It provides a method that is used by the license manager to determine validity of the license for the class GetLicense. GetLicense obtains an instance of the License class.

When you create your own license provider, you will probably also be creating your own license class. For example, if you are implementing a license scheme in which the users purchase licenses on a per-CPU basis, your license class must contain a property that indicates the number of CPUs for which it has been licensed. The only trick to this is that all licenses are derived from keys, which are strings that can contain anything, including encrypted data. Therefore, if you need to store the number of CPUs for which a particular license was purchased, you have to come up with a way of storing that in the string. Don't worry about coming up with complex algorithms. As you'll see in the samples that follow, the string can be as secure as you like. Also, because you have control over the verification process at both design time and runtime, you can use a more secure runtime validation and a less secure design-time validation to make development easier and more convenient for your customers.

The basic process for licensing is as follows. At design time, when the class is instantiated for display in the IDE, the license is queried. The license provider can do anything it wants in order to obtain a license, whether that be looking at a file on disk, querying a web service, or some other means of verification. In the case of the LicFileLicenseProvider (the only one that ships with the .NET Framework), the license obtained from the .lic file, if valid, is embedded into the assembly when it is compiled. At runtime, the key for the license is extracted from the assembly (if there is a license embedded) and checked for validity. For ASP.NET applications, the runtime license is stored in a binary file on disk that will be loaded when needed.

If you use Visual Studio .NET, the license key embedding is taken care of automatically for you at build time. If you are creating your assemblies manually, you will need to use the LC.EXE tool. This command-line tool reads a text file that contains a list of licensed classes used for a particular application and creates a binary .licenses file. This file can then be embedded into an assembly using the Assembly Linker tool (AL.EXE).

The next few sections will take you through the process of creating a licensed control, including describing the usage scenario and showing you the code required to use the control in an application.

The fictitious scenario is this: The Acme Stocks company provides live, real-time stock quotes to its customers. In addition, the company has created a Windows Forms control called StockLabel. This control dynamically displays the price of a stock on a Windows Form (presumably by obtaining that price from the Acme Stocks web service). To use this control, you need to purchase it from the Acme Stocks company.

Because stock price information is very sensitive, especially real-time data such as that provided by Acme Stocks, the vendor needs to make sure that only the appropriate people can use the control. To accomplish this, the company has implemented .NET licensing for its control.

At design time, the control displays some fixed information about a bogus stock that doesn't exist. This gives the developer the ability to lay out the control but the design-time environment doesn't need to be connected to the Internet. At runtime, however, the control makes use of the web service to obtain the stock quote. It also makes use of a web service to validate that the form containing the control has been authorized for use. When a developer purchases this control (or subscribes, pays in advance for 30 days, and so on), she provides Acme Stocks with the GUID (globally unique identifier) that belongs to the type that will contain the control. In this chapter's example, the type is a form, but it could be a panel or any other container.

At runtime, this GUID is detected by the stock quote control and sent to the validation web service. If the validation web service indicates that the GUID is still valid, the control allows itself to be executed. Otherwise, the request for a license fails and the application is unable to continue until the control has been properly licensed or removed from the form. The examples that follow will show you how to implement this model.

Creating a License

Creating a license is fairly simple. The first step is to create a class that inherits from the System.ComponentModel.License class and provide the appropriate overrides. If you want to maintain additional information about the license, such as number of processors allowed and so on, you could make that data properties of the license and extract it from whatever key you use. Listing 37.1 shows the very simple AcmeStockLicense.

Listing 37.1. The Acme Stock Company's License Class Implementation
 using System; using System.ComponentModel; using System.Collections; using System.Collections.Specialized; using System.IO; using System.Diagnostics; namespace StockLibrary {   /// <summary>   /// Summary description for AcmeStockLicense.   /// </summary>   public class AcmeStockLicense : License   {     private string licenseKey = string.Empty;     public AcmeStockLicense(string key)     {       licenseKey = key;     }     public override void Dispose()     {       // nothing needs disposing of     }     public override string LicenseKey     {       get       {          return licenseKey;       }     }   } } 

As you can see, there is nothing all that exciting about this class. It is a simple property container for the license key that implements the right overrides from the abstract license class. If complex logic on the license itself were needed for encrypted license keys, it could be added to this class.

Creating a License Provider

As mentioned earlier, the LicenseProvider class is just a vehicle for the Common Language Runtime's LicenseManager class to obtain a license for a given class. Each time a class is initialized and the Validate method is invoked, the Common Language Runtime looks to see whether it has a license provider defined. If so, that license provider has its GetLicense method invoked. If an exception occurs or the license returned is null, the control (or class) is considered unlicensed. If the license is returned, the Common Language Runtime assumes that everything is okay.

To create a LicenseProvider class, create a class that inherits from LicenseProvider and overrides the appropriate methods. In the case of the Acme Stocks fictitious sample, the license provider being built needs to use a web service to validate the container type's GUID.

This introduces an element of instability in the control. For example, if there is no network connection, the control will fail to authorize itself and obtain a license. One way to mitigate this involves creating a method of license caching that isn't illustrated in this chapter. However, by using other techniques described in this book, you can extend this provider to contain license caching.

But for this sample control, the instability isn't of any concern. The control won't display its data unless it can contact the stock quote web service, so having to rely on the validation web service isn't a problem. Listing 37.2 shows the custom LicenseProvider, AcmeStockLicenseProvider, and its use of the validation web service to secure a license on behalf of the client.

Listing 37.2. The Acme Stock Company License Provider Implementation
 using System; using System.ComponentModel; using System.Collections; using System.Collections.Specialized; using System.IO; using System.Diagnostics; namespace StockLibrary {   /// <summary>   /// Summary description for AcmeStockLicenseProvider.   /// </summary>   public class AcmeStockLicenseProvider : LicenseProvider   {     public AcmeStockLicenseProvider()     {     }     public override License        GetLicense(LicenseContext context,          Type type, object instance,          bool allowExceptions)     {       AcmeStockLicense license = null;       if (context.UsageMode == LicenseUsageMode.Designtime)       {         // always provide a 'fake' license for developers during design-time         license = new AcmeStockLicense("");       }       else       {         // obtain the GUID attribute of the containing type         string guid = type.GUID.ToString();         // Invoke the web service        AcmeValidator.Validator validator = new AcmeValidator.Validator();        string licenseKey = validator.ValidateTypeGuid( guid );       if (licenseKey != string.Empty)       {         license = new AcmeStockLicense(guid);       }     }     return license;   }    } } 

From this code, you can see that if the control is instantiated by the Visual Studio .NET 2003 IDE, it will create a default blank license and allow itself to function. A real control like this would also detect whether it is in design mode and would not display the live stock quotes.

If a control is instantiated at runtime, the preceding license provider attempts to contact the validation web service and supply the container type's GUID. The container type sets its GUID with the System.Runtime.InteropServices.GuidAttribute attribute that is typically used for COM InterOp but serves just as useful a purpose here. The validation web service will then compare the supplied GUID with the Acme Stock company's list of valid customers and their allocated GUIDs for container types. If the GUID matches in the database, it will return a license key for that GUID, granting the client permission to use the control at runtime. Again, for a more efficient use of networking and license code, the runtime license could be cached offline for a period of time to avoid overloading the validation service and slowing down the WinForms application.

Listing 37.3 shows a small web service written to validate controls. Instead of going to the trouble of creating a database of customer GUIDs, the code simply fakes it and returns the GUID back as a license key. To make a control fail validation, just return the empty string instead of the GUID.

Listing 37.3. The Sample Validation Web Service Used by the License Provider to Authenticate Client Controls
 using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; namespace AcmeStockControlValidator {   /// <summary>   /// Summary description for Validator.   /// </summary>   public class Validator : System.Web.Services.WebService   {      public Validator()      {       //CODEGEN: This call is required by the ASP.NET Web Services Designer       InitializeComponent();      }      #region Component Designer generated code        //Required by the Web Services Designer       private IContainer components = null;      /// <summary>      /// Required method for Designer support - do not modify      /// the contents of this method with the code editor.      /// </summary>      private void InitializeComponent()      {      }      /// <summary>      /// Clean up any resources being used.      /// </summary>      protected override void Dispose( bool disposing )      {        if(disposing && components != null)        {          components.Dispose();        }        base.Dispose(disposing);      }      #endregion     [WebMethod]     public string ValidateTypeGuid(string guid)     {        // check the guid against the database of registered clients        // if the guid is there, return their unique license key        // for the sample, I will just return the guid back.        return guid;      }   } } 

The service is very simple. It takes the GUID supplied by the client control, skips what would ordinarily be some database activity, and then returns a valid license key for that client. As mentioned earlier, to test what happens when the license is not valid, just return the empty string.

Building Licensed Controls

Now that you have a license, a license provider, and a well-defined scheme for obtaining and validating licenses, you can actually write your custom control. Adding licensing to a custom control is simple after you have done the hard work of creating the license provider and the license. It becomes even easier when you consider that multiple controls can use the same license provider. Companies often use the same license provider for every licensed class they produce. All the company has to do is add a little bit of code to each control, and they automatically become managed by the same licensing scheme as all other controls that the company produces.

To protect your control with your licensing scheme, you need to do two things:

  • Decorate your control class with a LicenseProviderAttribute attribute class.

  • Use either the IsValid or the Validate method of the LicenseManager control to enforce license validation on your class. You can also use the IsLicensed method to determine whether a license was found (regardless of whether that license is valid).

This chapter's example uses the Validate method. This method will throw a security exception if no license is found. If a license (any license) is found, no exception will be thrown and the code will be allowed to continue. Because it is known that the code will not return any license unless the license is valid, this works well. Listing 37.4 shows a sample user control that is protected by the AcmeStockLicenseProvider.

Listing 37.4. A License-Protected Label Control
 using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms; namespace StockLibrary {   /// <summary>   /// Summary description for StockLabel.   /// </summary>   [Description("Provides live stock quote information")]    [LicenseProvider( typeof(AcmeStockLicenseProvider) )]   public class StockLabel : System.Windows.Forms.UserControl   {     private System.Windows.Forms.Label label1;     private System.Windows.Forms.Label label2;     /// <summary>      /// Required designer variable.     /// </summary>     private System.ComponentModel.Container components = null;     public StockLabel()     {       // This call is required by the Windows.Forms Form Designer.       InitializeComponent();        try       {        LicenseManager.Validate(typeof( StockLabel), this.Container);      }      catch       {        MessageBox.Show(          "Could not obtain a valid runtime license for the StockLabel control. " +          "Please contact Acme Stocks to obtain a valid license for this control.",          "Invalid License", MessageBoxButtons.OK, MessageBoxIcon.Error);          this.Enabled = false;          this.Visible = false;      }    }    /// <summary>     /// Clean up any resources being used.    /// </summary>    protected override void Dispose( bool disposing )    {      if( disposing )      {        if(components != null)        {          components.Dispose();        }      }      base.Dispose( disposing );     }     #region Component Designer generated code     /// <summary>      /// Required method for Designer support - do not modify      /// the contents of this method with the code editor.     /// </summary>     private void InitializeComponent()     {       this.label1 = new System.Windows.Forms.Label();       this.label2 = new System.Windows.Forms.Label();       this.SuspendLayout();       //        // label1       //        this.label1.Location = new System.Drawing.Point(8, 16);       this.label1.Name = "label1";       this.label1.TabIndex = 0;       this.label1.Text = "Live Stock Price:";       //        // label2       //        this.label2.ForeColor = System.Drawing.Color.IndianRed;       this.label2.Location = new System.Drawing.Point(112, 16);       this.label2.Name = "label2";       this.label2.TabIndex = 1;       this.label2.Text = "$31.50";       //        // StockLabel       //        this.Controls.Add(this.label2);       this.Controls.Add(this.label1);       this.Name = "StockLabel";       this.Size = new System.Drawing.Size(224, 64);       this.ResumeLayout(false);      }      #endregion   } } 

This class is protected by the AcmeStockLicenseProvider, as indicated by the LicenseProviderAttribute custom attribute class. In addition, in the constructor of the class is a call to LicenseManager.Validate. This call will attempt to obtain a license based on the type of the form. As you know, this means it will try to reach the web service at runtime and obtain a valid license key based on the form's GUID.

Now that the example has a library that contains a license, a license provider, and a license-protected control, a Windows Forms application can be created to use this control. To do this, follow the usual procedure for adding a control to the toolbar (open a new tab group, right-click it, select Add New Items, and then browse to the DLL that contains the control).

You should notice that the control renders just fine in design mode. If you wrote the web service the way it appears in the earlier listing, the application should also render just fine. If you modify the web service so that it just returns the empty string, you will see the dialog box shown in Figure 37.1 when you run your application. In addition, the StockLabel control will become disabled and invisible. In a real-world situation, you might want to remove the control from the form to prevent a programmer from re-enabling the control after the license check failed.

Figure 37.1. A dialog that appears indicating that the license check for the StockLabel control failed.

Licensed Web Controls Versus Windows Forms Controls

When coming up with your licensing scheme and design, it is worth your time to take a look at what kind of control you are building. The environments in which these controls exist dictate what licensing methods might and might not be practical.

For example, if you are developing a Windows Forms control that requires licensing, you might not want to cause that runtime licensing check to make use of a network connection because most Windows applications should be able to run in an offline mode. If you are developing an ASP.NET application, the nature of the application enables you to assume that it will be connected to some kind of network.

    Visual C#. NET 2003 Unleashed
    Visual C#. NET 2003 Unleashed
    ISBN: 672326760
    EAN: N/A
    Year: 2003
    Pages: 316 © 2008-2017.
    If you may any questions please contact us: