====== Todayプラグインの作成 ======
====== Todayプラグインとして実装すべきもの ======
Todayプラグインは、Today本体が適切にイベントを送り、また、画面を破綻させないように、定められた作法で作る必要がある。
WindowsCEの初期のものから、PocketPCを経るうちに拡張されたものもあり、これらの情報が散在していて探すのが面倒である。
ここに、USB Select Todayを実装するにあたって、調べた事項をまとめておく。
===== エントリポイント =====
Todayプラグインは、DLLとして実装しなければならない。
このため、基本的には、C++/Win32アプリとして実装しなければならない。((C#.NETで作る方法もあるが、どの程度実用的なのか不明。性能的には疑問符が付く。))
Visual Studio 2005の場合、C++でスマートデバイスアプリを選んで、Win32のDLLを作成するプロジェクトを作る。
リソースは、普通に作成することが出来るが、resource.hの内容は自動的には includeされないので、stdafx.hの最後に、
#include "resource.h"
を追記しておく。.cppファイルの中で #include "resource.h"を指定しても、何故か、全く読み込まれないので、注意が必要である。((この問題で半日悩んだのは秘密。))
さらに、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として作成されていなければならない...あんまりちゃんと書いてないけれど。((最初適当なIDで作ったらやっぱり表示されなかった。どこにも、ダイアログを表示するコードフラグメントがないのだから、どっかでシステムが決めウチで面倒を見てくれているのだろう。))
オプションは、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;
}
===== メッセージハンドリング =====
==== WM_CUSTOMTODAY_QUERYREFRESHCACHE ====
==== WM_CUSTOMTODAY_CLEARCACHE ====
==== WM_CUSTOMTODAY_RECEIVEDSELECTION ====
==== WM_CUSTOMTODAY_LOSTSELECTION ====
==== WM_CUSTOMTODAY_USERNAVIGATION ====
==== WM_CUSTOMTODAY_ACTION ====
==== WM_LBUTTONUP ====
==== WM_PAINT ====
==== WM_ERASEBKGND ====
===== API =====
==== TODAYM_GETCOLOR ====
==== TODAYM_DRAWWATERMARK ====
==== TODAYM_TOOKSELECTION ====
===== レジストリ =====
==== DLL ====
==== Type ====
==== Options ====
==== Selectability ====
===== オプションダイアログ =====
====== リンク ======
* [[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mobilesdk5/html/mob5conWritingCustomTodayScreenItem.asp|Today Screen]]
* [[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnppcgen/html/today_screen_selection_api.asp|Today Screen Plug-in Selection API in Windows Mobile 2003 Second Edition Software for PocketPCs]]
* [[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnppcgen/html/TodayScrn.asp|Creating a Pocket PC Today Screen Plug-in with .NET Compact Framework]]
[[W-ZERO3:W-SIM端末:Windows Mobileに関して]]