|
ニュース検索
ピックアップ
トレンドワード
今週のIT求人情報
j.i.c メディア
|
LicenseProvider によるソフトウェアのライセンス制御はじめにこの記事を読んでいるということは、あなたはまず間違いなく Windows ソフトウェア開発者ですね。そして、あなたはきっと、ソフトウェアを書きたいという強い情熱に支えられてソフトウェアを書いているのでしょう。私が一日中コーディングに没頭しているのは、ただコーディングが好きだからです。同じように感じている人は多いのではないでしょうか。日々をこんなに楽しく過ごす方法がほかにあるでしょうか。 しかし、それこそが落とし穴です。少し立ち止まって、ソフトウェアを書くことの経済的側面を考えてみてください。我々はこの仕事を好きでやっているわけですが、食べさせなければならない家族や、支払わなければならない請求書もありますし、ときには休日に出かけたりしたいというのが本音のはずです。これは偏った見方ではなく、我々のソフトウェアを使用している人も、自分たちの仕事に関して同じように考えていると思われます。たとえどんなに自分の仕事が好きで、その作業をせずにいられないとしても、仕事をすればその労力が報われることを望んでいます。 問題は、ソフトウェアという製品はエンドユーザー側から見ると非常に漠然としたものであるという点です。彼らが目にするのはアプリケーション本体だけであり、それも、コンピュータのメモリに電気が通っているわずかな時間だけです。何百時間も何千時間もかけて開発やテストを行った製品のようにはとうてい見えませんし、少なくとも、車や冷蔵庫のような物体と物理的に比べられるものではありません。正直な話をすれば、車や冷蔵庫を盗もうなどと考えたこともない勤勉なコンピュータユーザーでも、ソフトウェアを友人と貸し借りすることについては何の抵抗も感じない人が多くいますし、ときにはソフトウェア付属の使用許諾契約に違反すると知っていながら貸借行為をする人もいます。ソフトウェアなんてCD上のただのビットにすぎないでしょう? そんなソフトウェアを兄弟や友達に貸してあげて、一体何が悪いというのでしょうか? あなたはこの質問に対する答えをよくご存知でしょう! そういうユーザーは、あなたの過酷な労働に対価を支払わなかった人です。ビジネス用語で言えば、あなたは相応の収入を得られなかったことになります。十分な収入が得られなければ、会社と同じように、あなたも破産するしかありません。個人レベルの話で言えば、あなたは金融上の義務(つまり支払うべき請求書)を遂行できなくなるので、どこかのファーストフードチェーンで働いてなんとか支払期日に間に合わせるか、破産宣告をして、高速道路の下でダンボール生活を送ることになります。私はそうなるのはごめんです。ですからユーザーには、アプリケーションの対価や、私がソフトウェアの設計、開発、テスト、マーケティング、配布に費やした労働に対する正当な対価を支払うことを要求したいと思います。 そのための有力な解決策はソフトウェアライセンスです。ソフトウェア業界では、この問題を解決するためにさまざまな方法が試みられてきました。かつてのソフトウェアは、不正コピー防止装置を施されたメディアで販売されていました(追加トラック付きのフロッピーディスクをご記憶の方もいるでしょう)。最近では、何らかのソフトウェアキーを入力しないとソフトウェアパッケージが使用可能にならないという方式がよく使われています。Microsoft は、ハイエンドのオペレーティングシステムとアプリケーション製品(Office など)に関してはインターネットベースのオンライン認証を導入しています(Office はおそらくこれまでで最も多く違法コピーが行われた製品です)。.NET Framework には、あなたの作成したアプリケーションにライセンス機能を組み込むための有用な技術が含まれています。本稿ではこの技術について紹介します。 ライセンスの制御 あなたがこれまでに読んだ.NETライセンス関連の資料では、ほとんどの場合、ライセンスの概念をコントロールと結び付けていたのではないでしょうか。つまり、コントロール開発者が、デザイン時または実行時のライセンス機能をコントロールに組み込んで配布するというケースです。その場合は、 図1 .NETライセンス制御の静的クラス図 図2は、全体的な実行シーケンスを表すUMLシーケンス図です。ライセンス付きコントロールは、コンストラクタの中でLicenseManagerにライセンスを要求します。 license = LicenseManager.Validate(typeof(MyLicensedControl), this); 図2 ライセンス制御のシーケンス図 この場合、ライセンス付きコントロールのコンストラクタは 必要なのは、ライセンスマネージャを呼び出してライセンスを要求することだけです。何らかの理由でライセンスが許可されていないときは、 呼び出されたライセンスマネージャは、コントロールのライセンスプロバイダの [LicenseProvider(typeof(MyLicenseProvider))] public class MyLicensedControl : System.Windows.Forms.Control { ... } ライセンスマネージャは、ライセンス付きコントロールクラスにアタッチされている public class MyLicenseProvider : System.ComponentModel.LicenseProvider { ... public override License GetLicense( LicenseContext context, Type type, object instance, bool allowExceptions) { ... } } .NETでのライセンス制御は以上のような流れになっていますが、結局のところ、実際のライセンス制御を行っているのは 読者に無駄な骨折りをさせないためにあらかじめ教えておくと、実は、Frameworkには LicFileLicenseProvider ライセンスファイルが存在し、有効な情報が書き込まれている場合は、このライセンスプロバイダがライセンスマネージャにライセンスを発行し、最終的にはコントロールにライセンスを許可します。それ以外の場合は、ライセンスプロバイダが MyControlAssembly.MyLicensedControl.lic このファイルの内容を取得したら、 ライセンスファイルのコンパイル ライセンスファイルの話を終える前に、「lc.exe」について触れておきます。「lc.exe」は.NET Framework付属のライセンスコンパイラで、ライセンスファイル情報を取得し、アセンブリリソースとしてエンコードするツールです。このツールがFrameworkに付属しているのは少々不思議ですが(既定の実装である 1つはリソース情報を簡単に生成できることですが、より興味深いのは、複数のライセンスファイルを1つのリソースにエンコードできることです。これにより、それぞれ異なるライセンスファイル文字列を持つ複数のコントロールのライセンス検証を一度に行えるようになります。ただし、このツールには大きな制約があります。それは、コンパイルしたモジュールとリソースをアセンブルするのにアセンブリリンカ(al.exe)を使用しなければならないという点です。コマンドライン派の人にとっては問題ないでしょうが、我々のようにVisual Studio .NETを使用する開発者にとっては大きな制約です。なぜならVisual Studio .NETでは、マルチモジュールアセンブリをコンパイルしたり生成したりできないからです(ただし、ビルドアクションを「埋め込まれたリソース」にすれば、リソース出力をプロジェクトファイルに埋め込むことは可能です)。 ここではVisual Studio .NETを取り上げましたが、どのバージョンのVisual Studio(2002または2003)でも、ライセンス付きコントロールを作成するための自動サポートはありません。ではライセンス付きコントロールを作成するにはどうしたらいいのでしょうか。答えは、単純に手動でライセンスファイルを作成し、コンパイルされたアセンブリと一緒に配布することです。私はいつもビルドアクションを「なし」に設定してライセンスファイルをVisual Studioプロジェクトに追加していますが、これは必須ではありません。こうするとライセンスファイルが他のプロジェクト/ソリューションファイルと一緒にソースリポジトリにまとめられるので、ソースコードの管理が少々簡単になるというだけです。引き続き、このファイルをアセンブリの実行ディレクトリに手動でコピーする必要があります。 別のライセンス方式私が思うに、Microsoftは.NET Framework用の既定のライセンスプロバイダを用意する必要に迫られて、ActiveXコントロールのライセンス機能に非常によく似た働きをするライセンスプロバイダを実装したのではないでしょうか。しかし、それと同時に、Microsoftはこのような単純なライセンス方式を自分たちの製品ソフトウェアに使用するつもりはなかったのではないかとも思います(少なくとも、重要なものについてはその意思はなかったでしょう)。既定の方式を使用する代わりに、より配布しやすく、賢く、出し抜かれにくく、何にでも使用できるライセンス方式を作成してみたらどうでしょうか。そこで、別のライセンス方式を考えてみることにします。 独自の方式を使用すれば、自作のコントロールにどんな機能のライセンスでも組み込むことができます。たとえば、火曜日にのみデザイン環境で使用できるコントロールや、ユーザーの地域の天気予報が「晴れ」の場合のみアプリケーション内で実行できるコントロール(ユーザーの住所の郵便番号を取得し、インターネットからその地域の天気予報をダウンロードする)などが考えられます。これは冗談ではなく、本当にこうしたことが可能なのです。そうする必然性はありませんが、そんな処理ができるくらい柔軟なライセンス方式だということです。以降では、もっと現実的な方法として、レジストリライセンスキーを使用するライセンス方式をご紹介します。しかしこの方式について詳しく説明する前に、1つ注意してほしいことがあります。それは、 .NET Windows Formsアプリケーションは ということです。これは一体どういう意味でしょうか。 これはつまり、アプリケーション全体に対しても、コントロールと同じ方法で簡単にライセンス機能を追加できるということです。これから紹介する方式はコントロールのライセンスではなくアプリケーションのライセンスを対象にしていますが、実際にはどちらにも適用できます。では、具体的な説明に進む前に、背景事情をもう少し説明しておきましょう。 アプリケーションライセンスとコントロールライセンスアプリケーションライセンスとコントロールライセンスの基本的な違いは、ライセンスをいつ検証するかです。あなたがコントロールやコンポーネントを開発して生計を立てているならば、ターゲット市場は他のアプリケーション開発者です。そのため、あなたのコントロールのライセンスでは、コントロールがデザイン時にVisual Studioプロジェクトに挿入されたときに検証を行います。自作のコントロールライブラリを何百万本も売りたいと思うならば、実行時ライセンスを無料で提供するか、実行時ライセンスをいっさい課さないのが普通です。大局的に見れば、ターゲット市場のアプリケーション開発者には、アプリケーションをどんどん使ってもらう必要があります。そうすれば、彼らはもっとたくさんのコントロールをあなたから買ってくれるからです。 しかし、アプリケーションライセンスの場合は話が違います。あなたがアプリケーションを開発するときは、あなた自身がデザイン時にアプリケーションをビルドします。したがって、デザイン時ライセンスを自分自身に課しても意味がありません。その代わりに、ユーザーが実行時に適切なライセンス情報を持っているかどうかを確認する必要があります。 Frameworkのライセンスアーキテクチャはこの点に配慮した設計になっており、 レジストリベースのライセンスレジストリベースのライセンスでは、特定の値を含んだレジストリキーの有無を調べるというライセンス方式を実装します。アプリケーション本体には、レジストリ値を書き込むためのコードを実装しません。この処理はインストールプログラムで行うようにします。ほとんどのアプリケーションは何らかの形でレジストリを使用しているので、これによって開発に大きな制限が課されることはありません。私もレジストリの登場以降は、インストールプログラムでレジストリキーを書き込むようにしています。 本当ならばもう少しスマートな方法でレジストリに値を書き込むこともできるのですが、この例では単純に文字列「Installed」を書き込むことにします。もっと洗練された実装方法については、ぜひ自分で考えてみてください(私も手の内をすべて明かすわけにはいきませんから)。ここでは次のような値を探します。 HKEY_CURRENT_USERSoftwareAcmeHostKeysde915e1-df71-3443-9f4d-32259c92ced2 ここで示したGUID値は、私が自分のアプリケーションに割り当てたGUIDです。Acme Softwareが販売しているアプリケーションはそれぞれ独自のGUIDを持っていますが、私はいろいろ考慮した結果、すべてのアプリケーションに同じライセンスプロバイダを使用しています。.NETの機能を利用して、すべてのAcme Softwareライセンスキーをここに入れているため、キー名が「HostKeys」になっています。あなたのアプリケーションでは、適切なところにキーを移動させてください。 このアプリケーションは非常に単純です(図3を参照)。 図3 レジストリライセンスを実装したアプリケーション このウィンドウが表示されれば、ライセンスは有効です。ライセンスが無効な場合は、図4のような例外ダイアログボックスが表示されます。 図4 無効なレジストリライセンスについての応答 これを機能させるには、新しいアプリケーションを作成し、ウィザードによる自動生成コードを少し修正する必要があります。先に断っておきますが、ここでVisual Basicプログラマを除け者にするつもりはありません。サンプルファイルには、まったく同じC# .NETアプリケーションとVisual Basic .NETアプリケーションが含まれています。ここで紹介しているコードはC#ですが、Visual Basicプログラマでも大筋は簡単に理解できると思います。 まずメインアプリケーションのソースコードを4か所修正し、さらに独自のライセンスプロバイダを記述する必要があります。修正する4か所とは次のとおりです。
/// <summary> /// Summary description for frmMain. /// </summary> [GuidAttribute("2de915e1-df71-3443-9f4d-32259c92ced2")] [LicenseProvider(typeof(RegistryLicenseProvider))] public class frmMain : System.Windows.Forms.Form { ... } このクラスに
using System.Runtime.InteropServices;
アプリケーションクラスのコンストラクタには、前述のコードを追加します。 private License _license = null; public frmMain() { // // Required for Windows Form Designer support // InitializeComponent(); // Obtain the license _license = LicenseManager.Validate(typeof(frmMain), this); } ここではライセンスマネージャの さらに、 /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } if (license != null) { license.Dispose(); license = null; } } base.Dispose( disposing ); } ここでは 最後に、アプリケーションの /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { // Create an instance of the licensed application frmMain app = null; try { // This will throw a LicenseException if the // license is invalid... if we get an exception, // "app" will remain null and the Run() method // (below) will not be executed... app = new frmMain(); } // try catch (Exception ex) { // Catch any error, but especially licensing errors... string strErr = String.Format("Error executing application: ’{0}’", ex.Message); MessageBox.Show( strErr, "RegistryLicensedApplication Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } // catch if ( app != null ) Application.Run(app); } レジストリライセンスプロバイダ 前にも述べたとおり、ライセンス検証処理の大部分はライセンスプロバイダの public override License GetLicense( LicenseContext context, Type type, object instance, bool allowExceptions) { // We’ll test for the usage mode...run time v. design time. // Note we only check if run time... if (context.UsageMode == LicenseUsageMode.Runtime) { // The Registry key we’ll check RegistryKey licenseKey = Registry.CurrentUser.OpenSubKey("SoftwareAcmeHostKeys"); if ( licenseKey != null ) { // Passed the first test, which is the existence of the // Registry key itself. Now obtain the stored value // associated with our app’s GUID. string strLic = (string)licenseKey.GetValue(type.GUID.ToString()); // reflected! if ( strLic != null ) { // Passed the second test, which is some value // exists that’s associated with our app’s GUID. if ( String.Compare("Installed",strLic,false) == 0 ) { // Got it...valid license... return new RuntimeRegistryLicense(type); } // if } // if } // if // if we got this far, we failed the license test. We then // check to see if exceptions are allowed, and if so, throw // a new license exception... if ( allowExceptions == true ) { throw new LicenseException(type, instance, "Your license is invalid"); } // if // Exceptions are not desired, so we’ll simply return null. return null; } // if else { return new DesigntimeRegistryLicense(type); } // else } ここで注目してほしいのは、現在の実行モードがデザイン時と実行時のどちらかを確認する部分のコードです。該当する if (context.UsageMode == LicenseUsageMode.Runtime) { ... // Run time mode } else { ... // Design time mode } // else デザイン時モードの場合は、デザイン時ライセンスオブジェクトのインスタンスを作成して返します。 return new DesigntimeRegistryLicense(type); 実行時モードの場合は、所定のレジストリキーを開き、アプリケーションのGUIDキー/値のペアを探します。ここでは次のようにしてレジストリキーを開きます。
RegistryKey licenseKey =
Registry.CurrentUser.OpenSubKey("SoftwareAcmeHostKeys");
その後、オブジェクトから取得したアプリケーションGUIDに基づいてキーを生成します(ライセンスタイプに string strLic = (string)licenseKey.GetValue(type.GUID.ToString()); if ( strLic != null ) { // Passed the second test, which is some value // exists that’s associated with our app’s GUID. if ( String.Compare("Installed",strLic,false) == 0 ) { // Got it...valid license... return new RuntimeRegistryLicense(type); } // if } // if 検証が成功した場合は、実行時ライセンスオブジェクトの新しいインスタンスを返します。そうでない場合は、例外が許可されているかどうかを確認し、許可されている場合は新しいライセンス例外をスルーします。 if ( allowExceptions == true ) { throw new LicenseException(type, instance, "Your license is invalid"); } // if 例外が許可されていない場合は、単純に この例ではライセンスクラスが同じですが、同じにする必要はありません。実行時ライセンスのクラスを次に示します。 public class RuntimeRegistryLicense : License { private Type type; public RuntimeRegistryLicense(Type type) { if ( type == null ) throw new NullReferenceException( "The licensed type reference may not be null."); this.type = type; } public override string LicenseKey { get { // Simply return the application’s GUID return type.GUID.ToString(); } } public override void Dispose() { } } この例では新しいライセンスタイプを より高度なライセンス機能ここで紹介したテクニックは、ごく基本的なものです。たいていのユーザーは懇切丁寧な手助けがなければレジストリを開いてキーを探し出すことはできませんが、このライセンス機能を出し抜くことのできるユーザーも少なくありません。しかし、このライセンス機能をもっと破られにくくする方法はいろいろ考えられます。 最もわかりやすい方法は、このサンプルで使用した単純な「Installed」という文字列を有効期限日時に置き換えることです(さらに値を暗号化することをお勧めします)。あるいは、もっと複雑な、アプリケーションの実行許可を要求するためにWindowsやWebサービスを呼び出すようなライセンスプロバイダを作成することもできます。 私はどちらも実践したことがありますが、非常に効果的でした。また、前にも述べたとおり、何らかの無効化条件が与えられたときにライセンスオブジェクトに自身を無効化させたり、アプリケーションをシャットダウンさせたりすることも可能です。このようなライセンスオブジェクトは、おそらくライセンスWebサービスまたはライセンスデータベースを調べて、アプリケーションの日々のライセンス状況を判断するという形式になるでしょう。いずれのケースでも、製品アセンブリを見つけにくくすることが大切です。 終わりに 本稿ではFrameworkの基本的なライセンスプロセスを学んできたわけですが、読者の中には、なぜFramework内での処理にこだわらなければならないのかと思った人もいるのではないでしょうか。結局のところ、独自のライセンスマネージャとライセンスプロセスを作成することは無理なのでしょうか? Frameworkを使わないライセンスアーキテクチャ――たとえば 私の考えでは、独自のライセンスアーキテクチャではなくFrameworkのライセンスアーキテクチャを使用した方が、将来的には得をすると思います。Frameworkで決められたパターンに従っていれば、Frameworkに変更が加えられても対応できる可能性があります。また、Frameworkコンポーネントを利用できるため、再利用や保守コストの節約につながります。 ライセンス機能そのものに関して言えば、想像力しだいでいろいろなことが実現可能です。ぜひライセンス機能を実装して、これまで失ってきた収入を取り戻そうではないですか! 著者紹介Kenn Scribner(Kenn Scribner)
複雑なWindowsプログラミングやCOMプログラミングについて教えたり相談を受けたりすることを、自分の勤めと思っている。趣味を仕事として追求できることに喜びを感じ、自分の経験を他の開発者と分かち合うことを楽しんでいる。これまでの著書に『MFC Programming with Visual C++ 6 Unleashed』、『Teach Yourself ATL in 21 Days』、『Understanding SOAP』、『Applied SOAP: Implementing .NET Web Services』(最新刊)がある。その他にも、Mike Blasczak著『Professional MFC with Visual C++ 6』などの刊行に協力している。また、Visual C++ DeveloperにXMLおよびSOAP関連の記事を多数執筆。Kenn Scribnerの記事(ならびに数々のプロダクトおよびコードサンプル)は彼の個人Webサイト「EnduraSoft」で見ることができる。
新着ニュース・コラム ホワイトペーパー
|
注目のトピックス アクセスランキング
最新コラム一覧
|
|||||||||||||||||||||||