2007年12月16日日曜日

C++/CLIと#include <Windows.h>

もっと根本的な問題#define MessageBox MessageBoxWとSystem::Windows::Forms::MessageBoxの名前がぶつかる問題とか解決してほしいなぁ。
と書いたけど、一応現時点での対策も書いておきます。
#include <Windows.h>
...
#pragma push_macro("MessageBox")
#undef MessageBox
MessageBox::Show( "message", "caption" );
#pragma pop_macro("MessageBox")
です。でも面倒くさい…。

VS2008の変更点: C++

VS2008を使い始めて気がついた点をあげていきます。今回はC++について。

まず/Wp64(64 ビット移植性の問題の検出)のサポートがなくなっています。VS2005の1代でつぶれました。せっかく警告たとしても、Win32では無理やりcastするのが正しいときが多々あり、警告の意味が薄れてしまうからでしょうか。更にはx64コンパイラも用意されていますし、x64プロセッサもクライアントPCにまで出回ってきて、実機でテストしなさい、ってことかもしれません。

新しくSTL/CLRライブラリとC++マーシャリングライブラリが増えています。STL/CLRは本当はVS2005に入るはずだったとか間に合わなかったとかだそうで? C++/CLIは個人的にはC#で書ききれないWin32 nativeな部分のマーシャリング?に使うものという位置づけで、STLの出番はちょっと少ないです。JITで最適化していく.NETでSTL/CLRのコンパイル時インライン展開の最適化はどうなんでしょう?
もう一つのC++マーシャリングはまぁ、今まで必要になった人が書いていたものをあらかじめ用意しましたよ、程度で。こちらもgenericではなくtemplateで書かれているのでコンパイル時にインライン展開されます。
マーシャリングはうれしいけど、もっと根本的な問題#define MessageBox MessageBoxWとSystem::Windows::Forms::MessageBoxの名前がぶつかる問題とか解決してほしいなぁ。

今回なくなったライブラリとしてはATL ServerとATL_MIN_CRT関連。obsolateではなくきれいさっぱり削除されています。ATL Serverは使っていませんでしたがATL_MIN_CRT関連は使っていたのでちょっと痛手。もちろん#ifdefで分けていたので、ファイルサイズが大きくなる程度ですが、気分的に。
一応

Visual C++ 2008では、ATL_MIN_CRTが定義されているかどうかに関係なく、ATLプロジェクトのCRTに対する依存はすべて最小限に抑えられます。
と書かれていますが、この説明に意味はなく、ソースを見る限り「リンカが選択的にリンクしますよ」程度のことでした。

ついでにATLのソースをVS2005とVS2008で比較してみたところ、気づいたことが2点ありました。VS2005ではATL_MIN_CRTの影響なのかセキュリティ強化されたCRTを使わないバージョンが存在しましたが、VS2008ではセキュリティ強化されたCRTが強制的に使われます。おかげでガンガンにCRTを使います。
もう1点、なぜかVS2008でも_MTを見ています。VS2005以降、シングルスレッドCRTライブラリが削除されてマルチスレッドCRTライブラリしか存在しないわけですが、ATLは_MTを見てシングルスレッド動作も意識しています。…でもATL_MIN_CRTが廃止されたためにマルチスレッドCRTライブラリが必須なはずですが f(^^;

2007年12月15日土曜日

Visual Studio Team System 2008 Team Suite

VS2008の続報です。Visual Studio Team System 2008 Team Suite日本語版が12/14にリリースされていました。国内での販売は2008/02/01からだそうです。
ところで、.NET Framework 2.0 SP1日本語 Language Packや.NET Framework 3.0SP1はどうなってることやら。インストールして詳しく調べることにします。

2007年12月3日月曜日

ダイアログの表示のされ方(2)

ダイアログの表示のされ方(1)の続きです。
ダイアログには、タイトルバーがあり各種ボタンが並びますが、これまた制御がややこしいため調査しました。
ボタンの場合、表示されているかどうかの他に、動作するかどうかもあります。簡単な話に聞こえますが、表示されていなくてもマウス操作やショートカットキーで動作することもあるため、独立して調査する必要があります。

// ControlBoxが表示される条件
Form.ControlBox == true && Form.ShowIcon == true &&
( Form.FormBorderStyle == FormBorderStyle.FixedSingle || Form.FormBorderStyle == FormBorderStyle.Fixed3D ||
Form.FormBorderStyle == FormBorderStyle.Sizable ||
Form.FormBorderStyle == FormBorderStyle.FixedDialog && Form.Icon != null );

// ControlBoxが動作する条件
Form.ControlBox == true && Form.FormBorderStyle != FormBorderStyle.None;
アイコンが表示されていなくても、Alt+Spaceで動作します。最初からかなりややこしい条件で、泣けました。
// HelpButtonが表示される条件、及び動作する条件
Form.HelpButton == true && Form.ControlBox == true && Form.MinimizeBox == false && Form.MaximizeBox == false &&
( Form.FormBorderStyle == FormBorderStyle.FixedSingle || Form.FormBorderStyle == FormBorderStyle.Fixed3D ||
Form.FormBorderStyle == FormBorderStyle.FixedDialog || Form.FormBorderStyle == FormBorderStyle.Sizable );
Shift+F1については確認できていません。今やヘルプボタンはマイナーな存在になりつつあるため、気にする必要はないかもしれませんね。
// MinimizeBoxが表示される条件、及びMaximizeBoxが表示される条件
( Form.MinimizeBox == true || Form.MaximizeBox == true ) && Form.ControlBox == true &&
( Form.FormBorderStyle == FormBorderStyle.FixedSingle || Form.FormBorderStyle == FormBorderStyle.Fixed3D ||
Form.FormBorderStyle == FormBorderStyle.FixedDialog || Form.FormBorderStyle == FormBorderStyle.Sizable );

// MinimizeBoxが動作する条件
Form.MinimizeBox == true && Form.ControlBox == true &&
( Form.FormBorderStyle == FormBorderStyle.FixedSingle || Form.FormBorderStyle == FormBorderStyle.Fixed3D ||
Form.FormBorderStyle == FormBorderStyle.FixedDialog || Form.FormBorderStyle == FormBorderStyle.Sizable );

// MaximizeBoxが動作する条件
Form.MaximizeBox == true && Form.FormBorderStyle != FormBorderStyle.None;
最小化ボタン・最大化ボタンどちらかが有効になっているときに表示されます。最小化のショートカットを知らないため、見えていて有効になっているとき動作するとしました。最大化に関してはタイトルバーのダブルクリックを含めています。
// CloseBoxが表示される条件
Form.ControlBox == true && Form.FormBorderStyle != FormBorderStyle.None;

// CloseBoxが動作する条件
true;
ControlBoxプロパティそのものでした。Alt+F4は絶えず有効でした。

…疲れました。

2007年12月2日日曜日

ダイアログの表示のされ方(1)

タイトルは何の変哲もないものになってしまいました。
Windowsでは、ダイアログを出すとき、タスクバーに表示されるか・Alt+TABのリストに並ぶか・タスクマネージャに表示されるか、それぞれ制御できるようです。ところが.NET Frameworkには直接的に制御できるのはShowInTaskbarというプロパティぐらいしかなく、制御しづらい上にわかりにくいです。
そこでひたすらパターンを組み合わせてどのように表示されるかまとめてみました。とはいえ、総当たり表を見てもパターンが多すぎてわかりづらいので、逆引き形式で並べてみます。

// タスクバーに表示される条件
Form.ShowInTaskbar == true;

// 3D Flipに表示される条件
Form.ShowInTaskbar == true;

// Alt+Tabに表示される条件
Form.ShowInTaskBar == true ||
Form.FormBorderStyle != FormBorderStyle.FixedToolWindow && Form.FormBorderStyle != FormBorderStyle.SizableToolWindow;

// タスクマネージャに表示される条件
Form.FormBorderStyle != FormBorderStyle.FixedToolWindow && Form.FormBorderStyle != FormBorderStyle.SizableToolWindow;

だんだん、関係ないパラメータに依存してきていることがわかりました。

さらに、タスクバー・Alt+Tab・タスクマネージャにはダイアログアイコンが表示されます。表示されるアイコン自体は共通したものでしたが、アイコンの選択のされ方には微妙な違いがありました。
if( Form.ShowIcon == true && Form.Icon != null )
TheIcon = Form.Icon;
else if( Form.ShowIcon == true && Form.Icon == null && Form.FormBorderStyle != FormBorderStyle.FixedDialog )
TheIcon = Form.ParentForm.Icon;
else
TheIcon = null;

この式でTheIconに値が設定されないときはタスクバーなりタスクマネージャなりのデフォルトが使われる模様?
こうもややこしいと困りますね。

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が追跡できている。