许可证编译器 (Lc
exe) 的作用是读取包含授权信息的文本文件
并产生一个可作为资源嵌入到公用语言运行库可执行文件中的
licenses 文件
在使用第三方类库时经常会看到它自带的演示程序中包含有这样的Demo许可文件
复制代码 代码如下:
Infragistics
Win
Misc
UltraButton
Infragistics
Win
Misc
v
Version=
Culture=neutral
PublicKeyToken=f
b
b
b
fdf
Infragistics
Win
Misc
UltraLabel
Infragistics
Win
Misc
v
Version=
Culture=neutral
PublicKeyToken=f
b
b
b
fdf
Infragistics
Win
Printing
UltraPrintPreviewDialog
Infragistics
Win
UltraWinPrintPreviewDialog
v
Version=
Culture=neutral
PublicKeyToken=f
b
b
b
fdf
Infragistics
Win
UltraWinDataSource
UltraDataSource
Infragistics
Win
UltraWinDataSource
v
Version=
Culture=neutral
PublicKeyToken=f
b
b
b
fdf
这个文件的格式是文本文件但要按照它的格式要求来写
控件名称 程序集全名称
首先根据需要写一个需要被授权的控件列表格式如上所示例如HostAppexe 的应用程序要引用SamplesDLL 中的授权控件 MyCompanySamplesLicControl则可以创建包含以下内容的 HostAppLictxt MyCompanySamplesLicControl SamplesDLL
再调用下面的命令创建名为 HostAppexelicenses 的 licenses 文件 lc /target:HostAppexe /complist:hostapplictxt /i:SamplesDLL /outdir:c:bindir
生成将 licenses 文件作为资源嵌入在HostAppexe的资源中如果生成的是 C# 应用程序则应使用下面的命令生成应用程序
csc /res:HostAppexelicenses /out:HostAppexe *cs
NET Framework SDK目录中的LCEXE文件是由NET语言编写的它的功能就是为了根据许可文件的内容生成资源文件在编译的最后时刻由CSC编译器把生成的资源文件嵌入到执行文件中
用NET Reflector载入LCEXE开始源代码分析之旅
程序的入口处先是分析命令行参数根据参数的不同来执行指定的功能先看一个完整的参数列表代码是下面三行
复制代码 代码如下:
if (!ProcessArgs(args))
{
return num;
}
MSDN有完整的解释拷贝到下面方便您参考以减少因查找MSDN引起思路中断
/complist:filename 指定包含授权组件列表的文件名这些授权组件要包括到 licenses 文件中每个组件用它的全名引用并且每行只有一个组件命令行用户可为项目中的每个窗体指定一个单独的文件Lcexe 接受多个输入文件并产生一个 licenses 文件
/h[elp] 显示该工具的命令语法和选项
/i:module 指定模块这些模块包含文件 /complist 中列出的组件若要指定多个模块请使用多个 /i 标志
/nologo 取消显示 Microsoft 启动标题
/outdir:path 指定用来放置输出 licenses 文件的目录
/target:targetPE 指定为其生成 licenses 文件的可执行文件
/v 指定详细模式显示编译进度信息
/? 显示该工具的命令语法和选项
ProcessArgs方法的关键作用是分析出组件列表程序集列表如下面的代码所示
复制代码 代码如下:
if ((!flag
&& (str
Length >
)) && str
Substring(
)
ToUpper(CultureInfo
InvariantCulture)
Equals("TARGET:"))
{
targetPE = str
Substring(
);
flag
= true;
}
if ((!flag
&& (str
Length >
)) && str
Substring(
)
ToUpper(CultureInfo
InvariantCulture)
Equals("COMPLIST:"))
{
string str
= str
Substring(
);
if ((str
!= null) && (str
Length >
))
{
if (compLists == null)
{
compLists = new ArrayList();
}
compLists
Add(str
);
flag
= true;
}
}
if ((!flag
&& (str
Length >
)) && str
Substring(
)
ToUpper(CultureInfo
InvariantCulture)
Equals("I:"))
{
string str
= str
Substring(
);
if (str
Length >
)
{
if (assemblies == null)
{
assemblies = new ArrayList();
}
assemblies
Add(str
);
}
flag
= 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)与控件本身的逻辑完全分离分工协作保护组件的知识产权