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

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

  1. Infragistics.Win.Misc.UltraButton, Infragistics2.Win.Misc.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
  2. Infragistics.Win.Misc.UltraLabel, Infragistics2.Win.Misc.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
  3. Infragistics.Win.Printing.UltraPrintPreviewDialog, Infragistics2.Win.UltraWinPrintPreviewDialog.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
  4. 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,开始源代码分析之旅。

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

  1. if (!ProcessArgs(args))
  2. {
  3. return num;
  4. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

MSDN有完整的解释,拷贝到下面方便您参考,以减少因查找MSDN引起思路中断。

/complist:filename   指定包含授权组件列表的文件名,这些授权组件要包括到 .licenses 文件中。每个组件用它的全名引用,并且每行只有一个组件。命令行用户可为项目中的每个窗体指定一个单独的文件。Lc.exe 接受多个输入文件并产生一个 .licenses 文件。

/h[elp]     显示该工具的命令语法和选项。

/i:module   指定模块,这些模块包含文件 /complist 中列出的组件。若要指定多个模块,请使用多个 /i 标志。

/nologo  取消显示 Microsoft 启动标题。

/outdir:path  指定用来放置输出 .licenses 文件的目录。

/target:targetPE   指定为其生成 .licenses 文件的可执行文件。

/v   指定详细模式;显示编译进度信息。

/?  显示该工具的命令语法和选项。

ProcessArgs方法的关键作用是分析出组件列表,程序集列表,如下面的代码所示

  1. if ((!flag3 && (str2.Length > 7)) && str2.Substring(0, 7).ToUpper(CultureInfo.InvariantCulture).Equals("TARGET:"))
  2. {
  3. targetPE = str2.Substring(7);
  4. flag3 = true;
  5. }
  6. if ((!flag3 && (str2.Length > 8)) && str2.Substring(0, 9).ToUpper(CultureInfo.InvariantCulture).Equals("COMPLIST:"))
  7. {
  8. string str3 = str2.Substring(9);
  9. if ((str3 != null) && (str3.Length > 1))
  10. {
  11. if (compLists == null)
  12. {
  13. compLists = new ArrayList();
  14. }
  15. compLists.Add(str3);
  16. flag3 = true;
  17. }
  18. }
  19. if ((!flag3 && (str2.Length > 2)) && str2.Substring(0, 2).ToUpper(CultureInfo.InvariantCulture).Equals("I:"))
  20. {
  21. string str4 = str2.Substring(2);
  22. if (str4.Length > 0)
  23. {
  24. if (assemblies == null)
  25. {
  26. assemblies = new ArrayList();
  27. }
  28. assemblies.Add(str4);
  29. }
  30. flag3 = true;
  31. }

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

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

  1. string path = null;
  2. if (outputDir != null)
  3. {
  4. path = outputDir + @"\" + targetPE.ToLower(CultureInfo.InvariantCulture) + ".licenses";
  5. }
  6. else
  7. {
  8. path = targetPE.ToLower(CultureInfo.InvariantCulture) + ".licenses";
  9. }
  10. Stream o = null;
  11. try
  12. {
  13. o = File.Create(path);
  14. DesigntimeLicenseContextSerializer.Serialize(o, targetPE.ToUpper(CultureInfo.InvariantCulture), creationContext);
  15. }
  16. finally
  17. {
  18. if (o != null)
  19. {
  20. o.Flush();
  21. o.Close();
  22. }
  23. }
  1.  

这种方式是.NET Framework推荐的保护组件的方式,与我们平时所讨论的输入序列号,RSA签名不同。

来看一下,商业的组件是如何应用这种技术保护组件的。

  1. using System;
  2. using System.Web;
  3. using System.Web.UI;
  4. using System.Web.UI.WebControls;
  5. using System.ComponentModel;
  6.  
  7. namespace ComponentArt.Licensing.Providers
  8. {
  9. #region RedistributableLicenseProvider
  10. public class RedistributableLicenseProvider : System.ComponentModel.LicenseProvider
  11. {
  12. const string strAppKey = "This edition of ComponentArt Web.UI is licensed for XYZ application only.";
  13.  
  14. public override System.ComponentModel.License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
  15. {
  16. if (context.UsageMode == LicenseUsageMode.Designtime)
  17. {
  18. // We are not going to worry about design time Issue a license
  19. return new ComponentArt.Licensing.Providers.RedistributableLicense(this, "The App");
  20. }
  21. else
  22. {
  23. string strFoundAppKey;
  24.  
  25. // During runtime, we only want this control to run in the application
  26. // that it was packaged with.
  27.  
  28. HttpContext ctx = HttpContext.Current;
  29. strFoundAppKey = (string)ctx.Application["ComponentArtWebUI_AppKey"];
  30.  
  31. if(strAppKey == strFoundAppKey)
  32. return new ComponentArt.Licensing.Providers.RedistributableLicense(this, "The App");
  33. else
  34. return null;
  35. }
  36. }
  37. }
  38. #endregion
  39.  
  40. #region RedistributableLicense Class
  41.  
  42. public class RedistributableLicense : System.ComponentModel.License
  43. {
  44. private ComponentArt.Licensing.Providers.RedistributableLicenseProvider owner;
  45. private string key;
  46.  
  47. public RedistributableLicense(ComponentArt.Licensing.Providers.RedistributableLicenseProvider owner, string key)
  48. {
  49. this.owner = owner;
  50. this.key = key;
  51. }
  52. public override string LicenseKey
  53. {
  54. get
  55. {
  56. return key;
  57. }
  58. }
  59.  
  60. public override void Dispose()
  61. {
  62. }
  63. }
  64.  
  65. #endregion
  66. }

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

你只需要像下面这样,应用这个许可保护机制:

  1. [LicenseProvider(typeof(RedistributableLicenseProvider))]
  2. public class MyControl : Control {
  3.  
  4. // Insert code here.
  5.  
  6. protected override void Dispose(bool disposing) {
  7. /* All components must dispose of the licenses they grant.
  8. * Insert code here to dispose of the license. */
  9. }
  10. }

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

  1. “LC.exe”错误

    错误“LC.exe”已退出,代码为 -1. 可能的原因是: 这个第三方组件是个商业组件,他在组件的主使用类定义了 LicenseProvider(typeof(LicFileLicenseProvid ...

  2. [0] Lc.exe 已退出,代码 -1

    可能的原因是:在你的项目中引用了第三方组件,并且这个第三方组件是个商业组件,他在组件的主使用类定义了LicenseProvider(typeof(LicFileLicenseProvider))这个A ...

  3. 解决“错误为Lc.exe已退出,代码为-1”

    今天做项目的时候突然出现编译不通过,错误为Lc.exe已退出,代码为-1.网查了一下,原因是项目中使用了第三方组件(Developer Express v2011)造成的,分享如下:这个第三方组件是个 ...

  4. 错误为Lc.exe已退出,代码为-1

    近来在做项目的时候,突然遇到 了 LC.exe 已退出,代码为-1 的提示,怎么也编译不了,在查了相关的资料后,终于明白了原因,其解决方案如下 : 1. 以管理员的身份来运行vs, 并把项目中“pro ...

  5. 解决VS下“LC.exe已退出,代码为-1”问题

    今天使用VS2015开发一个Winform程序,手一抖拖错了一个第三方控件,然后将其去掉并删除相关的引用,结果导致了LC.exe错误:"Lc.exe已退出,代码为-1 ". 经过上 ...

  6. Lc.exe已退出,代码为-1

    编译项目,出现提示"Lc.exe已退出,代码为-1" .   解决办法: 意思就是把licenses.licx这个文件里的内容删除,但是文件还在(此时是个空文件),发生这个问题的原 ...

  7. "LC.exe" exited with code -1 错误

    当打开一个VS程序时出现"LC.exe" exited with code -1错误,解决方法是: 删除licenses.licx文件即可

  8. LC.exe exited with code -1

    昨天从win8.1升级到win10之后, 一切还算顺利, 就是升级时间比较长. 但是快下班的时候 遇到一个问题, 是之前在win8.1上没遇到的, 首先代码win8.1 vs2013 上跑的时候一切正 ...

  9. vs2012编译出错“LC.exe”已退出解决方法

    “LC.exe”已退出,代码为 -1. 解决方法: 将项目Properties下的licenses.licx文件删除,重新编译即可.

随机推荐

  1. Java获取Web服务器文件

    Java获取Web服务器文件 如果获取的是服务器上某个目录下的有关文件,就相对比较容易,可以设定死绝对目录,但是如果不能设定死绝对目录,也不确定web服务器的安装目录,可以考虑如下两种方式: 方法一: ...

  2. 一步一步hadoop安装

    部署hadoop集群 1.下载jdk1.6,从http://www.oracle.com/technetwork/java/javase/downloads/java-archive-download ...

  3. iOS一些关于日历的问题

    int CalculateDays(int ys, int ms, int ds, int ye, int me, int de) { int days = CalcYearRestDays(ys, ...

  4. java反射机制初探

    最近和一位师兄交流了一下Java,真可谓是大有收获,让我好好的学习了一下javad的反射机制,同终于明白了spring等框架的一个基本实现的思想,那么今天就和大家分享一下java的反射机制. 反射,r ...

  5. AWIT DBackup 0.0.20 发布,备份系统

    AWIT DBackup 0.0.20 修复了几个小 bug. AllWorldIT DBackup 是一个备份系统,为每个目录创建一个独立的压缩包,这更便于搜索. 特点: 使用 xz, bzip2, ...

  6. Ubuntu iptables配置

    sudo su sudo apt-get install iptables-persistent modprobe ip_tables #启动iptable   #删除原有iptables规则 ipt ...

  7. 用c#开发微信 系列汇总

    网上开发微信开发的教程很多,但c#相对较少.这里列出了我所有c#开发微信的文章,方便自己随时查阅.   一.基础知识 用c#开发微信(1)服务号的服务器配置和企业号的回调模式 - url接入 (源码下 ...

  8. google map javascript api v3 例子

    之前一直用百度map,但如果是国外的项目就需要用google地图.由于在国内屏蔽了google地图的服务,因此调用的是一个国内地址(开发用).这个地址没有用key,语言设置也还是中文的. //---- ...

  9. Javascrpt无刷新文件上传

    最近工作中遇到上传文件问题,主要需求是一步点击上传,兼容ie8+,当时用的dojox/form/uploader控件,这两天扒了一下源码,明白了原理拿出来分享一下. 总体思路如下: 1.对于支持XML ...

  10. Flume采集处理日志文件

    Flume简介 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据:同时,Flume提供对数据 ...