japan.internet.comThe Internet & IT Network
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
  • ニュースメール
2009年7月4日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
デベロッパー2005年10月11日 20:40

LicenseProvider によるソフトウェアのライセンス制御

海外海外internet.com発の記事
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!
  • この記事をokyuuへインポート

はじめに

 この記事を読んでいるということは、あなたはまず間違いなく Windows ソフトウェア開発者ですね。そして、あなたはきっと、ソフトウェアを書きたいという強い情熱に支えられてソフトウェアを書いているのでしょう。私が一日中コーディングに没頭しているのは、ただコーディングが好きだからです。同じように感じている人は多いのではないでしょうか。日々をこんなに楽しく過ごす方法がほかにあるでしょうか。

 しかし、それこそが落とし穴です。少し立ち止まって、ソフトウェアを書くことの経済的側面を考えてみてください。我々はこの仕事を好きでやっているわけですが、食べさせなければならない家族や、支払わなければならない請求書もありますし、ときには休日に出かけたりしたいというのが本音のはずです。これは偏った見方ではなく、我々のソフトウェアを使用している人も、自分たちの仕事に関して同じように考えていると思われます。たとえどんなに自分の仕事が好きで、その作業をせずにいられないとしても、仕事をすればその労力が報われることを望んでいます。

 問題は、ソフトウェアという製品はエンドユーザー側から見ると非常に漠然としたものであるという点です。彼らが目にするのはアプリケーション本体だけであり、それも、コンピュータのメモリに電気が通っているわずかな時間だけです。何百時間も何千時間もかけて開発やテストを行った製品のようにはとうてい見えませんし、少なくとも、車や冷蔵庫のような物体と物理的に比べられるものではありません。正直な話をすれば、車や冷蔵庫を盗もうなどと考えたこともない勤勉なコンピュータユーザーでも、ソフトウェアを友人と貸し借りすることについては何の抵抗も感じない人が多くいますし、ときにはソフトウェア付属の使用許諾契約に違反すると知っていながら貸借行為をする人もいます。ソフトウェアなんてCD上のただのビットにすぎないでしょう? そんなソフトウェアを兄弟や友達に貸してあげて、一体何が悪いというのでしょうか?

 あなたはこの質問に対する答えをよくご存知でしょう! そういうユーザーは、あなたの過酷な労働に対価を支払わなかった人です。ビジネス用語で言えば、あなたは相応の収入を得られなかったことになります。十分な収入が得られなければ、会社と同じように、あなたも破産するしかありません。個人レベルの話で言えば、あなたは金融上の義務(つまり支払うべき請求書)を遂行できなくなるので、どこかのファーストフードチェーンで働いてなんとか支払期日に間に合わせるか、破産宣告をして、高速道路の下でダンボール生活を送ることになります。私はそうなるのはごめんです。ですからユーザーには、アプリケーションの対価や、私がソフトウェアの設計、開発、テスト、マーケティング、配布に費やした労働に対する正当な対価を支払うことを要求したいと思います。

 そのための有力な解決策はソフトウェアライセンスです。ソフトウェア業界では、この問題を解決するためにさまざまな方法が試みられてきました。かつてのソフトウェアは、不正コピー防止装置を施されたメディアで販売されていました(追加トラック付きのフロッピーディスクをご記憶の方もいるでしょう)。最近では、何らかのソフトウェアキーを入力しないとソフトウェアパッケージが使用可能にならないという方式がよく使われています。Microsoft は、ハイエンドのオペレーティングシステムとアプリケーション製品(Office など)に関してはインターネットベースのオンライン認証を導入しています(Office はおそらくこれまでで最も多く違法コピーが行われた製品です)。.NET Framework には、あなたの作成したアプリケーションにライセンス機能を組み込むための有用な技術が含まれています。本稿ではこの技術について紹介します。

ライセンスの制御

 あなたがこれまでに読んだ.NETライセンス関連の資料では、ほとんどの場合、ライセンスの概念をコントロールと結び付けていたのではないでしょうか。つまり、コントロール開発者が、デザイン時または実行時のライセンス機能をコントロールに組み込んで配布するというケースです。その場合は、System.Windows.Forms.Controlから派生したどのクラスに対してもFrameworkライセンスを適用できます。これにはWindows Formsアプリケーションも含まれますが、まずはコントロールについて取り上げることにします。図1は、基本的なライセンス制御のしくみをUMLの静的クラス図で表したものです。

図1 .NETライセンス制御の静的クラス図
図1 .NETライセンス制御の静的クラス図

 図2は、全体的な実行シーケンスを表すUMLシーケンス図です。ライセンス付きコントロールは、コンストラクタの中でLicenseManagerにライセンスを要求します。

license = LicenseManager.Validate(typeof(MyLicensedControl), this);
図2 ライセンス制御のシーケンス図
図2 ライセンス制御のシーケンス図

 この場合、ライセンス付きコントロールのコンストラクタはMyLicensedControlクラス内で実装されます。ライセンスオブジェクトそのものについては、リソースがアタッチされているときにそれを適切に処理するという点を除いては、特にすることはありません(ライセンスの実装に依存します)。

 必要なのは、ライセンスマネージャを呼び出してライセンスを要求することだけです。何らかの理由でライセンスが許可されていないときは、Validate()の呼び出しが失敗して例外が生成されるか(例外が必要とされる場合)、nullライセンスが返されます(例外を抑制する場合)。どちらになるかはLicenseProvider.GetLicense()の呼び出しによって制御されますが、既定のFramework実装では例外が生成されます。

 呼び出されたライセンスマネージャは、コントロールのライセンスプロバイダのGetLicense()メソッドを呼び出します。では、.NET Frameworkコンポーネントであるライセンスマネージャは、使用するライセンスプロバイダをどうやって認識するのでしょうか。実は、ライセンスプロバイダを指定するのは開発者です。.NETの従来的な方法にのっとり、属性というメタデータを通じてライセンスプロバイダを指定します。

[LicenseProvider(typeof(MyLicenseProvider))]
public class MyLicensedControl : System.Windows.Forms.Control
{
   ...
}

 ライセンスマネージャは、ライセンス付きコントロールクラスにアタッチされているLicenseProvider属性を探します。LicenseProvider属性はライセンスプロバイダの型をコンストラクタへの入力として受け取るので、ライセンスマネージャはライセンスの検証が要求されたときにこの情報を使用することができます。ライセンスプロバイダはLicenseProviderクラスから派生させる必要があり、抽象メソッド(Visual Basicで言えばMustOverride)のGetLicense()をオーバーライドする必要があります。

public class MyLicenseProvider :
                System.ComponentModel.LicenseProvider
{
   ...

   public override License GetLicense(
      LicenseContext context,
      Type type,
      object instance,
      bool allowExceptions)
   {
      ...
   }
}

 .NETでのライセンス制御は以上のような流れになっていますが、結局のところ、実際のライセンス制御を行っているのはGetLicense()メソッドです。

 読者に無駄な骨折りをさせないためにあらかじめ教えておくと、実は、FrameworkにはLicFileLicenseProviderという基本的なライセンスプロバイダが用意されています。完璧を期すためにはここでLicFileLicenseProviderの使い方を説明しなければなりませんが、自作ソフトウェアにライセンス機能を組み込もうと本気で考えている人ならば、おそらくこのような単純な方式よりも、オリジナルのもっとクリエイティブな方式を使いたいと思うことでしょう。したがって、LicFileLicenseProviderについて説明した後に、そうした洗練された方式についても紹介したいと思います。

LicFileLicenseProvider

 LicFileLicenseProviderの機能はごくシンプルです。ライセンスファイルの有無を調べ、ファイルが存在する場合は、ファイルの内容を確認してライセンスを検証します。ライセンスファイルは、アプリケーションと一緒にディスク上に保存されている場合もあれば、アプリソースとしてエンコードされている場合もあります(その場合は情報を取り出すための追加コードが必要になります)。

 ライセンスファイルが存在し、有効な情報が書き込まれている場合は、このライセンスプロバイダがライセンスマネージャにライセンスを発行し、最終的にはコントロールにライセンスを許可します。それ以外の場合は、ライセンスプロバイダがicenseExceptionをスローし、コントロールは使用不能になります。

 LicFileLicenseProviderGetLicense()をオーバーライドするだけでなく(オーバーライドは必須)、IsKeyValid()GetKey()という追加の仮想メソッドを提供します。LicFileLicenseProviderをFrameworkの実装どおりのまま使用する場合は、GetKey()メソッドはアセンブリの実行ディレクトリ内で「{full name}.lic」という名前のファイルを探します。{full name}はライセンス付きコントロールクラスの完全タイプ名を表し、通常は「{assembly name}.{class name}」という形になります。たとえば、ここまでの例で使用してきたライセンス付きコントロールをアセンブリMyControlAssemblyが公開する場合は、ライセンスファイルの名前は次のようになります。

MyControlAssembly.MyLicensedControl.lic

 このファイルの内容を取得したら、IsKeyValid()メソッドはそれを文字列「{licensed class} is a licensed component.」(ピリオドを含む)と比較します。先ほどの例で言えば、「MyControlAssembly.MyLicensedControl.lic」ファイルには文字列「MyControlAssembly.MyLicensedControl is a licensed component.」が含まれているはずです。ライセンスファイルがない場合、またはファイルの内容が正しくない場合は、ライセンスの検証が失敗し、コントロールのインスタンス化が失敗します。ダウンロードサンプルとして、MSDNのサンプル色選択コンボボックスコントロールにライセンス機能を追加したものを用意したので、そちらも参照してください(記事上部よりダウンロードできます)。

 IsKeyValid()GetKey()は仮想メソッドなので、どこか別の場所でライセンスファイルを探したり、別の文字列(暗号化された文字列や、単純に別のフレーズを使用する文字列など)と比較したりする新しいクラスを簡単に派生できます。しかし、どんなに良い実装にしても、基本的にライセンスファイルを使用しているということには変わりありません。基底クラスがその機能を提供しているからです。これとはまったく異なるライセンス方式を実装したい場合は、LicenseProviderから直接ライセンスプロバイダを派生させた方がよいでしょう。自分で考えてみた方が身につきやすいと思うので、ここでは、派生したライセンスファイルプロバイダの例を示しません。その代わりに、別のライセンス方式をいくつか紹介することにします。

ライセンスファイルのコンパイル

 ライセンスファイルの話を終える前に、「lc.exe」について触れておきます。「lc.exe」は.NET Framework付属のライセンスコンパイラで、ライセンスファイル情報を取得し、アセンブリリソースとしてエンコードするツールです。このツールがFrameworkに付属しているのは少々不思議ですが(既定の実装であるLicFileLicenseProviderはリソースベースのライセンスファイルを参照しないので)、このツールにはおもしろい機能がいくつかあります。

 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アプリケーションはSystem.Windows.Forms.Formから派生したもので、これは元々System.Windows.Forms.Controlから派生している

 ということです。これは一体どういう意味でしょうか。

 これはつまり、アプリケーション全体に対しても、コントロールと同じ方法で簡単にライセンス機能を追加できるということです。これから紹介する方式はコントロールのライセンスではなくアプリケーションのライセンスを対象にしていますが、実際にはどちらにも適用できます。では、具体的な説明に進む前に、背景事情をもう少し説明しておきましょう。

アプリケーションライセンスとコントロールライセンス

 アプリケーションライセンスとコントロールライセンスの基本的な違いは、ライセンスをいつ検証するかです。あなたがコントロールやコンポーネントを開発して生計を立てているならば、ターゲット市場は他のアプリケーション開発者です。そのため、あなたのコントロールのライセンスでは、コントロールがデザイン時にVisual Studioプロジェクトに挿入されたときに検証を行います。自作のコントロールライブラリを何百万本も売りたいと思うならば、実行時ライセンスを無料で提供するか、実行時ライセンスをいっさい課さないのが普通です。大局的に見れば、ターゲット市場のアプリケーション開発者には、アプリケーションをどんどん使ってもらう必要があります。そうすれば、彼らはもっとたくさんのコントロールをあなたから買ってくれるからです。

 しかし、アプリケーションライセンスの場合は話が違います。あなたがアプリケーションを開発するときは、あなた自身がデザイン時にアプリケーションをビルドします。したがって、デザイン時ライセンスを自分自身に課しても意味がありません。その代わりに、ユーザーが実行時に適切なライセンス情報を持っているかどうかを確認する必要があります。

 Frameworkのライセンスアーキテクチャはこの点に配慮した設計になっており、GetLicense()メソッドを呼び出したときに、ライセンス要求の状態がデザイン時か実行時かを確認できるようになっています。以降では両方のケースを紹介しますが、今回の例ではコントロールのライセンス機能ではなくアプリケーション全体のライセンス機能の実装方法を取り上げたいので、実行時にのみライセンス検証を行うようにします。もっとも、これは重要な違いなのでしっかり意識する必要があります。

レジストリベースのライセンス

 レジストリベースのライセンスでは、特定の値を含んだレジストリキーの有無を調べるというライセンス方式を実装します。アプリケーション本体には、レジストリ値を書き込むためのコードを実装しません。この処理はインストールプログラムで行うようにします。ほとんどのアプリケーションは何らかの形でレジストリを使用しているので、これによって開発に大きな制限が課されることはありません。私もレジストリの登場以降は、インストールプログラムでレジストリキーを書き込むようにしています。

 本当ならばもう少しスマートな方法でレジストリに値を書き込むこともできるのですが、この例では単純に文字列「Installed」を書き込むことにします。もっと洗練された実装方法については、ぜひ自分で考えてみてください(私も手の内をすべて明かすわけにはいきませんから)。ここでは次のような値を探します。

HKEY_CURRENT_USERSoftwareAcmeHostKeysde915e1-df71-3443-9f4d-32259c92ced2

 ここで示したGUID値は、私が自分のアプリケーションに割り当てたGUIDです。Acme Softwareが販売しているアプリケーションはそれぞれ独自のGUIDを持っていますが、私はいろいろ考慮した結果、すべてのアプリケーションに同じライセンスプロバイダを使用しています。.NETの機能を利用して、すべてのAcme Softwareライセンスキーをここに入れているため、キー名が「HostKeys」になっています。あなたのアプリケーションでは、適切なところにキーを移動させてください。

 このアプリケーションは非常に単純です(図3を参照)。

図3 レジストリライセンスを実装したアプリケーション
図3 レジストリライセンスを実装したアプリケーション

 このウィンドウが表示されれば、ライセンスは有効です。ライセンスが無効な場合は、図4のような例外ダイアログボックスが表示されます。

図4 無効なレジストリライセンスについての応答
図4 無効なレジストリライセンスについての応答

 これを機能させるには、新しいアプリケーションを作成し、ウィザードによる自動生成コードを少し修正する必要があります。先に断っておきますが、ここでVisual Basicプログラマを除け者にするつもりはありません。サンプルファイルには、まったく同じC# .NETアプリケーションとVisual Basic .NETアプリケーションが含まれています。ここで紹介しているコードはC#ですが、Visual Basicプログラマでも大筋は簡単に理解できると思います。

 まずメインアプリケーションのソースコードを4か所修正し、さらに独自のライセンスプロバイダを記述する必要があります。修正する4か所とは次のとおりです。

  1. アプリケーションクラスの宣言(属性をいくつか追加)
  2. アプリケーションクラスのコンストラクタ
  3. アプリケーションクラスのDispose()メソッド
  4. アプリケーションのMain()メソッド

 GuidAttributeを使用するので、using句も追加します。それでは、まずクラス宣言から見ていきましょう。

/// <summary>
/// Summary description for frmMain.
/// </summary>
[GuidAttribute("2de915e1-df71-3443-9f4d-32259c92ced2")]
[LicenseProvider(typeof(RegistryLicenseProvider))]
public class frmMain : System.Windows.Forms.Form
{
   ...
}

 このクラスにGuidAttributeLicenseProviderという属性を追加しました。LicenseProviderについては前述のとおりです。GuidAttributeの目的は、このアプリケーションクラスにGUIDまたは一意の数値を割り当てることです。この属性は、通常はCOM相互運用機能のために使用されますが、ここでは既存のFrameworkサポートが持っている一意の数値識別子をアプリケーションクラスに割り当てるために再使用しています(これにより、クラスタイプのGUIDを後から簡単に特定できます)。独自の属性を生成することもできますが、そうするとFrameworkの組み込みのGUID検出サポートが使用できなくなります。また、GuidAttributeに割り当てたGUID値が、レジストリキーとして使用したGUID値と一致していることに注目してください。ライセンスプロバイダはこの値を反映して、アプリケーションのレジストリライセンスキー値を生成します。ここでGuidAttributeを使用するので、名前空間の一覧に次の行を追加しておく必要があります。

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);
}

 ここではライセンスマネージャのValidate()メソッドを呼び出しているだけで、特別なことは何もしていません。しかし、このValidate()メソッドは、ライセンスが無効の場合は例外をスローする可能性があります。したがって、この例外をどこかでキャッチするべきです。

 さらに、_licenseというプライベートデータメンバを追加していることに注目してください。ライセンス方式によってはこのライセンスオブジェクトを使用することもできますが(この例では使用していません)、どんな場合においても、アプリケーションの終了時にそのライセンスを破棄する必要があります。その際、ライセンスに割り当てられているリソースを適切に解放しなければならないことがあります。そこで、アプリケーションのDispose()メソッドを次のように修正します。

/// <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 );
}

 ここではnullライセンスかどうかを確認し、nullでない場合はDispose()メソッドを呼び出して、参照をnullにします。この例ではライセンスを使用していませんが、ライセンスが解放すべきリソースを持っている場合もあるので、このメソッドを用意する必要があります。

 最後に、アプリケーションのMain()メソッドを修正して、ライセンスマネージャが例外をキャッチできるようにします。

/// <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);
}

レジストリライセンスプロバイダ

 前にも述べたとおり、ライセンス検証処理の大部分はライセンスプロバイダのGetLicense()メソッド内で行われます。レジストリライセンスプロバイダのGetLicense()メソッドのコードを次に示します(ダウンロードサンプルの「RegistryLicenseProvider.cs」ソースファイル内にあります)。

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文は次のようになっています。

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に基づいてキーを生成します(ライセンスタイプにGuidAttributeを指定したことを思い出してください)。

string strLic =
   (string)licenseKey.GetValue(type.GUID.ToString());

 GetValue()メソッドは、キーに関連付けられている値かnullを返すので、まず戻り値がnullかどうかをテストします。その後、その戻り値を、有効なライセンスを表す正しい値と比較します。

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

 例外が許可されていない場合は、単純にnullを返します。

 この例ではライセンスクラスが同じですが、同じにする必要はありません。実行時ライセンスのクラスを次に示します。

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()
   {
   }
}

 この例では新しいライセンスタイプをLicense基底クラスから派生させているので、LicenseKeyプロパティをオーバーライドする必要があります(これは抽象メソッドです)。これにより、要求されたときにアプリケーションGUIDを返します。その他に、レジストリキーを再評価したり、有効期限やその他の無効化条件を確認したりすることもできます。

より高度なライセンス機能

 ここで紹介したテクニックは、ごく基本的なものです。たいていのユーザーは懇切丁寧な手助けがなければレジストリを開いてキーを探し出すことはできませんが、このライセンス機能を出し抜くことのできるユーザーも少なくありません。しかし、このライセンス機能をもっと破られにくくする方法はいろいろ考えられます。

 最もわかりやすい方法は、このサンプルで使用した単純な「Installed」という文字列を有効期限日時に置き換えることです(さらに値を暗号化することをお勧めします)。あるいは、もっと複雑な、アプリケーションの実行許可を要求するためにWindowsやWebサービスを呼び出すようなライセンスプロバイダを作成することもできます。

 私はどちらも実践したことがありますが、非常に効果的でした。また、前にも述べたとおり、何らかの無効化条件が与えられたときにライセンスオブジェクトに自身を無効化させたり、アプリケーションをシャットダウンさせたりすることも可能です。このようなライセンスオブジェクトは、おそらくライセンスWebサービスまたはライセンスデータベースを調べて、アプリケーションの日々のライセンス状況を判断するという形式になるでしょう。いずれのケースでも、製品アセンブリを見つけにくくすることが大切です。

終わりに

 本稿ではFrameworkの基本的なライセンスプロセスを学んできたわけですが、読者の中には、なぜFramework内での処理にこだわらなければならないのかと思った人もいるのではないでしょうか。結局のところ、独自のライセンスマネージャとライセンスプロセスを作成することは無理なのでしょうか?  Frameworkを使わないライセンスアーキテクチャ――たとえばGetLicense()にブール値を渡して例外を有効にするなどの方法はあり得ないのでしょうか? (私としては、GetLicense()にブール値を渡すよりも、LicenseMangerまたはライセンス付きクラスに属性またはプロパティを割り当てる方法を採用したいと思います。今のところ、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」で見ることができる。
関連テーマ
このエントリーを含むはてなブックマーク この記事をクリップ!
BuzzurlにブックマークBuzzurlにブックマーク Yahoo!ブックマークに登録
この記事をokyuuへインポート
最新トップニュース
データメーション
【データメーション】
中国が「Green Dam」フィルタ規制を撤回(7月1日)
Graphic Design Forum
【Graphic Design Forum】
Chris Dickman(6月25日)
プライバシー ジャパン・インターネットコム版
【プライバシー ジャパン・インターネットコム版】
グーグル・ストリートビューの問題について総務省の見解(6月23日)
エンジニアの独り言
【エンジニアの独り言】
システムを「使う」時代のエンジニアに求められるもの(6月2日)
最新ハイテク講座
最新ハイテク講座
電気は家庭でつくる時代へ!燃料電池「エネファーム」(7月3日)
アクセス解析で見るWebマーケティング
アクセス解析で見るWebマーケティング
決定力を探るアクセス解析(7月3日)
百式のネットビジネス研究
百式のネットビジネス研究
ファーストフードを高級っぽく盛り付けて紹介している「Fancy Fast Food」(7月3日)
週刊-サイト別アクセス状況データ
週刊-サイト別アクセス状況データ
ビデオリサーチインタラクティブ調査(月間インターネットオーディエンスデータ)(7月2日)
成約率、反応率を上げる Web 文章術
成約率、反応率を上げる Web 文章術
言葉がダイレクトにキャッシュを生む(7月2日)
不況時代の Web ビジネス最適化講座
不況時代の Web ビジネス最適化講座
アクセス解析エキスパートここだけの話、Web コンシェルジュの“勉強法”こっそり教えます(7月2日)
「Webからの脅威」―その傾向と最新対策
「Webからの脅威」―その傾向と最新対策
不正プログラムの分類(7月1日)
DevX
DevX
JavaScriptとDOMによる動的なWebページの作成(6月30日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
今のままで大丈夫?3匹の子ブタ的キャリア危険度診断(6月30日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
Web サイトは「無駄な穴のたくさん開いたじょうご」〜サイト成果向上の基本的な考え方(6月30日)
Copyright 2009 Japan Internet.com K.K. All Rights Reserved.http://www.internet.com/