ユーザ用ツール

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;
}

メッセージハンドリング

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

オプションダイアログ

リンク

1)
C#.NETで作る方法もあるが、どの程度実用的なのか不明。性能的には疑問符が付く。
2)
この問題で半日悩んだのは秘密。
3)
最初適当なIDで作ったらやっぱり表示されなかった。どこにも、ダイアログを表示するコードフラグメントがないのだから、どっかでシステムが決めウチで面倒を見てくれているのだろう。

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

More information