ページ

2010年9月28日火曜日

[Silverlight] Silverlight は Web のためのものでは無くなった

Silverlight…not just for the Web… より。

おぉ、ほんとだ! http://msdn.microsoft.com/en-us/library/default.aspx を見ると

msdnsilverlight_en_us.jpg

このように Silverlight が 「.NET Development」 の下に移動してます。
今までは 「Web Development」 の下でした。
上記記事にあるとおり、out of browser もあるし、Windows Phone 7 もあるんだから Web の下よりは .NET の下の方がいいんじゃないか、っていうことですね。

けど、日本語版 http://msdn.microsoft.com/ja-jp/library/default.aspx の方を見ると

msdnsilverlight_ja_jp.jpg

まだ 「Web 開発」 の下ですね。
きっとそのうち英語版と同じように移動するんでしょう。

2010年9月22日水曜日

[Silverlight] Effect がパフォーマンスに与える影響

Silverlight Performance Tip: Understanding the impact of Effects on performance より。
なるほどぉ。
正確なことは上記記事を見てもらうとして要点のみ。

<Grid x:Name="LayoutRoot" Background="White">
    <Border Width="200" Height="100" Background="LightGray">
        <Border.Effect>
            <DropShadowEffect/>
        </Border.Effect>
        <Grid Width="200" Height="100">
            <TextBox Width="100" Height="30"></TextBox>
        </Grid>
    </Border>
</Grid>

こんな XAML があるとします。表示させると以下のような感じです。

DropShadowEffectPerformance.jpg

DropShadowEffect を使って影を落としていて、真ん中にテキストボックスがあるというごく単純なものです。
で、DropShadowEffect などのエフェクトがかかっていると、その内側の要素に再描画が必要になった場合に DropShadowEffect の部分ごと再描画されるそうです。
この例では中にテキストボックスがあるわけですが、テキストボックスの点滅するキャレットも再描画によって描かれてます。なので、キャレットが点滅するたびに回りの Border ごと再描画されることになります。もちろん、キャレットだけでなく、マウスホバーで色を変えたりするようなボタンとか、進捗にあわせて動くプログレスバーとか、再描画が発生するものはみんな同じことになります。
この例では Border の下にはテキストボックスがあるだけなので特に問題にはなりませんが、コントロールがたくさんあるような場合にはそれらがみんな再描画されることになるので CPU 負荷が高くなったりといった問題が発生するかもしれません。
で、どうすればいいかというと

<Grid x:Name="LayoutRoot" Background="White">
    <Border Width="200" Height="100" Background="LightGray">
        <Border.Effect>
            <DropShadowEffect/>
        </Border.Effect>
    </Border>
    <Border>
        <Grid Width="200" Height="100">
            <TextBox Width="100" Height="30"></TextBox>
        </Grid>
    </Border>
</Grid>

こんな風に Border を 2つに分けてしまえばいいそうです。
実際やってみると確かに見た目は同じになります。
そして、テキストボックスが含まれている方の Border には何のエフェクトも無いことになるので無駄な再描画は発生しないということですね。
なるほどなぁ。

なお、再描画が発生する範囲は Silverlight を貼り付けている HTML の方に

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
  <param name="enableRedrawRegions" value="true"/>
:
:

enableRedrawRegions を指定すれば可視化することができます。

2010年9月14日火曜日

[Silverlight][Twitter] Seesmic Desktop 2 って MEF で機能追加できるようになってるのか

Twitter クライアントの一つ、Seesmic Desktop 2 ですが、これって Silverlight 4 なんですね。Silverlight 4 ですが、ブラウザ上で動かすのではなくクライアント PC にインストールして実行する形態になっています。(out-of-browser です)

そして、
Writing plugins for Seesmic Desktop
(Microsoft の Silverlight チームのプログラムマネージャーの Tim Heuer 氏のブログ)
によると Seesmic Desktop 2 は Managed Extensibility Framework(MEF)によって拡張可能になっているそうです。Visual Studio 2010 用のテンプレート Seesmic Desktop Platform Developer Templates なんてのもありますし、Seesmic Desktop Platform Plugins には Tim 氏が作ったプラグインが紹介されています。
結構拡張性も高そうで、なかなかおもしろそうです。

# といいつつ、インストールしてちょっと使ってみたけど、イマイチ自分には合わず。
# 結局、TweetDeck に戻っちゃいました。
# 一時期は Seesmic Web を使ってたけど、最近 TL が更新されない病が発生してて使い物にならず。

2010年9月13日月曜日

[.NET] MTA では OpenFileDialog が動かない?

たまたま見かけてちょっと気になったので覚え書きとして。。。

Current thread must be set to single thread apartment (STA) mode before OLE calls can be made」 より。
Visual Studio セットアッププロジェクトでインストーラーを作るときにインストーラークラスのカスタムアクションで System.Windows.Forms.OpenFileDialog を使うとエラーが出ちゃうそうです。Win XP や 2003 ではちゃんと動くけど、Vista やそれ以降の Windows で発生するとのこと。
デバッガーにアタッチしておくと、"Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process." というエラーメッセージが出るそうです。原因はこのメッセージのまんまで、MSI が MTA で動いてるからで、OpenFileDialog なんかは STA じゃないと動かないかららしい。

へぇ、OpenFileDialog とかって MTA じゃ動かなかったんだ。知らんかった。

上記の記事では対策方法も紹介されています。
別のスレッドを作って、そいつを Thread.SetApartmentState(ApartmentState.STA) してから OpenFileDialog を ShowDialog() してやればいいようです。

2010年9月10日金曜日

[C#] yield return はスレッドセーフなのか? 追記

昨日の 「[C#] yield return はスレッドセーフなのか?」 に匿名さんからコメントを頂きました。
昨日の記事の最後のところにまとめとして 「lock { ~ } の中で yield return を使うのはやめておいた方がよさげ」 と書きましたが、ちょっと乱暴なまとめだと思ったので追記します。
(最初は昨日の記事に追記するつもりだったんですが、長くなったので別記事にしました)

えーと、もともと 「lock { ~ } の中で yield return を使うのはやめておいた方がよさげ」 と私が思ったのは 「ロックが解除されるタイミングが呼び出し元に依存するから」 というのが理由です。
お仕事で lock { ~ } の中で yield return を使うコードを書いていたときに、ふと、「これってちゃんとロックかかるの?」 と疑問に思ったのが発端で今回のことを調べたんです。このとき書いていたコードは 「なるべく早くロックを解除して他のスレッドが動けるようにしたい」 というものだったので、自然とロック解除のタイミングが呼び出し元に依存するのは避けたいと思ってました。
そういう前提があったので 「lock { ~ } の中で yield return を使うのはやめておいた方がよさげ」 と、まとめのところに書きました。けど、そういう前提であることをあまり明確にせずにこうまとめちゃうのはちょっとまずかったですね。

-- 9/10 13時追記 ここから
前の記事にもちょこっと追記しましたが、指摘を頂いたのでこちらにも追記しておきます。
すっかりロックが解除されるのは Enumerator の Dispose() が呼び出されたときだけかのように書いちゃってますが、実際には MoveNext() が false を返すときにも Dispose() と同じ処理が行われるようになっています。なので、Enumerator の Dispose() メソッドを呼び出したときか、MoveNext() が false を返して列挙が終わるときにロックが解除されることになります。
もちろん、以下に書いたファイルの例でも同じです。
-- ここまで追記

ということで、また他の例を。

コメントで頂いたように using { ~ } や try ~ finally でも lock { ~ } と同じことが起こります。
たとえば、以下のようなコード。

private IEnumerable<string> GetLines()
{
    using (var stream = new FileStream("test.txt", FileMode.Open, FileAccess.Read, FileShare.None))
    using (var reader = new StreamReader(stream))
    {
        var text = reader.ReadToEnd();
        var lines = text.Split('\n');
        foreach (var line in lines)
        {
            yield return line;
        }
    }
}

これはファイルをオープンするときに FileShare.None を指定してファイルをアクセス禁止にしています。
yield return が無ければ using のスコープを抜けるときに stream.Dispose() と reader.Dispose() が呼び出されてファイルがクローズされます。もちろん、このときにファイルのアクセス禁止も解除されます。
しかし、yield return によって出来上がるコードは lock { ~ } のときに見たのと同じようなものですから、stream.Dispose() と reader.Dispose() が呼び出されるのは Enumerator の Dispose() メソッドからになります。
呼び出し元が foreach を使っているときは、以下のような感じになるわけです。

foreach (var line in GetLines())
{

    ... line を使って何か処理 ...

}
// foreach を抜けたときに Enumerator の Dispose() が呼ばれて test.txt のアクセス禁止が解除される

このように GetLines() から返ってきた Enumerator での列挙が終わったあとに、foreach によって作られた Enumerator.Dispose() の呼び出しが行われて、ファイルクローズおよびアクセス禁止解除ということになります。

では、yield return を使わない場合はどうなるでしょうか?
以下のようなコードです。

private IEnumerable<string> GetLines()
{
    using (var stream = new FileStream("test.txt", FileMode.Open, FileAccess.Read, FileShare.None))
    using (var reader = new StreamReader(stream))
    {
        var text = reader.ReadToEnd();
        var lines = text.Split('\n');
        return lines;
    }
}

この場合は、GetLines() メソッドから return する時点で using のスコープを抜け、stream.Dispose() と reader.Dispose() が呼び出されてファイルがクローズされ、アクセス禁止が解除されます。
ですから、呼び出し元が上記と同じ foreach を使ったものであっても、foreach の列挙が始まるときにはすでにファイルがクローズされている状態になっています。

で、これはどちらが正しいとか言えるようなたぐいのものではありません。
前者は 「列挙が終わって Enumerator の Dispose() メソッドを呼び出すまでファイルがアクセス禁止になる」、後者は 「Enumerator を作成する間だけファイルがアクセス禁止になる」 ということになるわけですが、どちらにしたいかなんてことはケースバイケースです。
ただ、今回取り上げたような yield return の動作を知らないと 「yield return のところで using のスコープを抜けてファイルはクローズされるんじゃないの?」 と勘違いしやすいとは言えると思います。

というわけで、結局のところ、lock { ~ }、using { ~ }、try ~ finally などスコープに重要な意味があるものの中に yield return を書くときはちょっと注意が必要ではあると思うけど、yield return でいいかどうかなんてことはケースバイケース、という感じですね。

それにしても、yield return についてちゃんと考えたのなんて今回が初めてなんですが、なかなか興味深かったです。

2010年9月9日木曜日

[C#] yield return はスレッドセーフなのか?

スレッドセーフと言ってもいろいろ意味がありますが、ここでは以下のようなコードのことを指してます。

class YieldTest
{
    private List<Hoge> list = new List<Hoge>();

    public IEnumerable<Hoge> GetHoges1(string s)
    {
        lock (this.list)
        {
            var result = new List<Hoge>();
            foreach (var x in this.list)
            {
                if (x.Value == s)
                {
                    result.Add(x);
                }
            }
            return result;
        }
    }

    以下略

見てもらったまんまですが、何かの List があって、この List は別のスレッドからも操作されるからアクセス時には必ず lock すること、というような場合です。
で、上記の GetHoges1() メソッドは以下のように yield return を使って書いてもほとんど同じ意味です。

    public IEnumerable<Hoge> GetHoges2(string s)
    {
        lock (this.list)
        {
            foreach (var x in this.list)
            {
                if (x.Value == s)
                {
                    yield return x;
                }
            }
        }
    }

さて、この yield return を使った GetHoges2() メソッドはスレッドセーフなんでしょうか?

yield return っていうのは、魔法の力で動いているわけではなく、C# コンパイラが自動的に IEnumerator を実装したクラスを作成してくれて動いてるわけです。どんなコードが作成されるのかを言葉で説明するのは難しいですが、yield return や yield break のところでバラバラに分割してうまいこと MoveNext() に収め直すというような感じです。ですから、普通に考えると lock のスコープを抜けてしまいきちんと排他されないんじゃないか?と思えるわけです。
C# 言語仕様」 の 「8.14 yield ステートメント」 を見ると try ~ catch の中に yield は書けない、というようなことが書いてあります。しかし lock に関することは何も書いてありません。

というわけで、実際に生成されたコードを Reflector で見てみました。
すると以下のようになってました。

private class Enamerator : IEnumerator<Hoge>, IDisposable
{
    private List<Hoge> list = (this.list がセットされる)
    private IEnumerator<Hoge> enumertor;
    private bool lockToken = false;

    public Hoge Current { get; set; }

    public bool MoveNext()
    {
        if (最初の実行)
        {
            Monitor.Enter(this.list, ref this.lockToken);
            this.enumertor = this.list.GetEnumerator();
        }
        while (this.enumertor.MoveNext())
        {
            if (条件判定)
            {
                this.Current = this.enumertor.Current;
                return true;
            }
        }

        // 9/10 13時追記
        // コードは省略しますが、ここで下の Dispose() メソッドと同じ処理(Monitor.Exit の呼び出し)が行われます。

        return false;
    }

    public void Dispose()
    {
        if (this.lockToken)
        {
             Monitor.Exit(this.list);
        }
    }
}

上記のコードは意味的にはこんな感じになっているという例であって、実際の自動生成されたコードとはかなり違うんですが、まぁ、こんな感じです。
どうでしょう?ちゃんと、初めて MoveNext() メソッドが呼ばれたときに Monitor.Enter で list に対してロックをかけ、Dispose() メソッドで Monitor.Exit でロックを解除しています。

-- 9/10 13時追記 ここから
上にも書き足しましたが、MoveNext() が false を返すときにも Dispose() と同じ処理が行われるようになっています。なので、Enumerator の Dispose() メソッドを呼び出したときか、MoveNext() が false を返して列挙が終わるときにロックが解除されることになります。
-- ここまで追記

すげぇ!C# コンパイラは lock のことまで考慮して yield return の処理をしてるのか!

。。。と、思いましたが、実は違いますね。
まず、lock ステートメントは C# のシンタックスシュガーで、コンパイルすると Monitor を使ったコードが出来上がります。yield return と違ってそんなにややこしくなく、以下のような try ~ finally で Monitor.Enter と Monitor.Exit を囲んだコードに変換されるだけです。全体を try { ~ } で囲み、finally で Monitor.Exit() を呼び出すことによって途中で例外が発生しようが何しようがスコープを抜けるときには確実にロックが解除されるようになっているわけですね。

lock (this.list)
{
    ...
}

---- 上記をコンパイルすると下記のコードができあがる ----

bool lockToken = false;
try
{
    Monitor.Enter(this.list, ref lockToken);

    // ここに lock { ... } の ... 部分が入る
}
finally
{
    if (lockToken)
    {
        Monitor.Exit(this.list);
    }
}

また、「C# 言語仕様」 の 「8.14 yield ステートメント」 に try ~ catch の中に yield は書けないとありますが、try ~ finally の中には書けるとあります。
で、try ~ finally の中に yield が書いてある場合は、finally の部分が自動生成された Enumerator の Dispose() メソッドの中で実行されるわけですね。
だから、C# コンパイラはいつもどおりに lock ステートメントを解釈し、いつもどおりに yield return を解釈すると、自動的に正しく lock が働くコードが生成されるわけです。

というわけで、まとめ。

lock { ~ } の中で yield return を使ってもきちんとロックされる。

ただし、重要な注意点があります。
上記の自動生成されるコードを見れば明らかですが

lock { ~ } の中で yield return を使った場合は、返された Enumerator の Dispose() メソッドを呼ぶまでロックが解除されない。

なお、多くの場合、yield return で返された Enumerator は foreach で使われるんじゃないかと思います。
foreach ステートメントは Enumerator が IDisposable を実装している場合は自動的に Dispose() メソッドを呼び出すようになっています。(「C# 言語仕様」 の 「8.8.4 foreach ステートメント」。これは C# の最初のバージョンからそういう仕様だったと思います) だから foreach で使う分にはきちんと Dispose() が呼ばれますから何も問題ありません。
というか、Enumerator は多くの場合に IDisposable を実装していますから、それをチェックしてきちんと Dispose() メソッドを呼び出してやらないとまずいことになる可能性大です。foreach を使わずに Enumerator を受け取るときは using を使うなりしてちゃんと Dispose() を呼び出してやりましょう。

そして、ロックが解除されるタイミングが違うというのも大きな違いですね。
最初の例の GetHoges1() では lock のスコープを外れた時点でロックが解除されます。
それに対して GetHoges2() では Enumerator を受け取った側で Dispose() を呼び出した時点でロックが解除されます。ということは、ロックが解除されるタイミングが呼び出し元の処理に依存してしまうわけですね。
なるべく早くにロック解除してやって他のスレッドが動けるようにしてやりたいというのが普通でしょうから、呼び出し元に依存しちゃうのは避けたい場合が多いんじゃないかと思います。

というわけで、本当のまとめ。

lock { ~ } の中で yield return を使ってもきちんとロックされる。しかし、Enumerator の Dispose() が呼ばれるまでロックが解除されないし、いつ Dispose() が呼ばれるかも呼び出し元に依存することになる。
だから lock { ~ } の中で yield return を使うのはやめておいた方がよさげ。

9/10 追記を書きました。
[C#] yield return はスレッドセーフなのか? 追記

2010年9月8日水曜日

[VS] Visual Studio の色設定

へぇ、こんなサイトがあったんですね。
http://studiostyles.info/

Visual Studio 用の色設定がたくさん投稿されています。
背景が黒いやつの方が人気あるみたいですねぇ。私はデフォルトの白背景のまま使ってるから、黒背景は違和感あるなぁ。

ちなみに、使い方は Visual Studio の「ツール」メニューの「設定のインポートとエクスポート」でインポートするだけです。インポートする前に今の設定をエクスポートしておけばあとで元に戻せます。(VS2010 ではインポートするときに今の設定を保存しておくか聞いてくれます)

おまけ
これは有名だと思いますが、VS2010 では Visual Studio Color Theme Editor を入れてやるとメニュー、ツールバー、タブ、タイトルバー、などなどの色を変更することができます。(メニューに「Theme」が追加されます)

2010年9月2日木曜日

[Silverlight] XAP の最適化ツール (と、VS2010 アドインの作り方)

Xaps Minifier. A Step Forward. New Options and New Features」 より。
おぉ、こりゃなるほどだ。

Xaps Minifier という VS2010 のアドインが紹介されています。(私はまだ試してませんが)
どういうものかは part 1 の記事で解説されていますが、ここでも簡単に概要を書いときます。
part 1 の記事の最初がやろうとしてることの解説、残りの大部分は Visual Studio 2010 アドインの作り方の解説になってます)

Silverlight のアプリでも規模が大きくなってくると複数のプロジェクトにわけたりします。Visual Studio では XAP ファイルはプロジェクトごとにできるようになってますから、プロジェクトの数だけ XAP ファイルが出来上がります。
で、XAP ファイルの中にはそのプロジェクトが使っているアセンブリ (DLL) がすべて入っています。なので、他のプロジェクトを参照していたり、共通で使うアセンブリがあったりした場合は、同じアセンブリがあちこちの XAP ファイルの中に入っていることになります。
というか、複数プロジェクトの場合、プロジェクト参照しながらの方が普通だと思うので、ほとんどのアセンブリがダブって入っていると思ったほうがいいくらいじゃないかと思います。
(XAP ファイルは ZIP の拡張子を変えたものですから、拡張子を ZIP にして解凍してみれば簡単に確認できます)

しかし、実行時には同じアセンブリが重複して読み込まれることはありません。
なので、複数の XAP ファイルの中に同じアセンブリを入れておく必要はありません。最初に読み込まれる XAP ファイルにだけ入れておけば十分です。
けど、XAP ファイルが読み込まれる順番はプログラムの作り方次第なのでどれが最初かなんてわかりません。
ただ、スタートアップに指定されている XAP は必ず最初に読み込まれるわけですから、これに含まれているアセンブリは他の XAP に入れておく必要がないことは明らかです。

というわけで、この 「スタートアッププロジェクトに含まれているアセンブリを他の XAP に入らないようにする」 という設定をしてくれるのが Xaps Minifier です。
ほんと、そりゃそうだって感じです。
(もちろん、こういうことが出来るのは他に配布したりする予定がない XAP ファイルだけですが)

あと、複数のプロジェクトで使われているアセンブリをスタートアップの XAP に含めるようにしてもくれるようです。こうすればトータルサイズは当然小さくなりますね。
ただ、これは 「[Silverlight] Silverlight アプリケーションの起動時に関するベストプラクティス」 にある 「起動時間を早くするにはスタートアップの XAP ファイルを小さくしろ」 っていうのの真逆になっちゃいますが。
この辺はケースバイケースで考えるしかないでしょうね。

2010年9月1日水曜日

[IE9] コンテンツ領域が 2 ピクセルくらいサイズが変わるらしい

Making Sites Look Their Best in Standards Mode」 より。
今までまったく気づいてませんでしたが、IE8 まではコンテンツ領域の回りに 2 ピクセルのボーダーがあったんですね。言われてみると確かにあるw

で、どうやら IE9 ではこの 2 ピクセルが無くなるらしい。
ただし、無くなるのはスタンダードモードのときだけで、レガシーモードのときは今までと同じに 2 ピクセルのボーダーがあるとのこと。
スタンダードモードかレガシーモードかはどうやら doctype が HTML5 になってるかどうかで判断するみたい。

いやぁ、やっぱり IE くらいのアプリケーションになると、ここまで後方互換性を気にしてるんだなぁ、と素で感心。というか、こっそり変えちゃっても誰も気づかないんじゃない?ww

9/6 追記
ちゅきさんに頂いたコメントでタイトルが間違ってることに気づきました。
「2ピクセル小さくなる」ってタイトルでしたが、ボーダーが無くなるんだから小さくなるんじゃないですね。けど、右のスクロールバーとか下のステータスバーとかのところにボーダーがあるのかよくわからなかったので、とりあえず「2ピクセルくらいサイズが変わるらしい」というタイトルに変更しました。

[Silverlight][3DCG] SilverMotion (Silverlight 用の 3DCG 描画エンジン)

SilverMotion
Silverlight 用の 3DCG 描画エンジンだそうです。
PostVision という会社の商品みたいですね。(フリー版もあるようです)

Silverlight 自体には 3D 描画機能はありませんから全部自前で(おそらくは C# で)実装したんですね。
デモページ で動いているところが見れます。
私の環境だと、複数ライト + テクスチャマッピング + ノーマルマッピング + 環境マッピングで総ポリゴン数 9万くらいのカエルさんが 45fps くらいで描画できてました。
(オープンソースの Silverlight 用 3DCG 描画エンジンなんかもあったと思いますが、私はそれらのことを知らないので、それらに比べてどうなのかとかはわかりません)

ちなみに Blender, 3DS Max, Maya, MilkShape などからエクスポートした .3DS ファイルに対応しているそうです。


[Silverlight][3DCG] SilverMotion (Silverlight 用の 3DCG 描画エンジン)

SilverMotion
Silverlight 用の 3DCG 描画エンジンだそうです。
PostVision という会社の商品みたいですね。(フリー版もあるようです)

Silverlight 自体には 3D 描画機能はありませんから全部自前で(おそらくは C# で)実装したんですね。
デモページ で動いているところが見れます。
私の環境だと、複数ライト + テクスチャマッピング + ノーマルマッピング + 環境マッピングで総ポリゴン数 9万くらいのカエルさんが 45fps くらいで描画できてました。
(オープンソースの Silverlight 用 3DCG 描画エンジンなんかもあったと思いますが、私はそれらのことを知らないので、それらに比べてどうなのかとかはわかりません)

ちなみに Blender, 3DS Max, Maya, MilkShape などからエクスポートした .3DS ファイルに対応しているそうです。