ウィンドウプロシージャとメンバ関数を書いてみたものの、1つのthreadに複数のWindow Procedureが混在する場合、うまく振り分けできませんね。
どうすればいいんだろう…? まぁ、複数Windowを扱ったことがないからいいけど…。
2008年11月1日土曜日
ウィンドウプロシージャとメンバ関数その2
2008年10月26日日曜日
ウィンドウプロシージャとメンバ関数
コールバック関数の1つウィンドウプロシージャWindowProc()をクラスメンバ関数にしたいという話。
調べたところ、ATLはlinked listでクラスインスタンスを管理しておき、atlbase.inlファイルのAtlWinModuleExtractCreateWndData()関数でthread idをキーにして検索をしていました。これウィンドウメッセージが届くたびにやってる気がします。
読んで勉強になったのは、CreateWindowを呼び出したスレッドにウィンドウメッセージが届くのね。だからthread idがキーとして使える、と。
なら1歩進めてthread local storageに情報を置いてしまえばいいのでは?
class Window{
LRESULT WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam,LPARAM lParam ){
// ここが本体
...;
}
static __declspec(thread) Window* window;
static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ){
return window->WindowProc( hwnd, uMsg, wParam, lParam );
}
void CreateWindowOnThisThread(){
Window::window = this;
CreateWindowEx( .../* 適切に */ );
}
};
Window* Window::window = NULL;
こうすれば無難な関数に仕上がりました。ちなみにStaticWindowProc()はかなり負担の少ない実装になっていました。
mov eax, DWORD PTR _lParam$[esp-4]呼び出し規約がCALLBACKから__thiscallに変わるので、無意味なスタックの積み直しが行われてますが、まぁ仕方がありませんね。
mov ecx, DWORD PTR _wParam$[esp-4]
mov edx, DWORD PTR fs:__tls_array
push ebx
mov ebx, DWORD PTR _hwnd$[esp]
push eax
mov eax, DWORD PTR _uMsg$[esp+4]
push ecx
mov ecx, DWORD PTR [edx]
mov ecx, DWORD PTR Window::window[ecx]
call Window::WindowProc
pop ebx
ret 16
; 一部構文に矛盾がありますが、読みやすさのためのデマングルですので…
2008年10月22日水曜日
DateTimeと時差情報
DateTime構造体は2.0からKind情報を持つようになり、UTCとローカル時間を区別できるようになりました。これによってどんなことが起きるかというと…
DateTime now = DateTime.Now;ダメじゃん。epoch秒に慣れてる人は確実にはまりますね。
DateTime utcNow = now.ToUniversalTime();
Console.WriteLine( "now {0} utcNow", now == utcNow ? "==" : "!=" );
// => now != utcNow
MSDNを読むと…あー書いてある書いてある
DateTime オブジェクトの計算および比較では、対象となる複数のオブジェクトが同じタイム ゾーンの時刻を表している場合にのみ、意味のある結果を得ることができます。裏返せば、タイムゾーンが違ったら意味がない…。
この場合どうすればいいかというと、DateTimeOffset構造体を使うそうです。ただし、こちらは2.0SP1から。
2008年10月21日火曜日
Data Entity FrameworkとSQL Server Compact 3.5 SP1
Visual Studio 2008 SP1の新機能Data Entity Frameworkを使ってみたくなりました。ついでにずっと気になっていたSQL Server Compact 3.5 SP1と組み合わせて試してみることにしました。
SQL Server Compactはプロセス内で動作する軽量なSQLエンジンです。SQLiteみたいなものと言えばわかりやすいでしょうか。
Wizardに従って作っていきいざ実行すると…
ハンドルされていない例外: System.ArgumentException: 指定されたストア プロバイダが構成内に見つからないか、無効です。何を言っているのかわからない例外になります。
---> System.ArgumentException: 要求された .Net Framework データ プロバイダが見つかりません。これは、インストールされていない可能性があります。
「指定されたストア プロバイダ」とか言われてもWizardの生成したデフォルトそのままだし。「データ プロバイダが見つかりません」と言われてもサーバエクスプローラでもWizardでも表示できてるし…。
いろいろググりましたが見つかりません。ならばまずSQL Server Compact 3.5 SP1を単体で動かしてみましょう…
ハンドルされていない例外: System.DllNotFoundException: DLL 'sqlceme35.dll' を読み込めません: 指定されたモジュールが見つかりません。 (HRESULT からの例外: 0x8007007E)見つからないってどういうこと?
ならばプライベート ファイル ベースの配置をしてみましょう…
ハンドルされていない例外: System.BadImageFormatException: 間違ったフォーマットのプログラムを読み込もうとしました。 (HRESULT からの例外: 0x8007000B)…やっとっわかった気がする。
Microsoft SQL Server Compact 3.5 ReadmeやSQL Server Compact 3.5 と Visual Studioに色々書いてありました。
- SQL Server Compact 3.5 SP1 の 64 ビット リリースは、Microsoft ダウンロード センターからダウンロードできます (Web からの提供のみ)。
- Visual Studio と SQL Server Compact 3.5 で 64 ビット開発を行う場合は、[コンパイラの詳細設定] の [ターゲット CPU] オプションを明示的に [x86] に設定する必要があります。
ええ、私はVista x64で開発してますよ…。Visual Studio自身が32bitで動作していることを忘れてましたよ。普通、開発環境なんだから64bit版もインストール済みと思うよっ!
2008年10月20日月曜日
ArraySegment classと拡張メソッド
C#で部分配列を扱いたくなったとき、unsafe fixedしてpointerを使うこともできますが、ArraySegment classでがんばってみようと思いました。ところがArraySegmentにはインデクサなどのアクセッサが用意されてなく扱いづらいです。
var array = new int[10];
var segment = new ArraySegment<int>( array, 5, 5 );
segment.Array[ segment.Offset + 3 ]; // array[8] 相当
ここで拡張メソッドを思い出しました。といっても拡張インデクサは実装できません。気を取り直してArray.GetValue()やArray.SetValue()の真似をしてみました。
public static class ArraySegmentUtility{こうすれば最初の例は
public static T GetValue<T>( this ArraySegment<T> segment, int index ){
return segment.Array[ segment.Offset + index ];
}
public static void SetValue<T>( this ArraySegment<T> segment, T value, int index ){
segment.Array[ segment.Offset + index ] = value;
}
}
segment.GetValue( 3 );おおいい感じ。
早速使ってみよう…
segment.Array[ segment.Offset + i ] ^= x;ぽか~ん。アクセッサが用意されてない理由がわかった気がする…。
2008年10月19日日曜日
2008年9月30日火曜日
XmlWriter.Flush()
XmlWriterを使っているとよくXMLが崩れます。ってXmlWriterが悪いような書き方ですが、単に使い方が間違っていました。
普段、auto flushに慣れているわけですが、残念ながらXmlWriterはauto flushモードがなく、定期的にFlush()メソッドを呼び出す必要があります。
といっても、元々buffer fullになればFlush()されるわけですから本当に必要なのは、最後の1回だけです。
デストラクタが自動的にFlush() / Close()してくれるんじゃ…と思いましたがなんとデストラクタがありません。
となると手動でFlush()かClose()かDispose()を呼ぶ必要があるわけですが、Dispose()がpublicではありません。この場合のFlush()は意味が違いますし、Close()で変なステートメントを増やしたくないですし…
結局、using構文が一番しっくりきます。
