CSDN博客

img ego

.NET Framework给应用程序颁发许可证(中)

发表于2003/9/19 0:01:00  1391人阅读

by Kenn Scribner
翻译:邹建强


编译许可证文件

  在把所有许可证文件留下前,我应该提一下lc.exe。lc.exe或者说许可证编译器(license compiler),是一个工具,用于装配.NET Framework,它获取许可证文件信息并作为程序集资源进行编码。由于LicFileLicenseProvider默认的实现方式并不会去寻找一个基于资源的许可证文件,因此这个工具是被Framework临时添加进来的,但是它有某些令人感兴趣的灵活性。首先它产生资源信息方法很简便。更令人感兴趣的是,它能把多个许可证文件编码成单一资源,并且允许你把不同的许可证文件字串一次性地许可给多个控件。在我看来,它也存在着比较大的局限性,也就是说你必须使用程序集链接器(al.exe)来汇编你已编译过的模块和资源。对于敲命令行有偏爱的人来说这不是个问题。由于Visual Studio .NET不会编译或生成多模块汇编,因此对于大多数使用Visual Studio .NET的人而言,这是一个限制(虽然你可用一个“内嵌资源”的build操作把资源输入嵌入到你的项目文件当中)。

  说到Visual Studio .NET,你会发现如果想创建一个颁发许可证的控件,你找不到自动化的支持,无论是Visual Studio的2002版还是2003版。因此怎样才能创建这样的一个控件呢?答案是,你简单地手动创建一个这样的文件并且把它和编译过的程序集装配在一起。就我个人而言,把这个文件用“none”的build操作添加到Visual Studio的项目当中,这样做没有必要。当这个文件和别的项目/解决方案文件被包装在一起时,这仅仅使源代码控件稍微容易了点。还要不得不手动把这个文件复制到程序集执行目录当中。

可选的许可证方案

  我认为微软有义务为.NET Framework提供一个默认的许可证提供者,并且选择类似于ActiveX控件许可证的实现方式。我也认为微软不会考虑把这么简单的许可证方案应用于他们的任何软件产品当中(或者至少是任何重要的软件产品)。无论怎样,我们为什么提供一个更容易装配、更智能化、更难于破解的许可证方案来取而代之呢?下面就是这个可选的许可证方案。

  你可以以你喜欢的任何方式对你的控件颁发许可证。或许你能设计成仅在周二运行,或者你能根据用户本地区的天气预报是否是晴天来决定是否在一个程序中执行这个控件(例如:获取用户的邮政编码并从互联网上下载相关的天气预报)。我绝不是在开玩笑。你可以做到。这个许可证颁发流程灵活到你可以做到这些,但这并不意味着你应该这样做。我将演示一个更实际的方法,采用注册表许可证方式。在我详细描述这个方案前,你应该知道一个事实:

.NET Windows Forms应用程序派生于System.Windows.Forms.Form,它轮流由System.Windows.Forms.Control派生!

  等等……这意味着什么呢?不错,这意味着,你象给一个控件颁发许可证一样很容易就能对整个应用程序进行颁发许可授权。虽然我给你演示的是以应用程序而不是控件的许可证授权为目标的,但是它同样可以应用于这两种情况。让我们思考一会儿这句话的含义吧。

应用程序和控件许可证

  意识到应用程序与控件许可证间的基本差别是当你真正关心许可证的时候。如果你为了谋生开发了些控件和组件 ,目标市场是由别的软件开发者构成。这意味着你的许可证方案要在设计时design time )中对加入到Visual Studio 项目的插入操作做合法性检查。由于你可能想让你的控件库销售量达几百万份,你会免费提供这个运行时许可证,或者根本不强加上一个运行时的许可证。毕竟你的目标市场是销售给应用程序,这样他们会从你这里买更多的控件。

  然而应用程序的许可证会有所不同。我们自己在设计时构建应用程序,不会想到把设计时许可证强加于自身。相反,我们希望的是在运行时(run time)的适当位置确保用户得到相应的许可证信息

  框架设计者在他们设计许可证结构时考虑到了这一点,当GetLicense() 调用发生时,你可以检查许可证的请求状态,无论是在设计时还是运行时。这两种情况我都可以演示给你,但我们现在要考虑的校验许可证仅仅是一种情况就是运行时,这是由于这是应用程序的许可证颁发不同于控件的。因此,你应该意识到这是一个重要差别。

基于注册表的许可证

  基于注册表的许可证实现了一个许可证的方案,这种方案会检查含有指定键值的注册键存在性。应用程序自身不必为实现写一个注册表键值而编写代码──这由安装程序来完成。由于许多程序都以各种各样的形式利用注册表,因此这不会成为发展的限制。我们的安装程序会提前预占一个注册键值,那时起就有了注册。

  对于写入注册表中的键值,如果我愿意还可以写的更好些,但是对这个例子而言,我仅仅简单的写“Installed”这个字串。我会留些更好的实现方式给你(hey,我不能把我的全部秘密给你!)。下面这个键值是我要找的:

HKEY_CURRENT_USER/Software/Acme/HostKeys/2de915e1-df7

1-

3443-9f4d-32259c92ced2

  你看到 GUID 值是我分配给我的应用程序的一个GUID。Acme Software销售的每个应用程序都有自己的GUID,但是通过反射,我能对所有的应用程序使用相同的许可证提供者。我会在这里填满所有Acme Software许可证键值,因此这个键名就是“主键”(HostKeys)。在你自己的应用程序中,你可随意的把这个主键放在任何合适的地方。

  正象你在图3看到的应用程序非常简单。


图 3. 基于注册表许可证的应用程序

  如果你看到这个窗口,这份许可证是合法的。另一方面,如果这份许可证是非法的话,你会看到图4的异常对话框。


图 4. 非法注册表许可证反馈

  为了使之运行,我们需要创建一个新的应用并且要对用设计向导产生的代码做些轻微的改动。顺便说一句,虽然在此展示的代码是C#,但是我没轻视VB程序员的意思,这些例子比较容易转换,另外我这篇文章附加的示例源代码当中也提供了C#和VB.NET两种语言。

  开始前,我们除了写我们自己的许可证提供者以外,要对主程序的四个地方修改源代码。这四个地方是:

  1. 应用程序类的声明(加些属性)
  2. 应用程序类的构造函数
  3. 应用程序类的Dispose()方法
  4. 应用程序的"Main"方法

  由于我使用了GuidAttribute ,因此我会增加一个using子句。我们先看看类声明:

/// <summary>
/// Summary description for frmMain.
/// </summary>
[GuidAttribute("2de915e1-df71-3443-9f4d-32259c92ced2")]
[LicenseProvider(typeof(RegistryLicenseProvider))]
public
class frmMain : System.Windows.Forms.Form
{
   ...
}

  我给这个类加了两个属性:GuidAttributeLicenseProvider。你已经看过LicenseProvider了,另一个GuidAttribute属性的目的是分配一个GUID的数值给这个应用程序类。通常这个属性用于COM互操作,我这里重用它,简单地给应用程序类分配一个独一无二的数字识别码,这是Framework所支持的(也就是说,我们以后能很容易地决定类类型的GUID)。我可以生成自己的属性,可是我却会失去内置的框架GUID的侦测支持。我指定给GuidAttribute的GUID值一定要和我在注册表键值中使用的GUID值相匹配,这一点非常重要。我的许可证提供者反射给产生应用程序注册表键值的值就是这个值。因为我使用了GuidAttribute 属性,我必须把这个值加进我的命名空间集合中:

using System.Runtime.InteropServices;

  应用程序类的构造函数中,我把前面代码填加进去:

private License _license = null;

public frmMain()
{
   //
   // Required for Windows Form Designer support
   //
   InitializeComponent();  
   // Obtain the license
   _license = LicenseManager.Validate(typeof(frmMain), this);
}

  这里我简单地调用了许可证管理者的 Validate() 方法,无特别之处。但是,如果许可证是非法的, Validate() 方法能抛出一个异常。因此在某些地方捕捉这个异常是个不错的想法。但是也要注意我填加了一个调用_license的私有数据成员。某些许可证方案利用了这个许可证对象(本例中没有提供),但在所有的 情况中,当应用程序终结时我会需要处理许可证。分配给应该正确释放的许可证的可能是一些资源,因此我不得不对应用程序的 Dispose() 方法做修改:

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
   if( disposing )
   {
      if (components != null)
      {
         components.Dispose();
      }

     
if (license != null)
      {
         license.Dispose();
         license = null;
      }
   }
   base.Dispose( disposing );
}

  这里我对null的许可证做了一个速检,如果值不为null,我调用它的 Dispose() 方法,否则调用它的引用。即使这个样例中我不使用许可证 license,这个操作仍是必要的,因为这个许可证含有必须被释放的资源。

  最后,我修改了应用程序的 Main() 方法,要考虑到许可证管理器的异常:

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() 
{
   // Create an instance of the licensed application
   frmMain app = null;
   try
   {
      // This will throw a LicenseException if the
      // license is invalid... if we get an exception,
      // "app" will remain null and the Run() method
      // (below) will not be executed...
      app = new frmMain();
   } // try
   catch (Exception ex)
   {
      // Catch any error, but especially licensing errors...
      string strErr =
          String.Format("Error executing application: '{0}'",
                        ex.Message);
      MessageBox.Show( strErr,
                       "RegistryLicensedApplication Error",
                       MessageBoxButtons.OK,
                       MessageBoxIcon.Error);
   } // catch 
   if ( app != null ) Application.Run(app);
}
阅读全文
0 0

相关文章推荐

img
取 消
img