以前組み立てたは良いものの、大学やバイト先に持っていくので常用しているラップトップ(VAIO Type SZ)と使い分けが出来ず、メインマシンになりそこねていたShuttleのベアボーンを使って、流行りの仮想化というものに挑戦してみました。

仮想化というのは、一台のマシンの上で、実機をエミュレートした仮想のマシンを走らせることで、複数のOSを並行して走らせたりすることをいいます。一台のサーバーが複数のマシンの役割を果たしてくれる仮想化は、遊んでいるハードウェアは殆ど持っていない自分にとって夢のような技術です。幸い、組み立てていたPCは、こういうこともあろうかと余裕を持たせて4コアのCPUを積んでいたりしたので、挑戦することが出来ました。

これまでもWindows 7 RCとUbuntuをデュアルブートで入れてみたり、それらのOSの上で動くVirtual Boxという仮想化用のソフトを使いDebianやら色々走らせること(ホストOS方式)はしていたのですが、今回は仮想化でも、ハイパーバイザ方式での仮想化を試みてみました。ハイパーバイザ方式とは、ハイパーバイザという仮想化専用のOSを実機にインストールし、そのハイパーバイザが提供する仮想のマシンの上でOSを動かす方式です。ハイパーバイザ方式だと何がいいかというと、仮想化専用のOSがホストOSなため、ホストOSに無駄なリソースを喰われたり、ホストOSのアップデートなどでVMを落とす必要に迫られることが少ない点が優れています。今回は、VMware ESXiというハイパーバイザを入れ、その上にWindows 7 RCとUbuntu、CentOS、Free NASなど諸々を入れてみることを目指しました。VMware ESXiは以前、大学のサーバーで触らせてもらったことがあったのですが、そのときはCPUのコア:たくさん、メモリ:いっぱい、という、まぁ化け物みたいな構成のもので、後はVMを作るだけという状態で弄らせてもらったので簡単だったのですが、今回はサーバー機でない単なるベアボーンで一から構築したので少し難儀しました。

まず一点目がインストール時にHDDを認識しないという点。IDEのHDDを認識しないというのは聞いていたのですが、SATAで繋いでいた自分のマシンでも駄目でした。色々試したりしているうちに、ググって出てきたのがVMware ESXi を IDE HDD にインストール – trial and errorというページ。曰く、SATAでもAHCI モードで動いてない場合、IDE HDDとして認識され、それがもとで弾かれているとかなんとか。そこでBIOSの設定を見直し、AHCIモードに切り替えてみたのですが、こんどはSATA接続のDVDドライブが認識されないという問題が発生。どうもDVDドライブのファームウェアに問題があるらしく、泣く泣く設定を戻し、VMware ESXi を IDE HDD にインストール – trial and errorで紹介されている、インストールスクリプトでIDE接続のHDDをはじいている部分を書き換えるという方法で乗り切りました。DVDドライブをあきらめても良かったのですが、そうするとESXiをアップデートする必要に迫られた時に面倒がおきそうだったので、AHCIモード動作化は今回は見送りました。

続いて直面したのがオンボードのNICが認識されないという問題。なんでもESXiはIntelかBroadcomのチップを載せた一部のLANボードにしか対応していないとか。。こればかりは仕方がないので、EXPI9301CTというLANボードをAmazonでポチリ。\4,680と、目玉が飛び出るような高さではないけれど、今じゃ\4000もあればバルクのDVD±RWドライブが買えてしまうことを考えると、ちょっと高いかなと感じたり。そういうものなのでしょうか。

image ここまで来ると後は簡単で、ハイパーバイザに固定IPを割り振り、そのIPに対して、ラップトップにインストールしたVMware Infrastructure Clientで接続してVMを作っていくだけの簡単なお仕事になります。

なにはともあれまず入れたのはWindows 7。もうRTMもMSDNやTech Netで出てますし、Tech Net Direct Plusに申し込んで、RTMを手に入れても良いかと思ったのですが、折角Tech Net Direct Plusに申し込むならOffice 2010も欲しいよね、ということで、ぐっと我慢してOffice 2010のリリース時期がもう少し信じられるレベルまで固まるまで待つことにし、とりあえずはWindows 7 RCをインストール。その上でそのWindows 7に対し、リモートデスクトップ接続できるように色々と設定を行いました。VMware Infrastructure Clientのコンソールも、よいのですが、Aeroが効かないし、効率はやはりリモートデスクトップ接続に劣るからです。LAN内での接続はすぐ上手くいったのですが、外からインターネット経由で繋ぐのは少し難儀しました。自分の家は当然固定IPなぞ持っていないので、ダイナミックDNSサービスを利用し、ドメイン名でアクセスし、ブロードバンドルータからWin7のVMに対してリモートデスクトップ接続に使うポートをポートマッピングしてやったのですが、なぜか繋がらないという。。ラップトップからLANケーブルを引っこ抜き、代わりにイーモバイルの回線経由で接続のテストをしているつもりだったのですが、実は無線LANで家のネットワークにぶら下がりっぱなしだったのが原因でした。VMware Infrastructure Clientの接続が切断されない時点で気付くべきだったのですが、なかなか。。。時間を無駄にしてしましました。

つづいてセットアップしたるはFreeNAS。古いPCを簡単お手軽にNAS化できるアプライアンスOSです。数日前に7年程前のPCを引っ張り出してきて試してみたものの、そのPCのファンの五月蠅さに常時立ち上げておく気になれず、どうしたものかと思っていたものを移行させるために作成しました。本当はRAID化やバックアップの設定もしたいところなのですが、何せ物理HDDがベアボーンに内蔵させている一台しかないので、今のところはあきらめています。いずれeSATAでいくつもHDDを足したいところです。

更に続くはUbuntu.今のところ一応入れてみただけです。多分ずっと寝かせっぱなしになりそうだなぁ。。

そしてCentOS。とりあえずこいつを安定的に運用するのが目的。ぶっ壊すの覚悟のテスト用と、安定的に運用する奴と二つ。仮想化環境でなければ自分には到底実現できない、ぶるじょあな構成(笑)。壊したとしても、スナップショットがあるので、直ぐ戻せるという仮想化の利点を生かして、これでLinuxの勉強をしてみたいと思います。

AUG
9

明石海峡大橋を渡ってきました。

Published:2009-08-09 00:14:23 UTC

P10103317日から墓参りなどで大阪の母方・父方の実家を回っていまして、昨日は一日時間があったので、レンタカーを借りて明石海峡大橋を渡り、鳴門まで行ってきました。いやー明石海峡大橋、デカい!流石世界一の吊り橋。垂水側のアンカレイジ脇で写真を撮ったのですが、淡路島側はロクに写りませんでした。。

鳴門側では大鳴門橋の下部から、有名な鳴門の渦潮を見てきました。途中、渋滞に引っかかったりしたので、満潮時刻には間に合わず、綺麗な写真は撮れなかったのですが、波が渦を巻いている様は、非常に迫力のあるものでした。

そして鳴門では「釜揚げうどん」を食べてきました。本当は本場、讃岐まで行きたかったのですが、時間がなかったので。。とても美味しかったです。

P1010363P1010386

AUG
3

中国でTwitterを利用する方法

Published:2009-08-03 23:30:25 UTC

image ご存知の通り、中国では政府にとって好ましくないとされるサイトへのアクセスはブロックされており、Twitterもその一つです。TwitterのWebページのアクセスも、TwitterクライアントがTwitter APIを叩きに行くのもブロックされています(自分は普段TweetDeckを使っています)。しかし、それで「はいそうですか」、と素直に引き下がれるほど自分のTwitter依存度は低くなく。。一緒に上海に行った人達は日本にWebプロキシを建てた上で使っていたようですが、自分はちょっと違った方法を使いました。sobeesを使うという方法です。これはSilverlightで作られたTweetDeckライクなマルチカラムTwitterクライアントで、これはSilverlight3で構築されていて、Out of Browser機能が有効化されているので、これをデスクトップにインストールして使用していました。

ではなぜTweetDeckが使えずにsobeesが使えるかというと、sobeesはSilverlightで構築されているので、直接クライアントがTwitterのAPIを叩きに行けず、クライアントの配布されているsobeesのサーバーがsobeesのクライアントからのAPI呼び出し要求をTwitter APIに中継しているという性質によるものです。実は、中国国内からのアクセスが制限されいるのはTwitterのWebページとTwitter APIを直接呼び出すケースだけで、もばついやsobeesのように、Twitter APIを叩くサーバーが中国国外にあるWebクライアントは利用可能なのです。sobeesは現在リリースされているSilverlightアプリケーションの中では最もよく作り込まれているアプリケーションの一つで、TweetDeckと互角か、それ以上に使いやすいアプリケーションと言えるので、中国に行かれる場合はおススメのクライアントと言えるでしょう。ただ、ひとつだけ残念なことがあり、それは、IMEによる変換を行うときに誤動作をするという点。変換を行う時のEnterキー入力で入力が完了したものと勘違いし、そこでTweet送信をしてしまうというバグがあります。そのため、現状では、Tweetはテキストエディタで書いた上で張り付けて送信をせざるおえないという残念な状態です。バグレポートを投げようと思うので、それが対応されたらTweetDeck代替として結構使えるのではないかと思います。

AUG
3

上海から帰還

Published:2009-08-03 23:24:10 UTC

何とか上海から帰ってきました。今回は自分の研究室が提携している研究室のネットワークでの発表会で、自分もちょっと刺激を受けてこい、ということで行かせて貰ったのですが、いやはや向こうの学生のレベルは高かったです。研究内容のレベルはともかくとして(向こうは院生ばかりですから)、英語力の面からみて。中国からはFudan Univ.が、韓国からはKAISTが参加していたのですが、どちらも発表後の質疑応答をちゃんとこなしていて、しどろもどろになってしまった自分とは大違いでした。反省します。。

そういえば、滞在していたホテルが予想に反してあまりに綺麗だったので写真を撮りまくってPhotosynthでの合成表示を試してみました。Silverlightをインストールした上で観てみてください。

JUL
30

ちょっと上海行ってきます

Published:2009-07-30 09:00:02 UTC

提携している研究室同士の研究発表会ということで、2日までの日程で上海行ってきます。まぁ要は、ちょっと海外行って自分の英語の出来なさに打ちのめされて来い、というイベント。ありがたい話です。

JUL
29

Mac始めました。

Published:2009-07-29 01:48:20 UTC
Mac

IMAGE_291 唐突ですが、Mac使いになりました。まさか自分がMacを使う日が来るとは思っていなかったので、自分でも驚いています(笑) SAをやらせて貰ったりと色々とお世話になっている大学の先生からMacBook KUROを貸していただけることになり、今、手元にあのMacが。研究室で同期や後輩が次々にMacに乗り換えているのを見て、「WPFの使えないデスクトップOSなんて使う奴の気が知れないわ~」とかのたまっていたのですが、いちいち画面が綺麗で統一感があり、Macも目的によれば良いかもしれないなーと思ったりしています。これまでずっとWindowsで、全くMacをつかったことがないので、まず操作からしてよくわからないことが多いのですが(GNOMEの方が百倍とっつきやすいです)、躓きながら慣れていこうかと思います。ただ、残念なことに、八月の頭にかけては全く触る暇がなさそうなので、ちょっとの間、放置になりそうではあります。しかしMacかー。どう使ったものか。流石にMacまで来てMonoでC#コードをゴリゴリ書くというのは考えていないのですけど(まぁMonoDevelopは入れるでしょうが)、今更Objective Cとやらを覚えるのもなー、とも思います。なるべく潰しのきかない言語は勉強したくないという思いがあるので、pureなCや、Pythonを書くのに使わせて貰おうかなと思います。

IMAGE_292 しかし貸して頂いているマシン、先生が新しいマシン(勿論Mac)を買ったので使わなくなったから、ということで貸して頂いているのですが、自分のラップトップ(VAIO)よりパワーがあるという。。自分のはVAIO Type SZ53Bをメモリ4GBに増設して使っているのですが、CPUはIntel Core 2 Duo 1.67GHzで、借りているMacBookはIntel Core 2 Duo 2GHz。。なんか、ブートキャンプでWindows 7 RC入れてWindows機としてVAIOをリプレースしたくなるのですが(笑)うーむ。

FILCO FKB108M/NB

IMAGE_294 そしてFILCOのキーボードもお借りしました。いやーこれまでキーボードにはそれほど拘らずにきたのですが、良いキーボードってあるんですね。なかなか打ち心地が滑らかで、変な突っかかりや重さがないので、軽快にタイピングをすることができます。この打ち心地を覚えてしまうと、もう安キーボードには戻れない気がするので、もう少しこのキーボードを試したら、秋葉原でRealforceとかと打ち較べてみて新しいキーボードを自分で買ってみようかと思います。

 

なんか最近、部屋がますますマニアックな雰囲気になってきてヤバいです。。どげんかせんといかん(ぇ

IMAGE_287

CardSafe/EはMVVMパターンに則って作るように心掛けたのですが、WPFと同じ感覚でSilverlightにMVVMパターンを適用しようとすると、色々と不都合が出てきて苦労させられました。Silverlightではオミットされている機能が結構あるので、そこに引っかかるとしばしば手戻りが発生します。このエントリではCardSafe/Eを書いていて気付いた点をとりあえず二点ほど。

SilverlightにはDataTemplate.Datatypeが存在しない

地味に痛いですこの仕様。ItemsControlから派生したコントロール(ListBoxなど)のItemをItemsSourceにセットしたINotifyPoropertyChangedを実装したコレクションから生成する際に、コレクションが列挙するインスタンスの型によってDataTemplateを切り替える場合、WPFではDataTemplate.Datatypeがあるので重宝するのですが、Silverlightでは3 RTWでもサポートされていません。DataTemplateSelectorもサポートされていないので、インスタンスの型や状態によってDataTemplateを切り替えることはできないようです。とりあえず逃げ道として自分はDataContextにセットされたインスタンスをみてContentを書き換えるロジックを書いたUserControlを用意しているのですが、面倒ですね。XAMLで宣言的に書きたいのでDataTemplate.Datatypeは是非サポートしてもらいたいものです。

TabcotrolのItemsSourceはTabItemのコレクションしか受け付けない

なんでー!?これまたあんまりな仕様。WPFではItemsControlから派生したコントロールのItemsSourceにINotifyPoropertyChangedを実装したコレクションをセットすると、その列挙する内容をListBoxItemやTabItemなど適当なコンテナに包んで表示してくれるので、あとはDataTemplateで表示内容を好きなようにカスタマイズしてやればよかったのですが、SilverlightのTabControlのItemsSourceはTabItemのコレクションしか受けてつけてくれないという。。これではTabItemに対応するViewModelのインスタンスをセットし、DataTemplateでViewを設定して表示内容をカスタマイズするというM-V-VMでよく知られたパターンが使えません。SilverlightでもListBoxではListBoxItem以外も受け付けてくれたのですけどねぇ。。ではどうするか。自分の場合はViewModelのコレクションをラップしてTabItemのコレクションとして公開するラッパークラスとConverterを用意することで凌ぎました。

ラッパークラスはこんな感じです。ViewModelのコレクションとViewModelからTabItemへの変換を行うFuncをコンストラクタの引数として取ります。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Controls;

namespace SharpLab.CredentialsLockerSilverlight.ViewModels {
	public class ReadOnlyObservableTabItemCollection<TViewModel, TViewModelCollection> : ICollection<TabItem>, INotifyCollectionChanged where TViewModelCollection : ICollection<TViewModel>, INotifyCollectionChanged {

		private TViewModelCollection _viewModels;
		private ObservableCollection<TabItem> _tabItems;
		private Func<TViewModel, TabItem> _converter;

		public ReadOnlyObservableTabItemCollection(TViewModelCollection viewModels, Func<TViewModel, TabItem> converter) {
			_converter = converter;
			_viewModels = viewModels;
			_tabItems = new ObservableCollection<TabItem>();

			foreach (var item in _viewModels) {
				_tabItems.Add(_converter(item));
			}

			_viewModels.CollectionChanged += new NotifyCollectionChangedEventHandler(_viewModels_CollectionChanged);

			_tabItems.CollectionChanged += new NotifyCollectionChangedEventHandler(_tabItems_CollectionChanged);
		}

		void _viewModels_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			switch (e.Action) {
				case NotifyCollectionChangedAction.Add:
					_tabItems.Insert(e.NewStartingIndex, _converter((TViewModel)e.NewItems[0]));
					break;
				case NotifyCollectionChangedAction.Remove:
					_tabItems.RemoveAt(e.OldStartingIndex);
					break;
				case NotifyCollectionChangedAction.Replace:
					_tabItems.RemoveAt(e.OldStartingIndex);
					_tabItems.Insert(e.NewStartingIndex, _converter((TViewModel)e.NewItems[0]));
					break;
				case NotifyCollectionChangedAction.Reset:
					_tabItems.Clear();
					foreach (var item in _viewModels) {
						_tabItems.Add(_converter(item));
					}
					break;
			}
		}

		void _tabItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			if (CollectionChanged != null) {
				CollectionChanged(this, e);
			}
		}

		public event NotifyCollectionChangedEventHandler CollectionChanged;

		#region ICollection<TabItem> メンバ

		public void Add(TabItem item) {
			throw new NotSupportedException();
		}

		public void Clear() {
			throw new NotSupportedException();
		}

		public bool Contains(TabItem item) {
			throw new NotImplementedException();
		}

		public void CopyTo(TabItem[] array, int arrayIndex) {
			throw new NotSupportedException();
		}

		public int Count {
			get {
				throw new NotImplementedException();
			}
		}

		public bool IsReadOnly {
			get {
				return true;
			}
		}

		public bool Remove(TabItem item) {
			throw new NotSupportedException();
		}

		#endregion

		#region IEnumerable<TabItem> メンバ

		public IEnumerator<TabItem> GetEnumerator() {
			foreach (var item in _tabItems) {
				yield return item;
			}
		}

		#endregion

		#region IEnumerable メンバ

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
			return GetEnumerator();
		}

		#endregion
	}
}

これを以下のようなConverterから利用します。

using System;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.Windows.Data;
using SharpLab.CredentialsLockerSilverlight.ViewModels;

namespace SharpLab.CredentialsLockerSilverlight.Views.Converter {
	public class CardVMCol2TabItemColConverter : IValueConverter{
		#region IValueConverter メンバ

		public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
			return new ReadOnlyObservableTabItemCollection<CardViewModel, ObservableCollection<CardViewModel>>((ObservableCollection<CardViewModel>)value,
				(each) => {
					var newTabItem = new TabItem() {
						DataContext = each,
						Header = new CardTabHeaderView(),
						Content = new SharpLab.CredentialsLockerSilverlight.Views.CardView()
					};
					return newTabItem;

				});
		}

		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
			throw new NotImplementedException();
		}

		#endregion
	}
}

こうしてやることでTabControlのItemsSourceにViewModelのコレクションをバインドできるようになります。Converterは無理を通すのに便利な機能ですね。。

 ItemsSource="{Binding Path=CardViewModels, Converter={StaticResource CardVMCol2TabItemColConverter}}"
JUL
13

Silverlight 3のRTWがリリースされましたね。Silverlight Tools for Visual Studio 2008やExpression BlendのRCもリリースされています。version3から本気出すというMSの本領発揮とばかりに、結構便利になってきました。このエントリでは、自分が弄ってみて気づいたSilverlight 3 Betaからの変更点を書いてみたいと思います。

Silverlight 3 SDK Betaに含まれていたコントロールの一部がSilverlight Toolkitに移動

DataForm, DockPanel, WrapPanel, Expander, HeaderedContentControl, ViewboxがToolkitに移動したようです。それに伴って所属する名前空間が変わっているので注意が必要です。

Interactivity.dllの名前空間の変更

Expressionでは、3 MixPreviewからBehaviorというパターンがサポートされました。3 MixPreviewではプロジェクトでMicrosoft.Expression.Interactivity.dllというアセンブリを参照した上で、Behavior<T>クラスなどを継承して自分でBehaviorを作成するか、Microsoft Expression Community Galleryなどからダウンロードして使うという流れでした。今回リリースされたExpression 3RCでは、Microsoft.Expression.Interactivity.dllがSystem.Windows.Interactivity.dllと改名され、名前空間も変更されているので注意が必要です。この改名はBlend SDKの絡みでしょうか。また、BehaviorやTriggerActionの組み込み実装として、StoryBoardActionなどがMicrosoft.Expression.Interactions.dll内で提供されていますので、Microsoft Expression Community Galleryと共に活用していくよいでしょう。

DataGridのItemsSourceへのBindingが上手くいかない問題が修正

Silverlight 3 Betaの頃はDataGridのItemsSourceへのBindingが上手く動かないという問題がありました(DataGrid — binding to ItemsSource is broken : The Official Microsoft Silverlight Site)。今は修正されています。(これはむしろSilverlight Toolkitの話?)

Silverlight Tools for VS2008のSilverlightデザイナ削除

Silverlight 3 Tools for VS2008ではSilverlightデザイナが削除されています。まぁプレビューが上手く出来ない場合も多かったので、VS2010に期待ですね。今のうちにBlend 3に習熟しておきたいものです。

JUL
11

CardSafe/E公開!

Published:2009-07-11 22:49:49 UTC

CardSafeEverywhere 

ここ一か月ほど、週末やらに暇を見つけては作っていたアプリケーションが完成しました。CardSafe/Eというタイトルで、いわゆるパスワードマネージャなのですが、特長としてはMesh enabled webアプリケーションとして実装していることが挙げられます。Mesh enabled webアプリケーションとは、Microsoftが開発中のLive Frameworkを利用したアプリケーションの一形態です。Mesh enabled webアプリケーションは、Live Meshという、MicrosoftによるDropBox的なデータ同期サービス上に配置され、複数デバイス・サーバー間でのアプリケーションとそのデータの同期が自動的に行われるという特長があり、開発に用いる技術がAjaxやSilverlightであることから、デスクトップアプリケーションとして動かすだけでなく、Webブラウザ上の通常のWebアプリケーションとしても動かすことが出来ます。CardSafe/Eはこれを利用し、データ同期機能を持ったパスワードマネージャを実現しています。

利用方法

残念なことに、このLive Frameworkという技術はまだCTP版という扱いであり、通常のLive Mesh Beta版ではMesh enabled webアプリケーションであるCardSafe/Eは利用できません。CardSafe/Eを利用するには、https://developer.mesh-ctp.com/という開発者用のLive Meshを利用する必要があります。開発者用Live Meshは、テスト用ということもあり、Mesh enabled webアプリが使えるほかは、Live Mesh Beta版に比べて機能がかなり省かれており、CardSafe/E共々常用には向かないかと思います。それでもLive Frameworkを触ってみたいという場合は、

  1. Microsoft ConnectのAzure Services Invitation ProgramからLive ServicesのInvitation Codeを取得
  2. そのInvitation Codeを利用してAzure Services Developer Portalに登録
  3. 開発者用Live Meshにサインイン
  4. 開発者用Live MeshのDeviceタブからLive Framework Clientをインストール
  5. MeshにCardSafe/Eをインストール←このURLを踏んでMeshにCardSafe/Eをインストール

という手順になります。開発者用Live Meshへの登録手順などは、使ってみよう! Live Framework:第3回 はじめようLive Framework CTP|gihyo.jp … 技術評論社という記事が非常に分かりやすいので是非参照して進めてください。

new CloudApp()

ところで。このアプリケーションはnew CloudApp()というMicrosoftのコンテストに出しています。気に入って頂けた場合は、new CloudApp(): The Azure™ Services Platform Developer Challenge – Voteという投票ページでCardSafe/Eにご投票頂けると励みになります。CardSafe/Eに清き一票を!(ぇ

CardSafe/Eの公開ページ

CardSafe/E – SharpLab.

追補エントリを書きました。こちらもあわせてご参照ください:
UnhandledExceptionイベントは、サブスレッドの例外も捕捉可能 – SharpLab.

 

先日C# .NETアプリケーション開発 徹底攻略という本を購入しました。C#での業務アプリケーション開発におけるノウハウをまとめた本で、プロファイリングの方法など、普段、趣味グラマをしていると考えない部分について色々と触れているので大変参考になる本なのですが、一点、マルチスレッド処理における例外処理についての記述でおかしなところがありました。

2章6節4項の、サブスレッドで発生した例外の捕捉の仕方についての部分です。まず、93~95ページで、集約例外ハンドラではサブスレッドで発生した例外が捕捉出来ない、という部分があります。このようなコードです。

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Threading;

namespace ThreadExceptionBehavior {
	static class Program {
		/// <summary>
		/// アプリケーションのメイン エントリ ポイントです。
		/// </summary>
		[STAThread]
		static void Main() {

			Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
			Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(Program_UnhandledException);

			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run(new Form1());
		}

		static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
			Exception ex = e.ExceptionObject as Exception;
			MessageBox.Show(ex.Message);
		}

		static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
			MessageBox.Show(e.Exception.Message);
		}
	}
}

Form1.cs

using System;
using System.Windows.Forms;

namespace ThreadExceptionBehavior {
	public partial class Form1 : Form {
		public Form1() {
			InitializeComponent();
		}

		private delegate void SampleDelegate();

		private void button1_Click(object sender, EventArgs e) {
			//非同期スレッドの実行
			SampleDelegate a = new SampleDelegate(ThreadMethod);
			a.BeginInvoke(null, null);
		}

		private void ThreadMethod() {
			//意図的に起こした例外
			throw new Exception("非同期スレッドで起こった例外です。");
		}
	}
}

ここは良いのです。

そして続いての96~97ページで例外をメインスレッドへの通知して捕捉出来るように書き換える方法が書かれているのですが、このようになっています。

Form1.cs

using System;
using System.Windows.Forms;

namespace ThreadExceptionBehavior {
	public partial class Form1 : Form {
		public Form1() {
			InitializeComponent();
		}

		private delegate void SampleDelegate();

		private void button1_Click(object sender, EventArgs e) {
			//非同期スレッドの実行
			SampleDelegate a = new SampleDelegate(ThreadMethod);
			a.BeginInvoke(null, null);
		}

		private void ThreadMethod() {
			if (this.InvokeRequired) {
				//メインスレッドに呼び戻して実行
				this.BeginInvoke(new SampleDelegate(ThreadMethod));
				return;
			}
			throw new Exception("非同期スレッドで起こった例外です。");
		}
	}
}

19~23行目が加わった部分です。FormのInvokeRequiredプロパティがtrueの場合、つまりメインスレッド(UIスレッド)で実行されていない場合は、非同期にUIスレッドで実行しなおすようになっているのですが、これではサブスレッドでの例外処理の仕方の説明になっていません。ボタンをクリックした後直ぐに処理が帰ってくるという意味では非同期ではありますが、結局UIスレッド上で、ThreadMethodメソッドは動いているわけで、ThreadMethodメソッドに重たい処理を書いた場合は、UIがブロックされてしまうので無意味です。

本来ならば、以下のように書くべきではないでしょうか?

using System;
using System.Windows.Forms;

namespace ThreadExceptionBehavior {
	public partial class Form1 : Form {
		public Form1() {
			InitializeComponent();
		}

		private delegate void SampleDelegate();

		private void button1_Click(object sender, EventArgs e) {
			//非同期スレッドの実行
			SampleDelegate a = new SampleDelegate(ThreadMethod);
			a.BeginInvoke(null, null);
		}

		private void ThreadMethod() {
			try {

				//何らかの重たい処理

				throw new Exception("非同期スレッドで起こった例外です。");
			}
			catch(Exception e){
				this.BeginInvoke((MethodInvoker)delegate() {
					throw new ApplicationException("サブスレッドで例外が発生しました。", e);
				});
			}
		}
	}
}

これならば重たい処理によってUIがブロックされることもなく、またサブスレッドで発生した例外も例外集約ハンドラで集めることが可能です。