深入解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析

许可证编译器 (Lc.exe) 的作用是读取包含授权信息的文本文件,并产生一个可作为资源嵌入到公用语言运行库可执行文件中的 .licenses 文件
 

在使用第三方类库时,经常会看到它自带的演示程序中,包含有这样的Demo许可文件

复制代码 代码如下:

Infragistics.Win.Misc.UltraButton, Infragistics2.Win.Misc.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
Infragistics.Win.Misc.UltraLabel, Infragistics2.Win.Misc.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
Infragistics.Win.Printing.UltraPrintPreviewDialog, Infragistics2.Win.UltraWinPrintPreviewDialog.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
Infragistics.Win.UltraWinDataSource.UltraDataSource, Infragistics2.Win.UltraWinDataSource.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31

这个文件的格式是文本文件,但要按照它的格式要求来写:

控件名称, 程序集全名称

首先根据需要,写一个需要被授权的控件列表,格式如上所示。例如,HostApp.exe 的应用程序要引用Samples.DLL 中的授权控件 MyCompany.Samples.LicControl1,则可以创建包含以下内容的 HostAppLic.txt。 MyCompany.Samples.LicControl1, Samples.DLL。

再调用下面的命令创建名为 HostApp.exe.licenses 的 .licenses 文件。 lc /target:HostApp.exe /complist:hostapplic.txt /i:Samples.DLL /outdir:c:bindir

生成将 .licenses 文件作为资源嵌入在HostApp.exe的资源中。如果生成的是 C# 应用程序,则应使用下面的命令生成应用程序。

csc /res:HostApp.exe.licenses /out:HostApp.exe *.cs

.NET Framework SDK目录中的LC.EXE文件是由.NET语言编写的,它的功能就是为了根据许可文件的内容,生成资源文件。在编译的最后时刻,由CSC编译器把生成的资源文件嵌入到执行文件中。

用.NET Reflector载入LC.EXE,开始源代码分析之旅。

程序的入口处先是分析命令行参数,根据参数的不同来执行指定的功能。先看一个完整的参数列表。代码是下面三行

复制代码 代码如下:

if (!ProcessArgs(args))
 {
     return num;
 }

MSDN有完整的解释,拷贝到下面方便您参考,以减少因查找MSDN引起思路中断。
/complist:filename   指定包含授权组件列表的文件名,这些授权组件要包括到 .licenses 文件中。每个组件用它的全名引用,并且每行只有一个组件。命令行用户可为项目中的每个窗体指定一个单独的文件。Lc.exe 接受多个输入文件并产生一个 .licenses 文件。
/h[elp]     显示该工具的命令语法和选项。
/i:module   指定模块,这些模块包含文件 /complist 中列出的组件。若要指定多个模块,请使用多个 /i 标志。
/nologo  取消显示 Microsoft 启动标题。
/outdir:path  指定用来放置输出 .licenses 文件的目录。
/target:targetPE   指定为其生成 .licenses 文件的可执行文件。
/v   指定详细模式;显示编译进度信息。
/?  显示该工具的命令语法和选项。
ProcessArgs方法的关键作用是分析出组件列表,程序集列表,如下面的代码所示

复制代码 代码如下:

  if ((!flag3 && (str2.Length > 7)) && str2.Substring(0, 7).ToUpper(CultureInfo.InvariantCulture).Equals("TARGET:"))
 {
       targetPE = str2.Substring(7);
       flag3 = true;
 }
if ((!flag3 && (str2.Length > 8)) && str2.Substring(0, 9).ToUpper(CultureInfo.InvariantCulture).Equals("COMPLIST:"))
 {
      string str3 = str2.Substring(9);
      if ((str3 != null) && (str3.Length > 1))
        {
                    if (compLists == null)
                    {
                        compLists = new ArrayList();
                    }
                    compLists.Add(str3);
                    flag3 = true;
       }
}
if ((!flag3 && (str2.Length > 2)) && str2.Substring(0, 2).ToUpper(CultureInfo.InvariantCulture).Equals("I:"))
 {
       string str4 = str2.Substring(2);
       if (str4.Length > 0)
        {
                    if (assemblies == null)
                    {
                        assemblies = new ArrayList();
                    }
                    assemblies.Add(str4);
        }
        flag3 = true;
}

分 析出组件和程序集之后,再来ResolveEventHandler 委托的含义。如果运行库类加载程序无法解析对程序集、类型或资源的引用,则将引发相应的事件,从而使回调有机会通知运行库引用的程序集、类型或资源位于哪 个程序集中。ResolveEventHandler 负责返回解析类型、程序集或资源的程序集。

复制代码 代码如下:

ResolveEventHandler handler = new ResolveEventHandler(LicenseCompiler.OnAssemblyResolve);
AppDomain.CurrentDomain.AssemblyResolve += handler;

对第一部参数分析出来的组件列表,依次循环,为它们产生授权许可

复制代码 代码如下:

DesigntimeLicenseContext creationContext = new DesigntimeLicenseContext();
foreach (string str in compLists)
{
   key = reader.ReadLine();    hashtable[key] = Type.GetType(key);        LicenseManager.CreateWithContext((Type) hashtable[key], creationContext);
}

最后,生成许可文件并保存到磁盘中,等待CSC编译器将它编译成资源文件,嵌入到程序集中。

复制代码 代码如下:

string path = null;
if (outputDir != null)
 {
    path = outputDir + @"" + targetPE.ToLower(CultureInfo.InvariantCulture) + ".licenses";
 }
else
 {
      path = targetPE.ToLower(CultureInfo.InvariantCulture) + ".licenses";
 }
 Stream o = null;
 try
     {
            o = File.Create(path);
           DesigntimeLicenseContextSerializer.Serialize(o, targetPE.ToUpper(CultureInfo.InvariantCulture), creationContext);
     }
     finally
     {
            if (o != null)
            {
                o.Flush();
                o.Close();
            }
     }

这种方式是.NET Framework推荐的保护组件的方式,与我们平时所讨论的输入序列号,RSA签名不同。
来看一下,商业的组件是如何应用这种技术保护组件的。

复制代码 代码如下:

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace ComponentArt.Licensing.Providers
{
  #region RedistributableLicenseProvider
    public class RedistributableLicenseProvider : System.ComponentModel.LicenseProvider
    {
    const string strAppKey = "This edition of ComponentArt Web.UI is licensed for XYZ application only.";   

    public override System.ComponentModel.License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
    {
      if (context.UsageMode == LicenseUsageMode.Designtime)
      {
        // We are not going to worry about design time Issue a license
        return new ComponentArt.Licensing.Providers.RedistributableLicense(this, "The App");
      }
      else
      {
        string strFoundAppKey;
        // During runtime, we only want this control to run in the application
        // that it was packaged with.
        HttpContext ctx = HttpContext.Current;
        strFoundAppKey = (string)ctx.Application["ComponentArtWebUI_AppKey"];
        if(strAppKey == strFoundAppKey)
          return new ComponentArt.Licensing.Providers.RedistributableLicense(this, "The App");
        else
          return null;
      }
    }
  }
  #endregion
  #region RedistributableLicense Class
  public class RedistributableLicense : System.ComponentModel.License
  {
    private ComponentArt.Licensing.Providers.RedistributableLicenseProvider owner;
    private string key;
    public RedistributableLicense(ComponentArt.Licensing.Providers.RedistributableLicenseProvider owner, string key)
    {
      this.owner = owner;
      this.key = key;
    }
    public override string LicenseKey
    {
      get
      {
        return key;
      }
    }
    public override void Dispose()
    {
    }
  }
  #endregion
}

首 先要创建一个类型,继承于License类型,再创建一个继承于LicenseProvider的类型,用于颁发许可证,包含在设计时许可和运行时许可, 从上面的例子中可以看到,设计时没有限制,可以运行,但是到运行时,你必须有序列号,它才会生成许可对象,而不是返回null给.NET Framework类型。整个验证过程由.NET完成。
你只需要像下面这样,应用这个许可保护机制:

复制代码 代码如下:

[LicenseProvider(typeof(RedistributableLicenseProvider))]
public class MyControl : Control {
    // Insert code here.
    protected override void Dispose(bool disposing) {
       /* All components must dispose of the licenses they grant.
        * Insert code here to dispose of the license. */
    }
}

控件许可的验证代码(RedistributableLicenseProvider)与控件本身的逻辑完全分离,分工协作保护组件的知识产权。

时间: 2024-05-21 03:06:01

深入解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析的相关文章

深入解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析_实用技巧

在使用第三方类库时,经常会看到它自带的演示程序中,包含有这样的Demo许可文件 复制代码 代码如下: Infragistics.Win.Misc.UltraButton, Infragistics2.Win.Misc.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31Infragistics.Win.Misc.UltraLabel, Infragistics2.Win.Misc.v11.

asp.net LC.exe已退出代码为 -1的原因分析及解决方法_实用技巧

可能的原因是: 这个第三方组件是个商业组件,他在组件的主使用类定义了 LicenseProvider(typeof(LicFileLicenseProvider)) 这个Attribute. VS2005在编译时检测到这个类的时候,会检查到组件使用的是LicFileLicenseProvider这个属性,表示有组件使用的是把许可的辅助信息保存在license.licx文件中,这个文件保存在vs2005中解决方案资源管理器中的Properties文件夹内. 这个文件的内容实际上是个引用,他保存着你

“LC.exe”已退出,代码为 -1

今天在Visual Studio 2010中编译代码的时候,遇到了下面这个错误: "LC.exe"已退出,代码为 -1 上网查看资料发现: 查看加载的程序项目,找到Properties文件夹licenses.licx文件,然后右键选择删除就可以了,调试运行正常了. 更加详细的解决方案如下: 点击打开链接 上方网页备份:点击打开链接

深入探究VC —— 编译器cl.exe(1)

cl.exe的功能是将源代码文件编译为可提供链接器使用的obj对象文件.cl.exe命令行参数形式如下: CL (option...) file... [option | file]... [lib...] [@command-file] [/link link-opt...] 各命令行参数的意义如下 : 1.option 命令行中的"option"是指一个或多个编译选项.在指定选项时,需要在选项前加正斜杠(/)或一个短划线(-).有一些选项还会带有参数,使用":"

深入探究VC —— 编译器cl.exe(2)

这一章节介绍的全是VC编译器选项,option参数是cl.exe的编译选项,是cl.exe命令行参数中最复杂.也是最常用的.下面介绍一些常用的编译选项: 1.代码生成有关 这些选项将影响编译完成后生成的机器代码:     2.调试有关 与调试有关的编译选项如下图,/Z7./Zi./ZI这三个选项是对程序进行调试时所必需的选项,同时注意,在生成调试版本的程序时,需要指定/Od选项以禁用优化:   3.程序优化有关 与程序优化有关的选项如下图,优化选项将影响编译器对代码的优化,最选择代码体积最小还是

深入探究VC —— 资源编译器rc.exe(3)

Windows应用程序中,图标.菜单.畏途.图标.工具条.对话框等是以资源的形式存在的.开发人员也可以自定义资源类型.如果一个程序使用了资源,那么它在构建时需要对资源进行编译.程序所使用的资源会在资源脚本文件(*.rc)中进行描述编写,编译时,资源编译器会将*.rc文件编译为可链接的.res文件.(资源中可以包括字符串) 1.命令行参数 使用如下方式在命令行中运行rc.exe RC [options] srcipt-file options是编译选项,script-file是需要编译的资源脚本文

命令行编译器vbc.exe和csc.exe的使用

Titlevbc:MSDN相关 http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/vblr7/html/valrfvbcompileroptionslistedbycategory.asp csc:MSDN相关 使用 C# 2.0 命令行编译器 将quote.vb编译成quote.dll文件,使用的命令是:vbc.exe,我用的机器上装的是win server 2003 .命令所在的路径为:C:\WINDOWS\

解析Win 2000中的EXE文件(2)

ckcnv.exe > Cookie Convertor 变换Cookie cleanmgr.exe > Disk Cleanup, popular with Windows 98 磁盘清理 cliconfg.exe > SQL Server Client Network Utility SQL客户网络工具 clipbrd.exe > Clipboard viewer for Local will allow you to connect to other clipboards 剪

编译器-Javac.exe

2.3 Javac编译器 Javac编译器读取Java源代码,并将其编译成字节代码,调用Javac的命令行如下: C:\>javac options filename.java 值得注意的是,和Java解释器不同,Javac 编译器期望它正在编译的文件具有扩展名.Java.其命令行如下表 选项 功能 -classpath path 此选项用于设定路径,在该路径上Javac寻找需被调用的类.该路径是一个用分号分开的目录列表. -d directory 此选项指定一个根目录.该目录用来创建反映软件包