Last Updated 2011/09/21
自作のコントロールを有料(いわゆる、シェアウエアとして)で公開する場合、コントロールにライセンス情報を設定し、デザイン時の使用を制限するなどの処理が必要な場合があります。さいわい、.Net Framework にはコントロールにライセンス(使用許諾)を設定する機能があります。このページではコントロールにライセンスを設定する手順をできるだけ実践的に説明したいと思います。なお、話を簡単にするため、ここではフォームに貼り付けるビジュアルコントロールを対象として説明します。非ビジュアルなコンポーネントの場合も手順はまったく同じです。
自作のコントロールにライセンスを付与したい場合、クラスの宣言部において LicenseProviderAttribute 属性でライセンスプロバイダを設定します。.Net Framework 標準のライセンスプロバイダは LicFileLicenseProvider クラスなので、これでよければ以下のようになります。手続きはこれだけです。
namespace emanual { [LicenseProvider(typeof(LicFileLicenseProvider))] public class SpecialControl : Control { .... } }
このコントロールをツールボックスからフォームに貼り付けようとした時、.Net Framework は次のようなファイル名を持つファイルを探して、その内容をチェックします。
emanual.SpecialControl.lic
ファイルの内容が以下であればライセンスは OK と判断し、ファームに貼り付けることができますが、それ以外のときは LicenseException 例外が発生します。
emanual.SpecialControl is a licensed component.
ファイルの内容は見てのとおり、名前空間名+クラス名+半角スペース に続いて、"a licensed Component." です。
ライセンスファイルはコントロールプロジェクトの obj\debug ディレクトリに格納すること。
ライセンスファイルを使う基本的な手順は以上ですが、これでは秘密にしておくべきライセンスキーが秘密にならないので、次の項目では独自のライセンスキーを設定する手順について説明します。
ライセンスファイル(.lic ファイル)の形式と使い方は前の項目で説明したとおりですが、ここでは LicFileLicenseProvider クラスから派生するクラスを作成する手順を説明します。なお、ライセンスキーとして GUID を利用することにします。GUID は確率的にいうと、この世の中で唯一無二のデータといっていいですから重宝します。
まず、ライセンスを付与するコントロールを定義します。ここでは Control クラスから派生し、その BackColor プロパティに Color.Red を設定するだけのものとします。なお、GUID は Visual Studio の IDE の [ツール]-[GUID の作成] を選択すると表示されるダイアログボックスの [4. Registry Format] を選択し、[Copy] でクリップボードに GUID の文字列表現がコピーされます。
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; // LicenseProvider using System.Runtime.InteropServices; // GuidAttribute using System.Windows.Forms; // Control using System.Drawing; // Color namespace emanual { [GuidAttribute("9ED54F84-A89D-4fcd-A854-44251E925F09")] // ← ライセンスキーとして使う GUID を宣言 [LicenseProvider(typeof(MyLicenseProvider))] // ← 独自のライセンスプロバイダを設定 public class MyClassLibrary : Control { License FLicense = null; public MyClassLibrary() { // ライセンスの有効性をチェックする FLicense = LicenseManager.Validate(typeof(MyClassLibrary), this); // コントロールの BackColor プロパティを設定する this.BackColor = Color.Red; } protected override void Dispose(bool disposing) { if (FLicense != null) FLicense.Dispose(); // License オブジェクトの破棄は必須 base.Dispose(disposing); } } // end of MyClassLibrary class } // end of namespace
次に、ライセンスファイルを使うライセンスプロバイダを定義します。
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.IO; using System.Windows.Forms; namespace emanual { class MyLicenseProvider : LicFileLicenseProvider { // コンストラクタ public MyLicenseProvider() { } //------------------------------------------------------------------------------------- // MyClassLibrary をインスタンス化するとき、.Net Framework から呼び出されるメソッド public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions) { if (context != null) { // 実行時は常に実行可能にする if (context.UsageMode == LicenseUsageMode.Runtime) { return (new MyLicense(this, this.GetKey(type))); } } // デザイン時はデフォルトの手順でライセンスファイルの内容をチェックする return base.GetLicense(context, type, instance, allowExceptions); } //------------------------------------------------------------------------------------- protected override string GetKey(Type type) { // ライセンスを付与するコンポーネントに GuidAttribute 属性で設定した GUID を返す return type.GUID.ToString(); } //------------------------------------------------------------------------------------- // base.GetLicense が呼び出すメソッド protected override bool IsKeyValid(string key, Type type) { bool check = false; if (key != null) { // ライセンスファイルの内容が key を含むかどうかをチェックする if (key.IndexOf(this.GetKey(type), StringComparison.CurrentCultureIgnoreCase) >= 0) check = true; } return check; } //************************************************************************************* // MyLicense class //************************************************************************************* private class MyLicense : License { private string FKey; private LicFileLicenseProvider FOwner; public override string LicenseKey { get { return FKey; } } public MyLicense(LicFileLicenseProvider owner, string key) { FOwner = owner; FKey = key; } public override void Dispose() { } } // end of MyLicense class } // end of MyLicenseProvider class } // end of namespace
以上で、ライセンスを付与するコントロールとライセンスプロバイダの定義は終わりです。次に、テスト用のプロジェクトを作成します。
ライセンスファイル(ファイル名は emanual.MyClassLibrary.lic)の内容は以下のとおりです。内容の GUID は MyClassLibrary コントロールに GuidAttribute 属性で指定したものと同じでなければなりません。
emanual.MyClassLibrary 9ED54F84-A89D-4fcd-A854-44251E925F09
コントロールを作成し、シェアウエア(有料ソフト)として公開する場合、何らかの保護措置が必要です。通常のアプリケーションの場合、ユーザーにパスワードを入力してもらい、正しければ正規のユーザーであると認めることにすることが多いと思います。しかし、コントロールの場合はパスワードを入力する手段がないので、この手は使えません。
パスワードを入力するアプリケーションを別に作る手はあります。ユーザーが正しいパスワードを入力すると、そのアプリケーションからライセンフファイルを作成することも可能です。
いろいろな要素を加味し、検討した結果、以下のような手順がいいのではないかと思います。
なぜ、こうするほうがいいかについて説明します。
といったところです。
上記の手順の代替案として、C/C++ 系言語あるいはそれに類する言語を使って、ライセンス関係の処理を実行する DLL またはアプリケーションを作って、パスワードを入力してもらう手もあります。
ライセンスファイルを使う方法はコントロールを保護するという意味では確実性が高いのですが、個々のユーザーにファイルを配布しなければなりません。したがって、パスワードを入力する形式のほうが望ましいといえます。そういう意味で、C/C++ 系言語を使って DLL を作る方法がユーザーにとってもコントロールの作者にとっても便利な方法ではないかと考えます。
ソフトベンダーから販売されているソフトの多くは、単純にパスワードを入力するのではなく、ベンダーの WEB サイトにアクセスし、ユーザー固有のキーワードなどの入力を求めてきます。つまり、個々のユーザーに直接使用の許諾を与えるようにすることで、不正な使用を防止しています。ユーザーにとっては面倒な話ではありますが、こうでもしなければ著作権を守ることは難しいということでしょう。
【追記】
ソースコードを難読化するツールがフリーウエアとして公開されています。逆アセンブルしてソースコードを復元することを難しくする程度の効果はありますが、.Net Framework の機能を使う部分までは難読化できません。つまり、だいたいの処理手順は見えてしまいます。ユーザーの能力と悪意の程度にもよりますが、大きな期待はしないほうがよさそうです。
この項は、「ついでに」というところですが、.Net Framework のライセンス関係のクラスはアプリケーション全体にも適用することができます。なぜなら、Form クラスも Control クラスを継承しているからです。フォームオブジェクトにライセンスを適用する基本的な手順はコントロールの場合と同じですが、アプリケーションの実行時にライセンスの有効性をチェックする点が異なります。
しかし、アプリケーションを保護する目的でライセンス関係のクラスを使うことに意味があるかどうかが問題です。私の答えは ない! です。ライセンスファイルを使ってアプリケーションを保護するつもりなら、通常のファイル操作で間に合います。
ライセンスを付与するコントロールを作ってフォームに貼り付けると、フォームを含むプロジェクトの properties ディレクトリ内に licenses.licx ファイルが自動的に作成されます。
このファイルがプロジェクト内にあると、プロジェクトをビルドするときにライセンコンパイラ(lc.exe)が起動し、アセンブリにライセンス情報を組み込もうとします。しかし、デザイン時にのみライセンスが必要で、実行時には必要ない場合はアセンブリに組み込む意味がありません。
さて、licenses.licx ファイルの [プロパティ] を見てください。その [ビルドアクション] は「埋め込まれたリソース」になっているはずです、これがデフォルトの動作だからです。ライセンス情報をアセンブリに組み込む必要がない場合は、[ビルドアクション] を「なし」に変更しておきましょう。これで、アプリケーションのビルド時にライセンスコンパイラが起動しなくなります。
−以上−