2009年10月17日土曜日

dojo.Deferred

JavaScriptにはスレッドがありません。時間のかかる処理をするにはsetTimeoutを使った疑似スレッドを使うことになります。
すると今度は完了待ちのための手段が大変です。いくつかの処理が終わったら最後にこれをする、とかあるわけです。
そんなときのための対策にこんな関数を用意して使っていました。

// ちょー適当な関数名…
function doat(count, func) {
return function() {
if( --count == 0 )
func();
};
}
この関数は関数を返します。返ってきた関数はcount回呼び出すとfuncを呼びます。
これで、各疑似スレッドに返ってきた関数を呼び出させれば、最後に終わったスレッドがfuncを実行してくれるわけです。

でもdojoにdojo.Deferredクラスを見つけました。dojo的にはこっちを使うみたいです。
まだ使い慣れていませんが、そのうちこっちに乗り換えよう…。

2009年9月19日土曜日

ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly

データからBitmapの生成をよくするんですが、だいたいこんなコードを書きます。

byte[] pixels;

var bitmap = new Bitmap( width, height, PixelFormat.Format32bppArgb );
var data = bitmap.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb );
Marshal.Copy( pixels, 0, data.Scan0, pixels.Length );
bitmap.UnlockBits( data );
Marshal.Copy()が無駄に思えたのでImageLockMode.UserInputBufferを使ってみました。するとこんな風に書き換わります。
byte[] pixels;

var bitmap = new Bitmap( width, height, PixelFormat.Format32bppArgb );
bar handle = GCHandle.Alloc( pixcels, GCHandleType.Pinned );
try{
var data = new BitmapData{
PixelFormat = PixelFormat.Format32bppArgb,
Width = width,
Height = height,
Stride = stride,
Scan0 = Marshal.UnsafeAddrOfPinnedArrayElement( pixels, 0 ),
};
bitmap.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.UserInputBuffer|ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb, data );
bitmap.UnlockBits( data );
}
finally{
handle.Free();
}
無駄なコピーが排除できた! と喜んだものの、処理がかなり遅くなりました…。GCHandle.Alloc()のせいではなく、ImageLockMode.UserInputBufferが原因のようです。

2009年9月7日月曜日

IE6 CSSのclass selectorの酷さ

IE6の酷いところはいろいろ言われていますが、今更ながらこんなことではまるとは。と言うことで書き留めておきます。

<style type="text/css">
.a.c {
color: red;
}
.b.c {
color: blue;
}
</style>
何の変哲もないスタイルです。classにa cが同時に含まれていれば赤く、b cが同時に含まれていれば青くなります。
そうこんな感じ
<div class="a c">
color: red;
</div>
…ところが、IE6はclassにbが含まれていないのに青くなります。いやマジで。


追記:
間違ってました。IE6はCSS1しかサポートしておらず、CSS1ではclass selectorのネストを許していませんでした。

2009年8月13日木曜日

Windows 7 導入

Windows 7を導入しました。Hyper-Vが使いたかったのでWindows Server 2008 R2の方がよかったのですが、Hyper-Vを有効にするとスリープが使えないのは痛いです。サーバーOSだから当たり前ですね。

以降、愚痴です。
今更ながらIRQの競合が起きてる模様?
video card、onboard sound、onboard nicがIRQを共有しているらしく、Windows 7起動時にsoundやnicが認識されない現象が。必ずvide cardは認識される辺り怪しいです。
XP / Vistaでは発生しなかった現象です。またWindows Server 2008 R2でも発生します。

2009年6月7日日曜日

Visual Studio 2010 beta1 C++でのinclude

なんでもVC++が進化しているそうなので試してみたく。
プログラムを書こうとして#include <...>まで書いて気がついた。はてさて、header fileのdirectoryはどう設定するの?

Visual Studio 2008までは ツール - オプション - プロジェクトおよびソリューション - VC++ディレクトリ という設定項目があり、そこにディレクトリを入力できた。が、2010 beta1ではそこがなくなっている。
もちろんプロジェクトのプロパティに設定してもコンパイルできることにはできるが、システムにインストールしたheader fileのdirectoryをプロジェクトごとに書くのはおかしい。

プロジェクトファイルを参照したところ答えがあった。

<ImportGroup Label="PropertySheets">
<Import Project="$(LocalAppData)\Microsoft\VisualStudio\10.0\Microsoft.Cpp.$(Platform).user.props"
Condition="exists('$(LocalAppData)\Microsoft\VisualStudio\10.0\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
つまり$(LocalAppData)\Microsoft\VisualStudio\10.0\Microsoft.Cpp.$(Platform).user.propsに書けば取り込まれます。

2009年6月5日金曜日

C# 4.0 dynamic その2

というわけでサンプルコードを使って、C# 3.0とC# 4.0とでどのように変わってくるかを説明します。
サンプルは、Outlook 2007の受信トレイ以下のフォルダにある重複メール(Message-Idが一致するメール)をゴミ箱へ移動するコードです。Outlook 2007の新機能Tableオブジェクトを使用しているのでOutlook 2003以前では動作しません。

まずはC# 3.0でのコード

static class Program {
// Outlookを初期化し、受信トレイに対してVisit()を呼びます。
static void Main( string[] args ) {
var application = new Application();
Visit( application.Session.GetDefaultFolder( OlDefaultFolders.olFolderInbox ) );
}

// 各フォルダに対してProcess()を呼びます。また再帰的にフォルダを探索していきます。
static void Visit( MAPIFolder folder ) {
Process( folder );
foreach( MAPIFolder f in folder.Folders )
Visit( f );
}

// ちょっとだけ読みやすくなる拡張メソッドです。
static IEnumerable<Row> AsEnumerable( this Table table ) {
while( !table.EndOfTable )
yield return table.GetNextRow();
}

// 本題の処理です。Message-Id一覧を作成し、重複するものがあったらゴミ箱へ移動します。
static void Process( MAPIFolder folder ) {
var messageIds = new HashSet<string>();
var table = folder.GetTable( null, OlTableContents.olUserItems );
table.Columns.RemoveAll();
table.Columns.Add( "http://schemas.microsoft.com/mapi/proptag/0x1035001E" );
table.Columns.Add( "EntryID" );
var ns = folder.Session;
var trash = ns.GetDefaultFolder( OlDefaultFolders.olFolderDeletedItems );
foreach( var row in table.AsEnumerable() )
if( !messageIds.Add( (string)row[1] ) ) {
MailItem mailItem = (MailItem)ns.GetItemFromID( (string)row[2], null );
Console.WriteLine( "{0}", mailItem.Subject );
mailItem.Move( trash );
}
}
}

続いてC# 4.0でのコード。違いがあるのはProcess()メソッドだけです。
static void Process( MAPIFolder folder ) {
var messageIds = new HashSet<string>();
var table = folder.GetTable( TableContents: OlTableContents.olUserItems );
table.Columns.RemoveAll();
table.Columns.Add( "http://schemas.microsoft.com/mapi/proptag/0x1035001E" );
table.Columns.Add( "EntryID" );
var ns = folder.Session;
var trash = ns.GetDefaultFolder( OlDefaultFolders.olFolderDeletedItems );
foreach( var row in table.AsEnumerable() )
if( !messageIds.Add( row[1] ) ) {
MailItem mailItem = ns.GetItemFromID( row[2] );
Console.WriteLine( "{0}", mailItem.Subject );
mailItem.Move( trash );
}
}
まず、デフォルト引数に対応したため、本質的でないnull引数を記述しなくてもよくなっています。

次に、row[1]とrow[2]の実体はstring型ですが宣言はobject型でした。C# 4.0ではここがdynamic型になっているため、明示的なキャストなしでメソッド引数に使えます。
ns.GetItemFromID()の戻り値もobjectがdynamicに変わっています。実体はCOMオブジェクトです。dynamicのまま扱ってもSubjectプロパティやMove()メソッドは呼び出せますが、MailItem型ということがわかっていますのでキャストしています。その際にも明示的にキャスト式を書く必要はありません。

2009年6月4日木曜日

C# 4.0 dynamic

Visual Studio 2010 beta1で遊んでいます。
C# 4.0および.NET Framework 4.0で提供されるdynamicキーワードについて誤解していました。

dynamic宣言した変数はメソッド呼び出しを行っても、実行時にバインドされます。
ここまでは合ってます。

誤解していたのは、ソースコード上で明示的に宣言したもののみがdynamic変数になると考えていました。(object→dynamicにキャストするイメージでした。)しかし、Visual Studio 2010で試していてわかったのですが、PIAなどのクラスメソッドの戻り値がそもそもdynamicになっています。(C# 3.0まではobject型)
なので、今までstringなどの変数に代入するためにはobject→stringの明示的なキャストが必要でした。C# 4.0ではdynamicになっているため、stringに代入しようとするだけで自動的にキャストされます。C# 3.0のvarで受けた場合、dynamicのままでした。

文字だけなのでイメージがわきづらいですが、PIAを使った場合に思った以上にdynamicの恩恵が受けられそうです。

サンプルコードでも書けばわかりやすいかな?