2014年05月21日

C#で自分自身のファイル名を知る方法

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgに感謝を込めて」シリーズ その9

■問題 (出題者:ところてん さん)
 
自分自身のファイル名を知る方法を示してください。 ビルド後のファイルが、hogehoge.exeであれば、 ”hogehoge.exe”が表示されるようなプログラムを書いてください。 スクリプト言語でも同様です。 ファイル名が変更されたらそれに追従するようにしてください。

いくつかの方法を書いてみました。 「自分自身のファイル名」という意味合いでは、最後の例が一番合ってるかな。


■C#で書いたコード
using System;
using System.Reflection;
using System.Diagnostics;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            string path = "";

            // 方法その1
            path = Environment.GetCommandLineArgs()[0];
            Console.WriteLine(path);

            // 方法その2
            path = Process.GetCurrentProcess().MainModule.FileName;
            Console.WriteLine(path);

            // 方法その3 (若干意味合いが異なる)
            path = Assembly.GetEntryAssembly().Location;
            Console.WriteLine(path);

            // 実行中のメソッドがあるアセンブリファイル名を表示する例
            path = Assembly.GetExecutingAssembly().Location;
            Console.WriteLine(path);
         }
   }
}
  

Posted by gushwell at 22:30Comments(0)TrackBack(0)

2011年12月17日

アセンブリを動的にロードし、そして完全にアンロードする

   このエントリーをはてなブックマークに追加 Clip to Evernote
このエントリは、C# Advent Calendar jp 2011 への参加記事です。



アセンブリを動的にロードし、利用し終わったらメモリからアンロードしたいという要求って、どれくらいあるのか
わかりませんが、今回は、そのやり方を説明します。

これって簡単なようでなかなか面倒です。残念ながら、Assembly クラスには、Load系のメソッドはありますが、
Unload系のメソッドがありません。

こんな時に AppDomainを使うと、要求を満たすことができます。

まずは、以下のようなクラスが、SampleLibraryアセンブリに定義されていたとします。


この SampleClassがあるSampleLibraryをロードし、SampleClass.Executeを呼び出し、
終わったら、アセンブリをアンロードする例を考えてみましょう。

簡単に説明すると、以下の手順を踏みます。

1. 新しくアプリケーションドメインを作成する。

2. そのアプリケーションドメイン内にプロキシ―クラスをロードする

3. プロキシ―クラス内で、目的のアセンブリ(SampleLibrary.dll)をロードする。

4. ロードしたアセンブリの機能 (SampleClass.Execute)をプロキシ―経由で呼び出し処理をする。

5. アセンブリがロードされたアプリケーションドメインをアンロードする。

では、順に見ていきましょう。

1. 新しくアプリケーションドメインを作成する。

ここでは、”SampleDomain" という名前のアプリケーションドメインを作成することとしましょう。
コードを示します。



2. そのアプリケーションドメイン内にプロキシ―クラスをロードする

次に、このアプリケーションドメインにプロキシ―クラスをロードします。
このProxyクラスは、実行されているアセンブリと同じアセンブリに存在するものとします。
Proxyクラスのコードは、後述します。

ここで重要なことは、AppDomain のCreate系のメソッドを利用し、直接、SampleClass のインスタンスを
作成してしまうと、ロードされるSampleLibrary アセンブリは、デフォルトアプリケーションドメインと
"SampleDomain" アプリケーションドメインの両方にロードされてしまうということです。

そのため、appDomain をアンロードしても、SampleLibrary をアンロードすることができません。

まずは、"プロキシークラス" をロードし、そのプロキシークラスの中で、型情報の取得対象である
目的のアセンブリをロードする必要があります。


このコードは、現在実行されているアセンブリ(Assembly.GetExecutingAssembly())に定義されて
いるProxyクラス(type.FullName)のインスタンスを生成するというコードになります。
これで、Proxyクラスのインスタンスが、SampleDomain アップドメイン内に作成されます。


3. プロキシ―クラス内で、目的のアセンブリ(SampleLibrary.dll)をロードする。

プロキシ―クラスは以下のように記述します。


この時、"プロキシークラス" は、MarshalByRefObject を継承する必要があります。

また、"プロキシークラス" とやり取りするパラメータは、

MarshalByRefObject で継承

[Serializable] 属性の付与

のどちらかを満たさなくてはなりません。今回のパラメータは、System.Stringなので、
[SerializableAttribute] が付与されています。


4. ロードしたアセンブリの機能 (SampleClass.Execute)をプロキシ―経由で呼び出し処理をする。

次に示すコードで、Proxyオブジェクト経由で、SampleClass.Execute メソッドを呼び出します。



5. アセンブリがロードされたアプリケーションドメインをアンロードする。

次のコードでアプリケーションドメインをアンロードします。


これで、アプリケーションドメインと一緒に、動的にロードしたアセンブリもアンロードされるこ
とになります。



それでは、Proxyクラスを経由して、ロード、呼び出し、アンロードする一連のコード例を示します。


例えば、上記コードを WindowsFormsのボタンクリックで実行するとします。
この場合、ボタンクリックの処理が終わると、SampleLibrary.dll をエクスプローラーから
削除することができます。

一方、AppDomain.CreateInstanceAndUnwrap で直接 SampleClassのインスタンスを生成する以下の
ようなコードでは、ボタンクリックの処理が終わっても、SampleLibrary.dll が使用中になり、
エクスプロ―ラーから削除することができません。


  注意:
   このコードを実行するには、SampleClassは、MarshalByRefObject を継承するか、
    [Serializable] 属性の付与する必要があります。

上記のソースコード上は、AppDomainの配下に、SampleLibrary.SampleClass をロードしている
ように見えますし、dynamicを使い、SampleClassはソースコード上には現れないようにしていいます。
しかし上記コードを実行するには、SampleClassの情報が必要なので、このコードが実行されるデフォ
ルトのアップドメインにも、SampleLibrary.SampleClass がロードされてしまいます。
そのため、生成した AppDomainをアンロードしても、SampleLibrary.SampleClassは残ってしまうという
ことです。

これを確かめるには、AppDomain.Unload(appDomain); を実行後に、


とDLLを削除するコードを追加します。すると、アクセスが拒否されて削除することはできないことが
確認できます。

一方、正しくアンロードできるコードでは、DLLファイルを削除することが可能です。




最後に、もう一つのやり方を示します。

それは、プロキシ―クラスを作成するのではなく、インターフェースを使う方法です。
この方法は、ロードしたいアセンブリのソース(今回の例では、SampleClass)に手を加えることが
できないと使えません。
つまり、他から購入したライブラリなどをアンロードしたいときには利用できないということです。

では、そのやり方を示します。
まず、新たなクラスライブラリ・プロジェクトを作成し、


というインターフェースを定義します。
次に、SampleClassを以下のように変更します。
この場合は、MarshalByRefObject から継承させる必要があります。


そして、これを呼び出す側は、


と ISampleインターフェース経由で Execute メソッドを呼び出すようにすれば、このアップドメイン
には、SampleLibraryは読み込まれないので、ロードしたアセンブリをアンロードすることができます。



実はこの記事の下書きはずいぶん前に出来上がっていたものです。なかなか完成までこぎ着けることが
できずに、そのまま放置していたのですが、C# Advent Calendar 2011 へ参加するために、公開できる
レベルになんとかしました。ですから、内容的に古い情報が含まれているかもしれません。
そういった箇所があればコメントしていただければと思います。

  
Posted by gushwell at 20:50Comments(0)TrackBack(0)

2010年07月11日

実行中のアセンブリのパスを得る

   このエントリーをはてなブックマークに追加 Clip to Evernote


で、現在実行中のコードを格納しているアセンブリのパスを得ることができます が、このcodeBaseは、

file:///C:/Sample/bin/Debug/CodeBaseSample.EXE

といった形式で、Uri形式の文字列です。
これを

C:\Sample\bin\Debug\CodeBaseSample.EXE

と通常のローカルパスの形式に変換するには、


と、Uri.LocalPathプロパティを使います。
  
Posted by gushwell at 23:58Comments(2)TrackBack(0)

2007年07月18日

アセンブリ名でロードする

   このエントリーをはてなブックマークに追加 Clip to Evernote
"TestLib.TestClass, TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

という typeネーム に対して、

Type type = Type.GetType(data.Type);
obj = Activator.CreateInstance(type);

としたら、エラーになる。
そうか、CreateInstance()は、勝手にアセンブリをロードしてくれないんだ。

ということで、
Assembly.LoadFrom(asm);
として、ロードしようとしたら、これまたエラーだ。
LoadFromには、ファイルパスを指定しないといけない。
アセンブリ名で、ロードするには、
Assembly.Load(asm);

としないといけないみたいだ。

ところで、記事先頭のタイプ名文字列から、アセンブリ名を取得するって、やはり自力でやらないといけないのかな。
まあ、それほど難しいコードじゃないけど、あっても良さそうなのに。  
Posted by gushwell at 20:24Comments(0)TrackBack(0)