2007年11月29日木曜日

Dictionary.TryGetValue

Dictionary.TryGetValue()にはまった。

このメソッドから制御が戻るときに、キーが見つかった場合は、指定したキーに関連付けられている値が格納されます。それ以外の場合は value パラメータの型に対する既定の値。このパラメータは初期化せずに渡されます。
と書かれているものの深く読んでなくて、valueは変更されないものと勘違い。
書かれているとおり、見つからなかった場合は既定値で初期化されます。

2007年11月23日金曜日

Bitmap、ColorPalette

ごめんなさい、ColorPaletteのAccessViolationExceptionは別の原因でした。
Bitmapコンストラクタには

scan0パラメータで指定されたメモリブロックの割り当てと解放は呼び出し元が行いますが、関連するBitmapが解放されるまでメモリは解放されません。
とありますが、これを守っていませんでした。Bitmapコンストラクタが終了した時点でメモリブロックを解放していました。Bitmap.LockBits()をするのが正解なようです。

ColorPaletteクラスが変則的な理由を調べるためにGDI+について調べました。
GDI+はもともとnative(unmanaged)で実装されていてBitmapクラスなどはそのwrapperになっています。そしてColorPaletteはというと、単なる構造体であり対応するnativeクラスはありません。…だからと言って扱いづらいクラス設計をしていいわけではありませんがっ。

見た感じColorPaletteクラスは複数のBitmapクラスで共有してもよさそうです。というよりも対応するnativeクラスが存在しないため、Bitmap.Paletteに代入された時点で値だけがnativeクラス側にコピーされColorPaletteの共有状態にはならないようです。

2007年11月22日木曜日

ColorPalette

複数のBitmapが同一のColorPaletteを持つとき、このColorPaletteが非常に扱いづらい。

  1. なぜかコンストラクタがない。
  2. そのためBitmap.Paletteプロパティから取得する必要がある。
  3. ところがBitmap.Paletteプロパティにアクセスするたびに新しいインスタンスが生成される。
文章だと説明しづらいので問題になるコードを
Bitmap[] bitmaps; // ここにColorPaletteを設定したい
Color[] colors; // このColorを設定したい

foreach( Bitmap bitmap in bitmaps ){
for( int i = 0; i < 256; i++ )
bitmap.Palette.Entries[i] = color[i];
bitmap.MakeTransparent(); // ついでに透明色の設定
}
ちゃんとColorを設定しているつもりでも、毎回ColorPaletteが生成され直してColor値が保存されない。じゃあ、どうするかというと
Bitmap[] bitmaps; // ここにColorPaletteを設定したい
Color[] colors; // このColorを設定したい

foreach( Bitmap bitmap in bitmaps ){
ColorPalette palette = bitmap.Palette; // ColorPaletteを取得、この時点でBitmapとは切り離されている
for( int i = 0; i < 256; i++ )
palette.Entries[i] = color[i];
bitmap.Palette = palette;
bitmap.MakeTransparent(); // ついでに透明色の設定
}
こんな感じ。コンストラクタが提供されないにも係わらず、インスタンスの設定が求められるのはおかしい。
ここでふと気づく。ループごとにColorPaletteを作成し直すのは無駄では? つまり
Bitmap[] bitmaps; // ここにColorPaletteを設定したい
Color[] colors; // このColorを設定したい

ColorPalette palette = bitmaps[0].Palette; // ColorPaletteを取得、この時点でBitmapとは切り離されている
for( int i = 0; i < 256; i++ )
palette.Entries[i] = color[i];
foreach( Bitmap bitmap in bitmaps ){
bitmap.Palette = palette;
bitmap.MakeTransparent(); // ついでに透明色の設定
}
こうすればColorPaletteの作成は1回で済む…というのは罠です。ColorPaletteはクラスなので、複数のBitmapでインスタンスが共有されてしまいます。具体的にはMakeTransparent()でAccessViolationExceptionが発生したりする。
インスタンスを独立させたくてもICloneable.Clone()も提供されないので、ひたすらコピーするしかない。なんとも迷惑な話です。

2007年11月20日火曜日

インサイドWindows第4版上

セカンド・オピニオン OS小論:OSの構造をもう少し考えてみるを見つけました。
奥深くまでいろいろ語られていて参考になりました。その影響で「インサイドWindows第4版上」なる本を買ってしまいました。下は売ってなかったのでそのうちにでも。

VS2008

Visual Studio Team System 2008 Team Suite提供開始。まずは英語版を入手。
LINQやAjaxや面白い機能がいっぱい。既にβ版を試してたけど、自分の目線で追々紹介していきます。

2007年11月19日月曜日

プロセッサの情報

コンピュータのプロパティはVistaだとこんな感じ。xpもダイアログは違うけど、プロセッサについて書かれてる。周波数は2回書かれてるし、空白のバランスが悪い。
でもこれには理由があって、赤下線部分の文字列、これはCPUから直接取得できる。こんな感じ。Visual C++依存のコードです。

#include <stdio.h>
#include <intrin.h>

int main(){
int cpuInfo[4];
__cpuid( cpuInfo, 0x80000000 );
if( cpuInfo[0] >= 0x80000004 ){
char brand[48];
__cpuid( (int*)&brand[ 0], 0x80000002 );
__cpuid( (int*)&brand[16], 0x80000003 );
__cpuid( (int*)&brand[32], 0x80000004 );
printf( "%.48s\n", &brand[0] );
}
return 0;
}

2007年11月18日日曜日

プレビュー


こんなことして遊んでいます。RO以外でも使えるclass libraryができました。

  • ZlibStream
    zlibなstreamを読み書きできる。
  • FileDataObject
    実在しないfileをclipboardに入れる。explorerにdropするときにも使える。ただしDataObject classと相性が悪い。
  • Tga
    TGA形式の画像をBitmapクラスに読み込む。

IDataObject

IDataObject、DataFormats.Format、FORMATETCこの辺りは定義がいい加減で困る。

しょーもないところではIDataObject.DUnadvise()。戻り値voidなのに戻り値の解説がついてます。Cの#define値を見せられてもどうにもなりません…そもそもどうやって返せと。.NET Framework 3.5ではドキュメント修正されてめでたしめでたし…んなことありません。
お隣りのIDataObject.DAdvise()。戻り値intだから解説が…ふむふむ真っ当に見える? ところが、OLE_E_ADVISENOTSUPPORTEDを返そうとすると、refやらoutの値を設定しなさいとコンパイルエラーが。いえ、設定できないからOLE_E_ADVISENOTSUPPORTEDを返したいのですが…結局、戻り値ではなく例外を投げることに。

本題はこっち。FORMATETC.cfFormatはshort型。実際の値としてはshortで負になる値がよく使われる。
でもDataFormats.Format.Idとしてはint型なので正に…単純に==演算子で比較すると一致しません。結局

FORMATETC formatEtc;
DataFormats.Format dataFormat;
if( formatEtc.cfFormat == unchecked((short)dataFormat.Id) ){
...;
}
とするしかないでしょうか。

2007年11月17日土曜日

GCHandle.AddrOfPinnedObject()とToIntPtr()の違い


GCHandle

managed objectに対するHandleを表す。

GCHandle.Alloc()

managed objectに対するGCHandleを取得する。同時に固定することも可能。

GCHandle.AddrOfPinnedObject()

GCHandleが表しているmanaged objectのaddressを返す。このとき、GCHandleが表しているmanaged objectはあらかじめ固定していないと例外が発生する。

GCHandle.ToIntPtr()

GCHandle自身のaddressを返す。GCHandleが表しているmanaged object自体は固定されている必要はない。

GCHandle.FromIntPtr()

GCHandle.ToIntPtr()で得られたaddressからGCHandleへ復元する。もちろん同じmanaged objectが追跡できている。