目次
Todayプラグインの作成
Todayプラグインとして実装すべきもの
Todayプラグインは、Today本体が適切にイベントを送り、また、画面を破綻させないように、定められた作法で作る必要がある。 WindowsCEの初期のものから、PocketPCを経るうちに拡張されたものもあり、これらの情報が散在していて探すのが面倒である。
ここに、USB Select Todayを実装するにあたって、調べた事項をまとめておく。
エントリポイント
Todayプラグインは、DLLとして実装しなければならない。 このため、基本的には、C++/Win32アプリとして実装しなければならない。1)
Visual Studio 2005の場合、C++でスマートデバイスアプリを選んで、Win32のDLLを作成するプロジェクトを作る。
リソースは、普通に作成することが出来るが、resource.hの内容は自動的には includeされないので、stdafx.hの最後に、
#include "resource.h"
を追記しておく。.cppファイルの中で #include “resource.h”を指定しても、何故か、全く読み込まれないので、注意が必要である。2)
さらに、Todayプラグインのお約束としては、次の内容の、defファイルを作る必要がある。
EXPORTS InitializeCustomItem @ 240 NONAME CustomItemOptionsDlgProc @ 241 NONAME
InitializeCustomItemとCustomItemOptionsDlgProcは、Today本体がプラグインを呼び出すためのエントリである。.cppの中にエントリだけ作っても、.defファイルを作成して、この内容を記述していないと、プラグインの一覧にさえ現れないので、忘れないようにする。
DllMain
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ); [in] hModule これはインスタンスへのハンドル。必要に応じて大域変数にでも保存しておく。 [in] ul_reason_for_call 呼び出された理由。DLL_PROCESS_ATTACHとDLL_PROCESS_DETACHが想定される。 [in/out?] lpReserved 予約。使われていないはず。
DllMainは、全てのDLLが備えていなければならないエントリポイントである。
ここでは、モジュールのインスタンスハンドルを取得したり、アイコンなどのリソースを用意したり、WNDCLASSを登録したりするのがDLL_PROCESS_ATTACH時に、またこれらの解放を行なうのがDLL_PROCESS_DETACH時に求められる動作である。
ここで、WndProc()を登録しないと、イベントがやってこないので、確実に行なう。
// IDS_TODAY_XXX_APPNAME は適当な文字列リソースを作っておくこと。 // 別に直接、L"SAMPLE" などとして、文字列を埋め込んでもいいだろうが // あまり美しくないと思う。 // WndProcは、LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM) で // 別途作成しておくこと。 WNDCLASS wc; memset(&wc, 0, sizeof(wc)); wc.style = 0; wc.lpfnwndProc = (WNDPROC)WndProc; wc.hInstance = hModule; wc.hIcon = 0; wc.hCursor = 0; wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszClassName = (LPCTSTR)LoadString(hModule, IDS_TODAY_XXX_APPNAME, 0, 0); if (!RegsterClass(&wc)) { // 登録失敗 }
DLL_PROCESS_DETACHが来たら、確保したリソースなどを解放するとともに、クラスの登録を削除する。
// g_hInst は ATACHした際に、hModuleをコピーしておいた大域変数 // IDS_TODAY_XXX_APPNAMEは、上を参照。 UnregisterClass((LPCTSTR)LoadString(g_hInst, IDS_TODAY_XXX_APPNAME, 0, 0), g_hInst);
InitializeCustomItem
HWND APIENTRY InitializeCustomItem( TODAYLISTITEM *ptlistItem, HWND hwndParent ); [in] ptlistItem szName, tlit, dwOrder, fEnabled, fOptions,およびgrfFlagsが設定されている。 fEnabledがFALSEなら、即座にNULLを返して終了すること。 [out] hwndParent
目に見えない初期化は、DllMainがDLL_PROCESS_ATTACHを受け取ったときに、行なわれている。ここでは、目に見える部分の初期化を行なう。即ち、ウィンドウを作成し、ウィジェットを適切に配置して、パネルの表示を行なう。
このとき、ptlistItem→grfFlagsに、オプション設定が渡ってきている。オプション設定画面を持つパネルの場合には、この情報に基づいて、表示や動作のカスタマイズを行なう。
ウィンドウは高さを0で作成しておく。WndProc()で、WM_TODAYCUSTOM_QUERYREFRESHCACHEを受け取ったときに、ptlistItem→cypを検査し、これが0ならば、適切な高さを設定し、描画イベントが発生する用に TRUEを返す。これが、初期化動作ということになる。
// g_hInst は、インスタンスハンドル // g_hWnd は、作成したウィンドウハンドルを格納する大域変数 // IDS_TODAY_XXX_APPNAME は、文字列リソースのID値 HWND APIENTRY InitializeCustomItem(TODAYLISTITEM *ptlistItem, HWND hwndParent) { LPCTSTR appName = (LPCTSTR)LoadString(g_hInst, IDS_TODAY_XXX_APPNAME, 0, 0); // Windowの高さは '0'で作る。 g_hWnd = CreateWindow(appName, appName, WS_VISIBLE | WS_CHILD, CW_USEDEFAULT, CW_USEDEFAULT, 240, 0, hwndParent, NULL, g_hInst, NULL); // 他に必要なリソースがあればここで作成しておく // ウィンドウハンドラを登録する SetWindowLong(g_hWnd, GWL_WNDPROC, (LONG)WndProc); // ウィンドウを表示する ShowWindow(g_hWnd, SW_SHOWNORMAL); UpdateWindow(g_hWnd); return g_hWnd; }
CustomItemOptionsDlgProc
BOOL APIENTRY CustomItemOptionsDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam ); [in] hDlg ダイアログのハンドル [in] message メッセージID [in] wParam 16bitパラメータ(メッセージに依存) [in] lParam 32bitパラメータ(メッセージに依存)
レジストリに、Options == 1をセットした場合には、このエントリに中身を書いてやらなければならない。0なら、ただリターンするだけの関数でもいいだろう。
このハンドラに対応するダイアログは、IDD_TODAY_CUSTOM (500)をIDとして作成されていなければならない…あんまりちゃんと書いてないけれど。3)
オプションは、DWORD TODAYLISTITEM::grfFlagsを介して、パネル本体とやり取りされる。32bitのデータ幅なので、大抵の場合は、単純なON/OFFや、機能番号などを渡すのに用いるのだろう。他の用い方があるかどうかは調べていない。USB Select Todayは、5bitを受け渡しに使っている。
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +-----------------------------------------------------+-+-+-+-+-+ |あき |I|3|2|1|0| +-----------------------------------------------------+-+-+-+-+-+ I: 初期化フラグ 0なら初期状態 0〜3は不定と見なし、全て1に初期化される 0〜3: ActiveSync(NDIS), Modem, Mass Storage, Serialの各アイコンを表示するか否か。
WM_INITDIALOG中で、ptlistItem == NULLのケースが初期状態ではないことに注意する。初回からちゃんと、ptlistItemは割り当てられている。ので、上のような初期化フラグを1bit設けた。別のやり方も勿論あるだろう。
BOOL APIENTRY CustomItemOptionsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { static TODAYLISTITEM *s_ptlistItem = NULL; switch (message) { case WM_INITDIALOG: { SHINITDLGINFO shidi; // ダイアログを初期化 shidi.dwMask = SHIDIM_FLAGS; shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN; shidi.hDlg = hDlg; SHInitDialog(&shidi); // ptlistItemを取得 s_ptlistItem = (TODAYLISTITEM*)lParam; if (s_ptlistItem) { // 例えばこれはUSB Select Todayの初期化シーケンス if ((s_ptlistItem->grfFlags & 0x10) == 0) { s_ptlistItem->grfFlags |= 0x1f; } } else { // これは異常系、どうするかは決めてください。 } // チェックボタンなどのウィジェットの状態を初期化 } return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK) { // ウィジェットの値を読み取って、grfFlagsに設定する。 // ダイアログを閉じる EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; case WM_DESTROY: s_ptlistItem = NULL; break; } return FALSE; }