解析.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;
}
.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方法的关键作用是分析出组件列表,程序集列表,如下面的代码所示
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;
.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; }
对第一部参数分析出来的组件列表,依次循环,为它们产生授权许可
DesigntimeLicenseContext creationContext = new DesigntimeLicenseContext();
foreach (string str in compLists)
{
key = reader.ReadLine();
hashtable[key] = Type.GetType(key);
LicenseManager.CreateWithContext((Type) hashtable[key], creationContext);
}
.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编译器将它编译成资源文件,嵌入到程序集中。
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)与控件本身的逻辑完全分离,分工协作保护组件的知识产权。
.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) 的原理与源代码剖析的更多相关文章
- “LC.exe”错误
错误“LC.exe”已退出,代码为 -1. 可能的原因是: 这个第三方组件是个商业组件,他在组件的主使用类定义了 LicenseProvider(typeof(LicFileLicenseProvid ...
- [0] Lc.exe 已退出,代码 -1
可能的原因是:在你的项目中引用了第三方组件,并且这个第三方组件是个商业组件,他在组件的主使用类定义了LicenseProvider(typeof(LicFileLicenseProvider))这个A ...
- 解决“错误为Lc.exe已退出,代码为-1”
今天做项目的时候突然出现编译不通过,错误为Lc.exe已退出,代码为-1.网查了一下,原因是项目中使用了第三方组件(Developer Express v2011)造成的,分享如下:这个第三方组件是个 ...
- 错误为Lc.exe已退出,代码为-1
近来在做项目的时候,突然遇到 了 LC.exe 已退出,代码为-1 的提示,怎么也编译不了,在查了相关的资料后,终于明白了原因,其解决方案如下 : 1. 以管理员的身份来运行vs, 并把项目中“pro ...
- 解决VS下“LC.exe已退出,代码为-1”问题
今天使用VS2015开发一个Winform程序,手一抖拖错了一个第三方控件,然后将其去掉并删除相关的引用,结果导致了LC.exe错误:"Lc.exe已退出,代码为-1 ". 经过上 ...
- Lc.exe已退出,代码为-1
编译项目,出现提示"Lc.exe已退出,代码为-1" . 解决办法: 意思就是把licenses.licx这个文件里的内容删除,但是文件还在(此时是个空文件),发生这个问题的原 ...
- "LC.exe" exited with code -1 错误
当打开一个VS程序时出现"LC.exe" exited with code -1错误,解决方法是: 删除licenses.licx文件即可
- LC.exe exited with code -1
昨天从win8.1升级到win10之后, 一切还算顺利, 就是升级时间比较长. 但是快下班的时候 遇到一个问题, 是之前在win8.1上没遇到的, 首先代码win8.1 vs2013 上跑的时候一切正 ...
- vs2012编译出错“LC.exe”已退出解决方法
“LC.exe”已退出,代码为 -1. 解决方法: 将项目Properties下的licenses.licx文件删除,重新编译即可.
随机推荐
- BZOJ4170 极光(CDQ分治 或 树套树)
传送门 BZOJ上的题目没有题面-- [样例输入] 3 5 2 4 3 Query 2 2 Modify 1 3 Query 2 2 Modify 1 2 Query 1 1 [样例输出] 2 3 3 ...
- CentOS 7 安装 vmware-tools
[原创]标题:<CentOS 7 安装 vmware-tools>:作者:肖雪峰,QQ:35360657. 用 VMware Workstation 11 新安装了CentOS 7虚拟机, ...
- js 将long型字符串转换成日期格式
工作中难免会碰到日期的转换,往往为了方便,后台都是把时间以long型(形如1343818800000)返回给web前端.再有前端自己根据页面需求转换成相应的日期格式.这里将我常用的一个转换时间的函数贴 ...
- JavaWeb开发学习(二)-配置Tomcat服务器
1. 下载Tomcat服务器 Tomcat是一个免费.开源的JavaWeb服务器. Tomcat官网是http://tomcat.apache.org/ 我使用的是版本是Tomcat7.0,下载Zip ...
- 解决Ubuntu Server 12.04 在Hyper-v 2012 R2中不能使用动态内存的问题
前言 全新Hyper-v 2012 R2终于开始支持在Linux的VPS中使用动态内存,可以大大优化服务器的资源分配,小弟我兴奋不已,于是抽空时间赶紧升级到 2012 R2,好好整理一番内存分配,不过 ...
- 使用git将代码push到osc上
1.下载git客户端 2.在osc上创建项目 ①使用:git bash here ②在目录下执行:git init ③ssh-keygen -t rsa -C "xqs@gmail.com& ...
- 【Bugly 技术干货】Android开发必备知识:为什么说Kotlin值得一试
1.Hello, Kotlin Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 1. ...
- dojo Provider(script、xhr、iframe)源码解析
总体结构 dojo/request/script.dojo/request/xhr.dojo/request/iframe这三者是dojo提供的provider.dojo将内部的所有provider构 ...
- 新手的第一个phonegap Android应用
对PhoneGap开发感兴趣的请加入群 PhoneGap App开发 348192525 手机成为现在软件应用必不可少的一种设备,然而手机平台的不统一造成我们需要为不同手机重写代码,这对一般应用来 ...
- 作业七:团队项目——Alpha版本冲刺阶段-12
今日编写代码如下: class ChessRule { /**卒子的移动规则*/ public void armsRule(int Man,JLabel play,MouseEvent me){ // ...