System.FormatException

引数の書式が、呼び出されたメソッドのパラメーターの仕様に一致していない場合にスローされる例外。

[実験ソース(C#)]

using System;

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            DateTime dt = DateTime.Parse("12/32"); // 12月31日や1月1日にはならない。

        }

    }

}

[結果]


ハンドルされていない例外: System.FormatException: 文字列は有効な DateTime ではありませんでした。
場所 System.DateTimeParse.Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
場所 ConsoleApplication1.Program.Main(String[] args) 場所 Program.cs:行 8

[対処など]
"12/32" のところを、"12/31" とすると、12月31日の DateTime型オブジェクトが得られる。

System.DuplicateWaitObjectException

同期オブジェクトの配列に 1 つのオブジェクトが 2 回以上現れた場合にスローされる例外。

[実験ソース(C#)]


using System;

using System.Threading;



namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            AutoResetEvent e = new AutoResetEvent(false); // 初期は非シグナル状態

            WaitHandle[] handles = new WaitHandle[2];

            handles[0] = e;

            handles[1] = e; // 同じイベントハンドルをセットしてはダメ



            ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), handles[0]);

            ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), handles[1]);

            WaitHandle.WaitAll(handles);

        }



        static void DoWork(Object obj)

        {

            AutoResetEvent ev = (AutoResetEvent)obj;

            Console.WriteLine("working...");

            Thread.Sleep(1000); // 1sec

            ev.Set();

        }

    }

}

[結果]


working...
working...

ハンドルされていない例外: System.DuplicateWaitObjectException: 引数に重複するオブジェクトがあります。
場所 System.Threading.WaitHandle.WaitMultiple(WaitHandle[] waitHandles, Int32 millisecondsTimeout, Boolean exitContext, Boolean WaitAll)
場所 System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles, Int32 millisecondsTimeout, Boolean exitContext)
場所 ConsoleApplication1.Program.Main(String[] args) 場所 Program.cs:行 16

[対処など]
同じイベントオブジェクトで待ち合わせしないようにする。
MSDNヘルプの内容からは、WaitAll() のタイミングで例外が発生するかとおもっていたら、子スレッドが開始されてから発生するようだ。

System.EntryPointNotFoundException

開始メソッドが指定されていないことが原因でクラスの読み込みに失敗した場合にスローされる例外。

[実験ソース(C#)]


using System;

using System.Runtime.InteropServices;

namespace ConsoleApplication1

{

    class Program

    {

        [DllImport(@"..\..\..\ClassLibrary1\bin\Release\ClassLibrary1.dll")]

        extern static void PrintHello();



        static void Main(string[] args)

        {

            PrintHello();

        }

    }

}

[結果]


ハンドルされていない例外: System.EntryPointNotFoundException: DLL '..\..\..\ClassLibrary1\bin\Release\ClassLibrary1.dll' の 'PrintHello' というエントリ ポイントが見つかりません。
場所 ConsoleApplication1.Program.PrintHello()
場所 ConsoleApplication1.Program.Main(String[] args) 場所 Program.cs:行 12

[対処など]
BadImageFormatExceptionの例 にもあるように、Assembly.LoadFile で読み込むと良い。
あるいは、[参照の追加] で DLL を追加してやるとより簡単。

System.DllNotFoundException

DLL インポートで指定した DLL が見つからない場合にスローされる例外。

[実験ソース(C#)]


using System;

using System.Runtime.InteropServices;

namespace ConsoleApplication1

{

    class Program

    {

        [DllImport("win32.dll", CharSet = CharSet.Unicode)]

        public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);



        static void Main(string[] args)

        {

            MessageBox(IntPtr.Zero, "テスト""タイトル"0);

        }

    }

}

[結果]


ハンドルされていない例外: System.DllNotFoundException: DLL 'win32.dll' を読み込めません: 指定されたモジュールが見つかりません。 (HRESULT からの例外: 0x8007007E)
場所 ConsoleApplication1.Program.MessageBox(IntPtr hWnd, String text, String caption, UInt32 type)
場所 ConsoleApplication1.Program.Main(String[] args) 場所 Program.cs:行 12

[対処など]
正しい DLL 名を指定する。今回の場合、MessageBox は user32.dll なので、そのように書き直す。
他に、依存関係のある他のDLLがない場合にも発生する。

System.CannotUnloadAppDomainException

アプリケーション ドメインをアンロードしようとして失敗した場合にスローされる例外。
二重に Unload しようとした場合も発生するようだが、そちらは単純なので置いておく。
今回は、子プログラムが終了に「もたつく」ケースを実験。

[実験ソース(C#)]
まずは、親プログラム。


using System;

using System.IO;

using System.Threading;

using System.Threading.Tasks;



namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            AppDomain domain = AppDomain.CreateDomain("MyDomain");



            string path = Path.GetFullPath(@"../../../ChildApp/bin/Release/ChildApp.exe");

            Task[] tasks = new Task[2];

            tasks[0] = Task.Factory.StartNew(() => domain.ExecuteAssembly(path, new string[] { "3""aaa" }));

            tasks[1] = Task.Factory.StartNew(() => domain.ExecuteAssembly(path, new string[] { "5""bbb" }));



            Task.WaitAny(tasks);



            // どれか1つでもタスクが終了すれば、強制終了する。

            Console.WriteLine("unloaded");

            AppDomain.Unload(domain);

        }

    }

}

続いて、ドメインに含まれる子プログラム。


using System;

using System.Threading;



namespace ChildApp

{

    class Program

    {

        /// <summary>

        /// 1秒毎に表示するアプリ

        /// </summary>

        /// <param name="args">カウント, 文字</param>

        static void Main(string[] args)

        {

            int cnt = int.Parse(args[0]); // エラー処理は省略

            string msg = args[1];

            try

            {

                for (int i = 0; i < cnt; i++)

                {

                    Thread.Sleep(1000);

                    Console.WriteLine(msg);

                }

            }

            catch (Exception e)

            {

                Console.Write(string.Format("プロセス({0})の中断:", msg));

                Console.WriteLine(e.Message);

            }

            finally

            {

                // Domainがunloadされると、強制終了になるが、

                // すぐには停止できない状況を作り出す。

                Thread.Sleep(2000);

            }

        }

    }

}

[結果]


bbb
aaa
bbb
aaa
bbb
aaa
bbb
bbb
unloaded

ハンドルされていない例外: System.CannotUnloadAppDomainException: appdomain をアンロード中にエラーが発生しました。 (HRESULT からの例外: 0x80131015)
場所 System.AppDomain.Unload(AppDomain domain)
場所 ConsoleApplication1.Program.Main(String[] args) 場所 Program.cs:行 23

[対処など]
ドメインのアンロード処理は非常に重い処理のようで、予定では3秒後(3回繰り返した後)、unloaded と表示されるはずが、5秒かかっても終わらないため、2つ目の子プロセス(5回繰り返す方)も正常に完了してしまう。
子プロセスが簡単に終了すれば、どうか? ということで、finally ブロックで2秒待っているところを、1秒に書き換えて実行した結果が次。

[結果]


aaa
bbb
aaa
bbb
aaa
bbb
bbb
unloaded
プロセス(bbb)の中断:スレッドを中止しようとしました。

この場合、期待通りドメインを Unload することで子スレッドは、アボートする。
実際に使用する局面では、どのように待ち合わせすればよいか、非常に難しい問題だと感じた。

System.BadImageFormatException (その3 異なる .NET Frameworkのバージョン)

ダイナミック リンク ライブラリ (DLL) または実行可能プログラムのファイル イメージが無効である場合にスローされる例外。
今回は、.NET Framework 4.0 で作成したDLLを、.NET Framework 2.0 のプログラムでLoadしようとした時に発生する例外を検証。

[実験ソース(C#) DLL側]


using System;



namespace ClassLibrary1

{

    public class Class1

    {

        public static void PrintHello()

        {

            Console.WriteLine("Hello");

        }

    }

}

プロジェクトを右クリックし、対象のフレームワーク .NET Framework 4.0 であることを確認。

次に、このDLLを呼び出し実行するプログラム。

using System;

using System.Reflection;

using System.IO;



namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            string path = Path.GetFullPath(@"..\..\..\ClassLibrary1\bin\Release\ClassLibrary1.dll");

            Assembly asm = Assembly.LoadFile(path);

            MethodInfo PrintHello = asm.GetType("ClassLibrary1.Class1").GetMethod("PrintHello");

            PrintHello.Invoke(nullnull);

        }

    }

}

こちらは、ターゲットのフレームワーク .NET Framework 2.0 にする。

[結果]


ハンドルされていない例外: System.BadImageFormatException: このアセンブリは現在読み込まれているランタイムより新しいランタイムでビルドされているため、読み込むことができません。 (HRESULT からの例外: 0x8013101B)
場所 System.Reflection.Assembly.nLoadFile(String path, Evidence evidence)
場所 System.Reflection.Assembly.LoadFile(String path)
場所 ConsoleApplication1.Program.Main(String[] args) 場所 Program.cs:行 12

[対処など]
フレームワークのバージョンをあわせる。
ただし、今回の実験とは逆に、DLLのバージョンを 2.0 とし、コンソールアプリの方を 4.0 とした場合は、例外は発生しない。

System.BadImageFormatException (その2 x64 と x86)

ダイナミック リンク ライブラリ (DLL) または実行可能プログラムのファイル イメージが無効である場合にスローされる例外。
今回は、x64 用に作成したDLLを x86 プログラムで呼び出した場合の例外を実験。

[実験ソース(C#) DLL側]


using System;



namespace ClassLibrary1

{

    public class Class1

    {

        public static void PrintHello()

        {

            Console.WriteLine("Hello");

        }

    }

}

このプログラムを x64 でコンパイル
自分のPCには、VS C# 2010 Express しかインストールしていないため、直接プロジェクトファイルを編集。
(以下の PlatformTarget の部分)


  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

    <PlatformTarget>x64</PlatformTarget>

    <DebugSymbols>true</DebugSymbols>

    <DebugType>full</DebugType>

    <Optimize>false</Optimize>

    <OutputPath>bin\Debug\</OutputPath>

    <DefineConstants>DEBUG;TRACE</DefineConstants>

    <ErrorReport>prompt</ErrorReport>

    <WarningLevel>4</WarningLevel>

  </PropertyGroup>

つづけて、このDLLを呼び出すプログラムを作成。


using System;



namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            ClassLibrary1.Class1.PrintHello();

        }

    }

}

[結果]


ハンドルされていない例外: System.BadImageFormatException: ファイルまたはアセンブリ 'ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'、またはその依存関係の 1 つが読み込めませんでした。間違ったフォーマットのプログラムを読み込もうとしました。
場所 ConsoleApplication1.Program.Main(String[] args)

[対処など]
同じプラットフォームにすることで正常に動作する。
他にも、逆パターンで、x86 用に作成した DLL を x64 プログラムで使用する場合もおきる。(らしい)
この場合は、プラットフォームターゲット プロパティを x86 (x64 でも AnyCPU でもなく) に設定して再コンパイルすると良い。