Licensing in Cider

We're just finishing up the implementation of licensing in Cider, I'll let you know when it will be visible in a CTP. 

What we did was implement the System.ComponentModel Licensing that was also used in Windows Forms.  Our feedback was that this is a sufficient model -- yeah it isn't great but it works and circumventing that licensing is sufficient to show intent.

So how does licensing work in a nutshell?  In essence, it's as simple as follows:

  1. A control developer creates a licensed control. A control, a LicenseProvider and a license file. (potentially creates a derived License)
  2. An application developer instantiates this licensed control at design time by drag dropping the control from the toolbox onto the design surface.
  3. The LicenseProvider reads the license file and parses it to ensure the contents are valid.  Note that different features/capabilities can be encoded into the license file.
  4. If the license file is valid, the control will be created and a licx file added to the project.  If it isn't, control cannot be instantiated on the design surface -- this happen every time the control is drag dropped onto the design surface. 
  5. At compile time, the LC.exe tool will run and based on the licx file, add licensing data into the assembly using the licensed control.
  6. At runtime, the LicenseProvider gets the licensing data that was embedded into the assembly using the control and performs a runtime license check.
  7. If the runtime license check fails an exception will be thrown from the constructor of the licensed control.

We provide a default implementation of the LicenseProvider called the LicFileLicenseProvider.  It is very simple, the license file is a file with a string that corresponds to:  "<type.FullName> is a licensed component.".  That is the license key.

When a control that uses the LicFileLicenseProvider is instantiated at design time, a lic file with the aforementioned license key is checked.  At build time, that license key is embedded in the assembly (this is determined by the call to LicenseContext.SetSavedLicenseKey that the LicenseProvider makes) and at runtime when the control is instantiated, the license key is extracted from the calling assembly and verfiied by the LicFileLicenseProvider.

There are a couple of good resources out there to fill in the gaps if you want to add licensing to your control.  Check out:

https://windowsforms.net/articles/licensing.aspx

https://www.myagent.dk/2005/02/net_licensing.html

License Initialization and Cleanup in Cider
In WPF, there isn't a concept of IDisposable so cleaning up the License in Dispose() won't work.  Additionally, there are situations where a control can be unloaded and reloaded multiple times.  So for licensing in WPF, the following pattern is recommended for calling LicenseManager.Validate() and License.Dispose():

[

LicenseProvider(typeof(JimsLicenseProvider))]
public class CustomLicensedButton : Button
{
    private License license = null;
    public CustomLicensedButton()
{
ValidateLicense();
        this.Loaded += new System.Windows.RoutedEventHandler(CustomLicensedButton_Loaded);
        this.Unloaded += new System.Windows.RoutedEventHandler(CustomLicensedButton_Unloaded);
    }

    void CustomLicensedButton_Unloaded(object sender, System.Windows.RoutedEventArgs e)
    {
        CleanupLicense();
    }

    void CustomLicensedButton_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        ValidateLicense();
    }

    private void ValidateLicense()
    {
        if (license == null)
        {
             license = LicenseManager.Validate(typeof(CustomLicensedButton), this);
        }
    }

    private void CleanupLicense()
    {
        if (license != null)
        {
             license.Dispose();
             license = null;
        }
    }
}

Adding Licensed Controls Through XAML
In this case, if the control is licensed but does not have a valid license file, the designer will not show the UI (Window, Page, UserControl etc) where it is being used, instead it will show a message indicating that the control could not be instantiated.

If the license file is valid, the control will work but the licx file will not be added/updated based on the addition of a licensed control.  If the licx file does not contain the type for your control, the license key will not be embedded into the assembly using that control and the runtime license check will fail.

The workaround is to drag drop a control from the toolbox onto any Windows, Page, UserControl etc. as the licx file is created/updated per project.  Alternatively, you can create the licx file by hand.

Writing the Licx File
The other difference between Windows Forms and Cider is that in Cider we don't re-write the licx file if has been modified by hand.  In VS 2005, you could delete a type key from the licx file and when the designer reloaded the UI it would ensure all of the keys for the licensed control used on that UI were in the licx.  This is no longer the case, the licx file is only created/updated when a control is drag dropped from the toolbox onto the designer surface.

Partial Trust
There is a bug in the current implementation that our Control Developer customers lamented at the last dev lab.  The bug is that licensing doesn't work in partial trust.  To give you an update, a bug has been filed on this and the initial prognosis is good although this is not something that is handled by the Cider team.