C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

--/--/-- --:--
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
タグ:
トラックバック(-) | コメント(-) | このエントリーを含むはてなブックマーク
2006/10/29 20:53
スポンサーサイト
タグ: .NET IronPython
2006/10/24 17:58
最終更新日 : 2007/03/23


実務経験を書き出してみた。
文字色が灰色の項目は経験薄。

[ 言語 ]
C# 2.0
C# 1.0
Visual Basic 2005
Visual Basic .NET 2003
Visual Basic 6.0
Java ( JSP / Servlet )
Managed C++
JavaScript
Transact-SQL
HTML
CSS
XML
UML 2.0

[ テクノロジー ]
Windows Communication Foundation
ADO.NET 2.0
ADO.NET 1.1
ASP.NET 2.0
ASP.NET 1.1
Windows アプリケーション
XML Web サービス
ClickOnce
ノータッチ・デプロイメント
InfoPath 2003
SQL Server 2005 Business Intelligence
Windows SharePoint Services 3.0
Windows SharePoint Services 2.0
SharePoint Portal Server 2003

[ DB ]

SQL Server 2005
SQL Server 2000
MySQL

[ プラットフォーム ]
Windows XP Professional Edition
Windows Server 2003

[ 資格 ]
Microsoft Certified Application Developer
Microsoft Certified Professional
OMG認定UML技術者資格試験 インターメディエイト
OMG認定UML技術者資格試験 ファンダメンタル
タグ: 実務経験
2006/10/20 01:49
MSDNに記載されている、Delegate.DynamicInvokeメソッドの例外に関する情報が間違ってる。
MSDNには以下のように記載されている。


[ MemberAccessException ]
呼び出し元には、(たとえば、メソッドがプライベート メソッドの場合に) デリゲートが表すメソッドへのアクセス権がありません。 または args にリストされているパラメータの数、順序、または型が無効です。

[ TargetException ]
デリゲートが表すメソッドがインスタンス メソッドであり、対象オブジェクトが null 参照 (Visual Basic では Nothing) です。 または デリゲートが表すメソッドが、そのメソッドをサポートしないオブジェクトまたはクラスに対して呼び出されています。

[ TargetInvocationException ]
カプセル化されたメソッドの 1 つが例外をスローします。


しかし、TargetInvocationException以外の例外&説明は、実際と全く異なる。

第一に、デリゲートが表すメソッドがプライベートメソッドだろうが、そのメソッドがプライベートメソッドを呼び出していようが、例外なんて発生しない。また、デリゲートが表すメソッドの要求するアクセス許可が呼び出し元になくて、System.Security.SecurityExceptionがスローされる場合も、MemberAccessExceptionなどスローされず、 ( SecurityExceptionをラップした ) TargetInvocationExceptionがスローされる。

第二に、argsにリストされているパラメータの数、順序、型が無効な場合でも、MemberAccessExceptionはスローされない。パラメータ の数が不正な場合はTargetParameterCountExceptionがスローされ、順序や型が向こうな場合は ArgumentExceptionがスローされる。

第三に、 「 デリゲートが表すメソッドがインスタンス メソッドであり、対象オブジェクトがnull参照 」 なんて状況はありない。 「 対象オブジェクトがnull参照 」 というのは、対象オブジェクトがGCに回収されている状況を指しているのだろうが、デリゲートが存在する限り対象オブジェクトがGCに回収されることはない。デリゲートのTargetプロパティにしっかりと対象オブジェクトの"強い"参照が保持されているから。

第四に、 「 デリゲートが表すメソッドが、そのメソッドをサポートしないオブジェクトまたはクラスに対して呼び出されています。  」 なんて状況もありえない。
まず、以下のように、デリゲートオブジェクトを普通に作成した場合、当然、メソッドとオブジェクトは正しく結びつく。
public delegate void SimpleMethod();

class Class1
{
    public Class1()
    {
    }

    public void Method1()
    {
        Console.WriteLine("Method1 was called.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Class1 obj = new Class1();
        Delegate objMethod1 = (SimpleMethod)obj.Method1;
        objMethod1.DynamicInvoke();
        Console.ReadLine();
    }
}

また、リフレクションを使って作成したとしても、無関係のオブジェクトと関連付けて作成することは不可能である。
public delegate void SimpleMethod();

class Class1
{
    public Class1()
    {
    }

    private void Method1()
    {
        Console.WriteLine("Method1 was called.");
    }
}

class Program
{
    static void Main(string args)
    {
        Assembly thisAssembly = Assembly.GetExecutingAssembly();
        Type class1 = thisAssembly.GetType("Class1");
        MemberInfo[] class1Member = class1.GetMember("Method1", MemberTypes.Method, BindingFlags.Instance | BindingFlags.NonPublic);
        MethodInfo class1Method1Info = class1Member[0] as MethodInfo;
        // 以下のコードでは、実行時に例外がスローされる。
        // Delegate class1Method = Delegate.CreateDelegate(typeof(SimpleMethod), new Object(), class1Method1Info);
        Delegate class1Method = Delegate.CreateDelegate(typeof(SimpleMethod), new Class1(), class1Method1Info);
        class1Method.DynamicInvoke();
        Console.ReadLine();
    }
}

( ついでに、対象メソッドの可視性はプライベートにしてある。 )


で、結局のところ、Delegate.DynamicInvokeメソッドの例外は、正しくは以下のようになる。

[ System.ArgumentException ]
args にリストされているパラメータの順序、または型が無効である場合にスローされる。

[ System.Reflection.TargetParameterCountException ]
args にリストされているパラメータの数が、必要なパラメータ数と異なる場合にスローされる。

[ System.Reflection.TargetInvocationException ]
カプセル化されたメソッドの 1 つが例外をスローする場合にスローされる。
タグ: C# .NET
2006/10/13 02:17
通常、Windowsアプリでは、イベントハンドラ内で重い処理を行う際は、フォームの一時的なハングを防ぐために重い処理を別スレッドで行う。
その時気をつけなければならないのが、コントロールの操作。コントロールは、コントロール (  の基になるウィンドウハンドル ) を所有しているスレッドで操作しなければならない。別スレッド内でそのままコントロールを操作してしまうと、正常に動作してくれない。 ( .NET 2.0から特に。 )
では、別スレッドで重い処理を行ったあと、どうやってコントロールを所有しているスレッドに処理を行わせるか?それには、Control.Invokeメソッドを使用する。Control.Invokeは、引数で受け取ったデリゲートオブジェクトを、そのコントロールを所有しているスレッド上で実行するメソッドである。

Contorol.Invokeの第一引数はDelegate型である。この引数に渡すためのデリゲートには、System.Windows.Forms名前空間にある、MethodInvokerデリゲートというデリゲートを使うのがいい 。MethodInvokerには、引数も戻り値もない。このデリゲートを使えば、Invoke用にわざわざデリゲートを自作する必要がなくなる。MSDNにもはっきりと、 「 簡単なデリゲートが必要だが定義するのが面倒な場合に使用できます。 」 と書かれている。

C#2.0の新機能である匿名メソッドを用いると、これら一連の処理が ( わりと ) 見やすくかける。匿名メソッドによって、無駄にメソッドを定義する必要がなくなり、デリゲートが扱いやすくなった。 ( 匿名メソッドはデリゲートとして扱えるが、直接Delegateクラスに変換することはできないことに注意。 )

先に述べたとおり、Control.Invokeメソッドはコントロール ( の基になるウィンドウハンドル ) を所有しているスレッドに処理を行わせる。従って、もし、重たい処理を行っている最中にフォームが閉じられたりして、ウィンドウハンドルが破棄されてしまうと、その後に呼び出されるControl.InvokeメソッドがInvalidOperationExceptionをスローすることになる。
重たい処理中に、フォームが閉じられてしまう可能性がある場合は、Control.IsHandleCreatedプロパティを利用し、コントロールにハンドルが関連付けられているかどうかを判別する必要がある。

以下に、重い処理を別スレッドで行い、処理の前後でフォーム上の各コントロールのEnabledプロパティを切り替えるサンプルコードを示す。

サンプル1// Form1.cs
private void button1_Click(object sender, EventArgs e)
{
    this.SetEnabledOfControls(false);
    Thread process = new Thread(
        delegate()
        {
            Thread.Sleep(5000); // 重たい処理の代用

            if (this.IsHandleCreated)
            {
                this.Invoke(
                    (MethodInvoker)delegate()
                    {
                        this.SetEnabledOfControls(true);
                    });
            }
        });
    process.Start();
}

private void SetEnabledOfControls(bool value)
{
    // 各コントロールのEnabledプロパティにvalueを設定する処理
}


コントロール操作を行うメソッド内でInvokeメソッドを利用するという手もある。その場合、コントロール操作を行うメソッドが別スレッド上で実行されるとは限らないので、
Control.InvokeRequiredプロパティを利用し、現在のスレッドがコントロールの所有スレッドなのかどうかを判別する。
以下にサンプルを示す。

サンプル2// Form1.cs
private void button1_Click(object sender, EventArgs e)
{
    this.SetEnabledOfControls(false);
    Thread process = new Thread(
        delegate()
        {
            Thread.Sleep(5000); // 重たい処理の代用
            this.SetEnabledOfControls(true);
        });
    process.Start();
}

private void SetEnabledOfControls(bool value)
{
    if (!(this.IsHandleCreated))
    {
        return;
    }
    MethodInvoker process =
        (MethodInvoker)delegate()
        {
            // 各コントロールのEnabledプロパティにvalueを設定する処理
        };
    if (this.InvokeRequired)
    {
        this.Invoke(process);
    }
    else
    {
        process.Invoke();
    }
}


以上2つの方法の内どちらを採用するにしろ、コントロール操作の度にControl.IsHandleCreatedプロパティとControl.InvokeRequiredプロパティ ( これは一つ目の方法だと不要 ) のチェックを行ってControl.Invokeメソッドを呼び出すのでは、コードがやや冗長である。
以下のように、静的クラスを用意して、静的メソッドとして定義してやると良い。

public static class ControlUtil
{
    public static object SafelyOperated(Control context, Delegate process)
    {
        return ControlUtil.SafelyOperated(context, process, null);
    }

    public static object SafelyOperated(Control context, Delegate process, params object[] args)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (process == null)
        {
            throw new ArgumentNullException("process");
        }

        if (!(context.IsHandleCreated))
        {
            return null;
        }
        if (context.InvokeRequired)
        {
            return context.Invoke(process, args);
        }
        else
        {
            return process.DynamicInvoke(args);
        }
    }
}


この静的メソッドは、以下のように利用する。 ( Control.Invokeメソッドと同じく、引数を指定したり、戻り値を取得することも可能。 )

SafelyOperatedメソッドを用いてサンプル2のSetEnabledOfControlsメソッドを記述private void SetEnabledOfControls(bool value)
{
    MethodInvoker process =
        (MethodInvoker)delegate()
        {
            // 各コントロールのEnabledプロパティにvalueを設定する処理
        };
    ControlUtil.SafelyOperated(this, process);
}
2006/10/08 04:29
業務エラーというのは、業務固有(アプリケーション固有)のエラーのこと。
例えば、DBに業務情報を格納しておくWebアプリで、あるユーザーが業務情報を登録しようとした時に、その業務情報に関連する情報が他のユーザーによって削除されていて、外部キー制約違反が起こってしまう場合などが該当する。


[ 業務エラーはApplicationExceptionで? ]

オレは今まで、業務エラー時にはApplicationException派生の自作クラスをスローしてた。
だって、ApplicationExceptionの説明には


ApplicationExceptionは、共通言語ランタイムではなく、ユーザー プログラムによってスローされます。デザインしているアプリケーションで固有の例外を作成する必要がある場合は、ApplicationException クラスの派生クラスを生成します。ApplicationException クラスは Exception を拡張しますが、新しい機能は追加しません。この例外は、アプリケーションで定義された例外とシステムで定義された例外を区別する手段として提供されます。


って書いてあるし、

Application Architecture for .NET ( 通称AAfN )第三章にも、


例外クラスは、ApplicationException の派生クラスとして作成します。


とか、


例外には大きく分けて、業務上の例外と技術上の例外の 2 種類があります。例外をこのように分類することにより、アプリケーションのさまざまなコンポーネントで該当する種類の例外をキャッチし、発行する処理を簡単に実現できるようになります。




  • 技術上の例外 (データベース接続の失敗など)

  • 業務上の例外 (外部キー制約の違反など)




って書いてあるから。(あとここにも。)



[ 業務エラーは戻り値で? ]
でも、クラス ライブラリ開発のデザイン ガイドラインの中のカスタム例外のデザインでは、


 「標準の例外の種類のキャッチとスロー」のガイドラインに示されているように、ApplicationException からカスタム例外を派生させることは推奨されていない点に注意します。

なんて書いてある。 ( 対象がクラス ライブラリ開発だからか・・・? )

書籍 「 Microsoft Visual Studio 2005 による Webアプリケーション構築技法 」 でも、 「 6.4 例外処理 」 で、DBに対して顧客情報を新規登録するメソッドを例に挙げ、終了パターンを以下のように3つに大別した上で、


・正常終了
受け渡された顧客情報を利用して、正常にデータベースに顧客情報が登録された場合。

・業務エラー
希望された顧客IDがすでに他の顧客により利用されていたといった理由により、顧客情報が登録できなかった場合。

・アプリケーション/システムエラー
データベース障害やネットワーク障害により、データベースの読み書きが行えなかった場合。


それぞれの表現方法を以下のように示している。 ( 実際は表形式 )


分類:
正常終了

対応するケース:
業務で期待された主たる処理が問題なく終了した場合

.NETでの表現方法:
戻り値の一部として表現


分類:
業務エラー

対応するケース:
業務設計の中で想定されている範囲内で、処理が分岐し、正常終了できなかった場合

.NETでの表現方法:
戻り値の一部として表現


分類:
アプリケーション/システムエラー

対応するケース:
業務設計の想定範囲外の異常事態が発生し、アプリケーション処理を正しく遂行できなくなった場合

.NETでの表現方法:
例外を用いて表現





[ ふむ・・・ ]
ということで、業務エラーについては、以下の2つの意見があるようだ。

・ApplicationExceptionの派生クラスを定義し、それをスローする。
・例外は用いず、戻り値 ( BooleanとかEnumってことかな ) で表す。

どっちがいいんだろ?
いや、単に人それぞれの好みで選んでいいのかな?
考察してみると面白そうだし、もしかしたらそのうち考察記事書くかも。
タグ: .NET C# 例外処理
2006/10/08 02:06
C#のコーディング規約。
基本はクラス ライブラリ開発のデザイン ガイドラインに従うとして、それ以外の範囲で俺的な規約。

[ 俺規約 ]
自身のインスタンスメンバには、thisキーワードを必ず付ける。

[ 理由 ]
・自身のインスタンスメンバであることが一目でわかり、可読性が向上する。 ( ローカル変数とインスタンスフィールドを一目で区別できる。静的メソッドとインスタンスメソッドを一目で区別できる。 )
・インテリセンスも利用できるし。
・" this. " というたった5文字を入力することによる手間・冗長化なんてたいした事ない。それよりも可読性の向上、インテリセンスの利用といったメリットの方が遥かに大きい。

[ 補足 ]
同様に、自身の静的メンバには、クラス名を必ず付ける。 なんとなく不自然さを感じるけど我慢。あと、VS2003では、クラス名が変更されたときに自分でちゃんと置換を行わないといけないので注意。 ( まぁ、それは " 自身の " に限らないけど。 )
つーか、自身の静的メンバを参照するためのキーワードがあればいいのにな (´・ω・`;)
2006/10/04 19:37
以前、コーディング俺規約 - フィールドの初期化という記事で、フィールドの初期化はコンストラクタで行った方がいいと書きました。そして、 「静的フィールドも同様に静的コンストラクタで初期化する。」 と書きました。が、静的フィールドの初期化に関しては、静的コンストラクタで行うのは不適切なようでした。理由は、以下に記述。


静的コンストラクタはインスタンスコンストラクタと違い、コンパイル時に特殊な処理が行われる。

静的コンストラクタを明示的に宣言しない場合、コンパイラが静的コンストラクタを用意してくれる。しかし、それだけではなく、beforefieldinitというフラグがそのクラスのMSIL定義に追加される。
静的コンストラクタを明示的に宣言した場合は、beforefieldinit フラグは追加されない。

beforefieldinit フラグがない(明示的に静的コンストラクタを宣言した)場合、その型の静的フィールドに初めてアクセスしようとした時、静的コンストラクタが実行される。また、静的コンストラクタが実行されたかどうかを確認するためのチェックが加えられる。

beforefieldinit フラグがある(明示的に静的コンストラクタを宣言しない)場合、その型の静的フィールドに初めてアクセスするよりも前に、静的コンストラクタが実行されることが保証される。beforefieldinitフラグがあると、ほとんどの場合、静的コンストラクタが実行されたかどうかを確認するチェックは加えられない。

静的コンストラクタが実行されたかどうかのチェックが加えられると、パフォーマンスが低下する場合があるらしい。
よって、静的フィールドの初期化は宣言時に行い、静的コンストラクタは明示的に宣言しないようにするのが適切である。


[ 検証 ]
以下のような検証用プログラムを組んでみた。

検証用プログラムclass Program
{
    static void Main(string[] args)
    {
        double time1 = Hoge.InitTime.Subtract(DateTime.Now).TotalSeconds;
        double time2 = Foo.InitTime.Subtract(DateTime.Now).TotalSeconds;
        Console.WriteLine("beforefieldinitフラグありの場合の初期化:{0}", time1);
        Console.WriteLine("beforefieldinitフラグなしの場合の初期化:{0}", time2);
        Console.ReadLine();
    }

    static class Hoge
    {
        public static DateTime InitTime = DateTime.Now;
    }

    static class Foo
    {
        public static DateTime InitTime;

        static Foo()
        {
            Foo.InitTime = DateTime.Now;
        }
    }
}


検証用プログラムをコンパイルすると、HogeクラスとFooクラスのMSILコードは、以下のようになる。Hogeクラスのクラス宣言部にはbeforefieldinit フラグがあり、Fooクラスのクラス宣言部にはbeforefieldinit フラグがないことが確認できる。

HogeクラスのMSILコード.class nested private abstract auto ansi sealed beforefieldinit Hoge
    extends object
{
    .method private hidebysig specialname rtspecialname static void .cctor() cil managed
    {
        .maxstack 8
        L_0000: call [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
        L_0005: stsfld [mscorlib]System.DateTime ConsoleApplication1.Program/Hoge::InitTime
        L_000a: ret
    }

    .field public static [mscorlib]System.DateTime InitTime
}

FooクラスのMSILコード.class nested private abstract auto ansi sealed Foo
    extends object
{
    .method private hidebysig specialname rtspecialname static void .cctor() cil managed
    {
        .maxstack 8
        L_0000: nop
        L_0001: call [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
        L_0006: stsfld [mscorlib]System.DateTime ConsoleApplication1.Program/Foo::InitTime
        L_000b: ret
    }

    .field public static [mscorlib]System.DateTime InitTime
}


検証用プログラムを実行すると、以下のような出力を得られる。

実行結果
beforefieldinitフラグありの場合の初期化:-0.03125
beforefieldinitフラグなしの場合の初期化:0


この結果から、beforefieldinitフラグありの場合、静的フィールドにアクセスするよりも前に、静的フィールドの初期化が行われていることがわかる。


[ 参照 ]
値型の静的フィールドをインラインで初期化します
参照型の静的フィールドをインラインで初期化します


タグ: .NET C#
2006/10/04 03:53
電子署名とは、電子的なデータやファイルの正当性を保証するための技術である。
電子署名にはいくつか種類があるが、RSA暗号化方式を用いた電子書名が特に有名である。
この記事では、RSA暗号化方式について解説する。

[ RSA暗号化方式 ]
公開鍵基盤(PKI)のひとつ。
秘密鍵と公開鍵という2種類の鍵と、電子証明書から成る。
RSA以外にもDSA等の方式があるが、RSAと他の方式では、秘密鍵と公開鍵の性質や署名の生成方法等が異なるので注意。


[ 電子証明書 ]
電子的な身分証明書。信頼できる第三者(認証局)に発行してもらう。
電子証明書には、認証局の電子証明書によって電子署名が施される。これにより、信頼できる第三者が発行したことが証明される。
認証局に発行してもらった電子証明書には秘密鍵と公開鍵が含まれている。また、他者に公開するためには、公開鍵のみを含んだ電子証明書を公開する。

実は、電子署名の発行は専用のツール(無償で入手可能)さえあれば誰にでもできる。自分自身の電子証明書を作ることも可能で、これは一般に「オレオレ証明書」と呼ばれていて、通常、この証明書には信頼性が全くない。

日本では、2001年4月1日から電子署名法が施行された。これにより、認定を受けた認証業務 (総務大臣,経済産業大臣,法務大臣の認定を受けた電子証明書発行サービス) で発行してもらった電子証明書による電子署名は、法的な効力を持つ。(発行は有償。また、電子証明書には有効期限がある。)
電子証明書はいくつも種類があり、どれを(どの認証局で)購入すればいいかは利用目的により異なる。


[ 秘密鍵と公開鍵 ]
秘密鍵は本人以外の手に渡ってはいけないもので、本人が厳重に保管する必要がある。
公開鍵は本人以外に公開してよいものである。
秘密鍵と公開鍵は暗号化・復号化を行うためのものである。
秘密鍵と公開鍵は対になっていて、秘密鍵で暗号化したデータは公開鍵でのみ復号化でき、公開鍵で暗号化したデータは秘密鍵でのみ復号化できる。


[ 電子署名 ]
データに対する電子的な署名。
電子署名は、対象のデータのハッシュ値を秘密鍵で暗号化したものである。
電子証明を公開鍵によって復号化した値と、対象データのハッシュ値を比較することで、 データ改竄の有無を調べることができる。

exeファイルやdllファイル、pdfファイル等では、電子署名をファイルに埋め込むことができる。
だが、jpegファイルなどではファイルに埋め込むことができないので、電子署名は独立した一つのファイルとなる。

電子署名は、データの正当性と署名を行った人物を証明する。しかし、署名を行った日時を証明することはできない。日時の証明には「タイムスタンプ」という技術 を利用する。 (タイムスタンプも電子署名を利用した技術である。簡単に説明すると、対象のデータのハッシュ値に国際原子時を加えたものに電子署名を行ったものがタイムスタンプとなる。)

2006/10/01 12:11
アセンブリには、署名(電子署名)を行うことができる。

一般的に、共有DLLには、署名を行うことが推奨される。
GACにインストールするDLL、ClickOnceのアプリケーション マニフェストと配置ファイルには、署名が必須となる。
また、一般的に、EXEファイル、一つのアプリケーション固有のDLLには、署名を付けないことが推奨される。

【 アセンブリの署名による利点 】
アセンブリの署名により、主に以下のような効果が得られる。

[ 1. DLLをGACにインストールできる ]
GACとは、一台のPC内で、グローバルに参照できる特殊な領域のことである。GACにインストールされたDLLは、一台のPC内で共有される。
具体的な場所は、 [ %SystemRoot%\assembly ] となる。 [ %SystemRoot% ] は、Windows XPなら通常 [ C:\WINDOWS ] を示す。
GACへのインストールは手動ででき、単にDLLをGACへドラッグ&ドロップするだけでインストールされる。また、グローバル アセンブリ キャッシュ ツール (Gacutil.exe) を使ってインストールすることもできる。Gacutil.exeを使ってインストールするには、.NET Framework付属のコマンドプロンプトで以下のコマンドを実行する。FileNameには、DLL名を記述する。gacutil /i FileNameなお、.NET Framework クラスライブラリ (以降、NFCと表記) も、ここにインストールされている。


[ 2. DLLのバージョン管理が行える ]
例えば、.NET Framework  (以降、.NET Fxと表記) 1.1のNFCは、バージョン1.0.5000.0で、.NET Fx 2.0のNFCはバージョン2.0.0.0であるが、これらはGAC内に共存できる。
これらが共存しているPCで、.NET Fx 1.1をベースに作成されたアプリケーションを実行するとどうなるか?答えは、バージョン1.0.5000.0のNFCが利用される。つまり、バージョンの異なるDLLが存在しても、アプリケーションは決められたバージョンのDLLを利用するということである。
DLLのバージョン管理が行われることで、DLL Hellが解消されるという利点が得られる。
なお、バージョン管理は、GAC内のDLLに限らず、アプリケーションのインストールディレクトリ内のDLLにも適用される。

(実は、NFCは下位互換性があり、また特別なので、.NET Fx 1.1がインストールされていない場合、.NET Fx 1.1ベースのアプリケーションは.NET Fx 2.0上で動き.NET Fx 2.0のNFCを利用するわけだけど^^; )


[ 3. アセンブリの改竄を検出できる ]
これは電子署名そのものの性質 (詳しくは次回) でもある。
アセンブリを誰かが勝手に改竄した場合、アプリケーションの実行時にそれを検出することができる。
この利点が、ある意味一番大きな利点かもしれない。
ただし、署名に使った秘密鍵が第三者の手に渡らないことが大前提である。署名に使った秘密鍵さえあれば、改竄した後に再び署名を行うことができてしまうため、秘密鍵は厳重に保管しなければならない。


【 アセンブリに署名を行う方法 】
アセンブリに電子署名を行うには、RSA暗号化方式 (詳しくは次回) の秘密鍵と公開鍵という、対になった鍵 (以降、キーペアと表記) が必要である。
キーペアを作成し、キーペアを元にアセンブリに署名する手順を以下に記述する。

[ 従来からの方法 ]
.NETでは、キーペアを作成するためのツールとして、厳密名ツール (Sn.exe) が提供されている。
使用方法は簡単で、.NET Framework付属のコマンドプロンプトで以下のコマンドを実行すれば、キーペアがファイルとして作成される。outfileには、出力ファイル名を記述し、拡張子は.snkとする。sn -k outfileキーペアファイルをキー コンテナという特殊なファイルに格納する場合は、続けて以下のコマンドを実行する。infileには作成したキーペアファイルの名前、ContainerNemaにはキー コンテナの名前を記述する。  (キー コンテナに拡張子はない。)sn -i infile ContainerName

キーペアを作成したら、アセンブリに署名を行う。署名を行うには、アセンブリ リンカ (Al.exe) を利用するか、AssemblyKeyFileAttribute (キーペアをキーコンテナにインストールした場合はAssemblyKeyNameAttribute) を使用する。

アセンブリリンカを使う場合は、アセンブリファイルではなくモジュールファイル(拡張子は.netmodule)が必要となるため、C#コンパイラを使って自分でコンパイルを行うことになる。この辺は、@ITさんの以下の記事が参考になる。
@IT:インサイド .NET Framework [改訂版]第1回 マネージ・コード/アセンブリ/モジュール

属性を使う場合は、以下のコードを (通常、AssemblyInfo.csに) 記述するだけでよい。[assembly: AssemblyKeyFile("FileName")]キーコンテナにインストールした場合は、以下のコードとなる。[assembly: AssemblyKeyName("ContainerName")]
ただし、NET Framework 2.0 では、AssemblyKeyFileAttributeやAssemblyKeyNameAttributeによるアセンブリの署名は、セキュリティ上の問題等を理由に非推奨となっている。


[ Visual Studio 2005 での署名 ]
Visual Studio 2005では、キーペアの作成からアセンブリの署名までを、簡単に行うことができるようになった。手順は以下のようになる。


  1. ソリューション エクスプローラでプロジェクトの右クリックメニューの [ プロパティ ] をクリックし、プロジェクト デザイナを開く。

  2. [ 署名 ] タブをクリック。

  3. [ アセンブリの署名 ] チェックボックスにチェックを付ける。

  4. コンボボックスの [ <新規作成...> ] を選択する。

  5. [ キー ファイル ] テキストボックスに、作成するキーペアファイルの名前 (拡張子は不要) を入力する。

  6. パスワードで保護する場合は、[ キーファイルをパスワードで保護する ] にチェックを付け、8桁以上のパスワードを入力する。保護しない場合は、チェックを外す。

  7. [ OK ] ボタンをクリックする。


パスワードで保護する場合は、キーペアではなく電子証明書 (内部にキーペアが含まれている。拡張子はpfx) が作成される。パスワードで保護しない場合は、キーペアが作成される。

電子証明書のパスワードは比較的容易に解読可能であるため、パスワードの有無は実質的に、キーペアファイルと電子証明書のどちらを作成するかの違いでしかないと言える。
また、電子証明書は、発行者と発行先が同一な、いわゆるオレオレ証明書なので、電子証明書を作成する必要はあまりないと思われる。 (電子証明書を使う必要がないのではなく、オレオレ証明書を使う必要がないという意味なので注意。)

なお、既存のキーペアや既存の電子証明書を利用する場合は、手順4,5,6の代わりに、コンボボックスの [ <参照...> ] を選択し、参照ダイアログにてファイルを選択する。



[ 参考 ]
アセンブリおよびマニフェストへの署名の管理
@IT:インサイド .NET Framework [改訂版]第2回 アセンブリのアイデンティティ

[ 関連 ]
ClickOnce マニフェストの署名
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。