Input Methodを作ってみる。難しそうだが、そうでもない。
最初は、プロジェクトを作る。 WCE ATL COM AppWizardを選ぶ。
次のようなスケルトンが出来上がる。
プロジェクトができたら、メニューから[挿入(I)]-[ATLオブジェクトの新規作成(A)]を選んで、ATL オブジェクトウィザードダイアログボックスを表示させる。
ここで、Simple Objectを選んで、[次へ]をクリックし、ATL オブジェクトウィザードのプロパティダイアログボックスを表示させる。
名前をつけ、OKボタンをクリックして、オブジェクトを追加する。
eMbedded Visual C++ 3.0のサンプルにあるdvoraksip.tlbを利用してIInputMethodをインプリメントする。 まず、ワークスペース上にあるオブジェクトを右クリックして、メニューを表示させ、インターフェースのインプリメントを選択する。
警告ダイアログボックスのOKボタンをクリックする。
使用可能なタイプライブラリの参照ボタンをクリックし、eMbedded Visual C++ 3.0のサンプルにあるdvoraksip.tlbを選択する。
IInputMethodを選択する。
// TestIM.h : Declaration of the CTestIM #ifndef __TESTIM_H_ #define __TESTIM_H_ #include "resource.h" // main symbols #import "C:¥Windows CE Tools¥wce300¥Pocket PC 2002¥samples¥atl¥dvoraksip¥dvoraksip.tlb" raw_interfaces_only, raw_native_types, no_namespace, named_guids ///////////////////////////////////////////////////////////////////////////// // CTestIM class ATL_NO_VTABLE CTestIM : public CComObjectRootExここで、次の4箇所のコードを変更する。, public CComCoClass , public IDispatchImpl , public IInputMethod { public: CTestIM() { } DECLARE_REGISTRY_RESOURCEID(IDR_TESTIM) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CTestIM) COM_INTERFACE_ENTRY(ITestIM) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IInputMethod) END_COM_MAP() // ITestIM public: // IInputMethod STDMETHOD(Select)(wireHWND hwndSip) { return E_NOTIMPL; } STDMETHOD(Deselect)() { return E_NOTIMPL; } STDMETHOD(Showing)() { return E_NOTIMPL; } STDMETHOD(Hiding)() { return E_NOTIMPL; } STDMETHOD(GetInfo)(_tagImInfo * pimi) { if (pimi == NULL) return E_POINTER; return E_NOTIMPL; } STDMETHOD(ReceiveSipInfo)(_tagSipInfo * psi) { return E_NOTIMPL; } STDMETHOD(RegisterCallback)(IIMCallback * lpIMCallback) { return E_NOTIMPL; } STDMETHOD(GetImData)(ULONG dwSize, VOID * pvImData) { if (pvImData == NULL) return E_POINTER; return E_NOTIMPL; } STDMETHOD(SetImData)(ULONG dwSize, VOID * pvImData) { return E_NOTIMPL; } STDMETHOD(UserOptionsDlg)(wireHWND hwndParent) { return E_NOTIMPL; } }; #endif //__TESTIM_H_
// TestIM.h : Declaration of the CTestIM #ifndef __TESTIM_H_ #define __TESTIM_H_ #include "resource.h" // main symbols #include <sip.h> ///////////////////////////////////////////////////////////////////////////// // CTestIM class ATL_NO_VTABLE CTestIM : public CComObjectRootEx, public CComCoClass , public IDispatchImpl , public IInputMethod { public: CTestIM() { } DECLARE_REGISTRY_RESOURCEID(IDR_TESTIM) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CTestIM) COM_INTERFACE_ENTRY(ITestIM) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IInputMethod) END_COM_MAP() // ITestIM public: // IInputMethod STDMETHOD(Select)(HWND hwndSip) { return E_NOTIMPL; } STDMETHOD(Deselect)() { return E_NOTIMPL; } STDMETHOD(Showing)() { return E_NOTIMPL; } STDMETHOD(Hiding)() { return E_NOTIMPL; } STDMETHOD(GetInfo)(_tagImInfo * pimi) { if (pimi == NULL) return E_POINTER; return E_NOTIMPL; } STDMETHOD(ReceiveSipInfo)(SIPINFO * psi) { return E_NOTIMPL; } STDMETHOD(RegisterCallback)(IIMCallback * lpIMCallback) { return E_NOTIMPL; } STDMETHOD(GetImData)(ULONG dwSize, VOID * pvImData) { if (pvImData == NULL) return E_POINTER; return E_NOTIMPL; } STDMETHOD(SetImData)(ULONG dwSize, VOID * pvImData) { return E_NOTIMPL; } STDMETHOD(UserOptionsDlg)(HWND hwndParent) { return E_NOTIMPL; } }; #endif //__TESTIM_H_
さらに、return E_NOTIMPLをreturn S_OKに変更する。 ここまでで、とりあえず、ビルドはできる。しかし、まったく使えないし、InputPanelにも変化はおきない。
COMサーバーの登録はレジストリにCOMサーバーの情報を書き込むことで行われる。 InputMethodを作成したときにはその情報もレジストリに書き込む必要がある。 レジストリに書き込まれる情報は、*.rgsファイルに存在している。 次の部分を追加する。
HKCR { Test.TestIM.1 = s 'TestIM Class' { CLSID = s '{996248C9-39E6-44A1-9E37-386D1F486A67}' } Test.TestIM = s 'TestIM Class' { CLSID = s '{996248C9-39E6-44A1-9E37-386D1F486A67}' CurVer = s 'Test.TestIM.1' } NoRemove CLSID { ForceRemove {996248C9-39E6-44A1-9E37-386D1F486A67} = s 'TestIM Class' { ProgID = s 'Test.TestIM.1' VersionIndependentProgID = s 'Test.TestIM' IsSIPInputMethod = s '1' ForceRemove 'Programmable' InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Apartment' } 'TypeLib' = s '{63A3EBFB-859B-4A7C-A1D9-E70A5C652401}' } } }
これで、ビルドするとInputPanelに名前は表示されるようになる。
CTestIM() { m_hImageList1 = ImageList_Create(16,16,ILC_COLOR | ILC_MASK,1,0); HBITMAP hBitmap1 = LoadBitmap(_Module.m_hInst,MAKEINTRESOURCE(IDB_LIST1)); ImageList_AddMasked(m_hImageList1,hBitmap1,RGB(255,255,255)); DeleteObject(hBitmap1); m_hImageList2 = ImageList_Create(32,16,ILC_COLOR | ILC_MASK,1,0); HBITMAP hBitmap2 = LoadBitmap(_Module.m_hInst,MAKEINTRESOURCE(IDB_LIST2)); ImageList_AddMasked(m_hImageList2,hBitmap2,RGB(255,255,255)); DeleteObject(hBitmap2); }イメージリストハンドルのメンバ変数も定義しておく。
HIMAGELIST m_hImageList1; HIMAGELIST m_hImageList2;
ここで作ったイメージリストをGetInfo()で渡されるpimiにセットする。
STDMETHOD(GetInfo)(_tagImInfo * pimi) { if (pimi == NULL) return E_POINTER; pimi->hImageNarrow = m_hImageList1; pimi->iNarrow = 0; pimi->hImageWide = m_hImageList2; pimi->iWide = 0; RECT rcSipRect = {0,0,240,80}; pimi->rcSipRect = rcSipRect; return S_OK; }
これで、このIMを選択したときに作成したBitmapが表示されるようになる。
#pragma once #includeclass CIMPanel : public CWindowImpl<CIMPanel> { BEGIN_MSG_MAP(CIMPanel) MESSAGE_HANDLER(WM_PAINT,OnPaint) END_MSG_MAP() public: private: LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { PAINTSTRUCT ps; HDC hDC = BeginPaint(&ps); RECT rc = {0,0,240,80}; FillRect(hDC,&rc,(HBRUSH)GetStockObject(WHITE_BRUSH)); POINT pt[] = {{0,0},{239,0}}; Polyline(hDC,pt,2); SetTextColor(hDC,RGB(255,0,0)); rc.top = 1; DrawText(hDC,L"A",1,&rc,DT_CENTER | DT_VCENTER); EndPaint(&ps); return 0; } };
これをIMクラスのメンバ変数に追加
CIMPanel m_panel;
selectメソッドが呼ばれたら、Windowを作成する。
STDMETHOD(Select)(HWND hwndSip) { RECT rc = {0,0,240,80}; m_panel.Create(hwndSip,rc,_T(""),WS_VISIBLE | WS_CHILD); return S_OK; }
Deselectメソッドが呼ばれたらWindowを壊しときましょう。
STDMETHOD(Deselect)() { m_panel.DestroyWindow(); return S_OK; }
とこれで、表示はできるようになりましたが、、パネルをクリックしても何も起こらないの。
最後に入力イベントの実装を行う。 SIPに情報を送るには、RegisterCallbackメソッドを通じて送られてくるIIMCallbackを使用する。 CIMPanelにIIMCallback型のメンバ変数を追加して、RegisterCallbackで送られてきたポインタを保存する。
STDMETHOD(RegisterCallback)(IIMCallback * lpIMCallback) { m_panel.m_pCallback = lpIMCallback; return S_OK; }CIMPanelの実装はこのようになります。
#pragma once #includeclass CIMPanel : public CWindowImpl { BEGIN_MSG_MAP(CIMPanel) MESSAGE_HANDLER(WM_PAINT,OnPaint) MESSAGE_HANDLER(WM_LBUTTONDOWN,OnLButtonDown) END_MSG_MAP() public: IIMCallback* m_pCallback; private: LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { PAINTSTRUCT ps; HDC hDC = BeginPaint(&ps); RECT rc = {0,0,240,80}; FillRect(hDC,&rc,(HBRUSH)GetStockObject(WHITE_BRUSH)); POINT pt[] = {{0,0},{239,0}}; Polyline(hDC,pt,2); SetTextColor(hDC,RGB(255,0,0)); rc.top = 1; DrawText(hDC,L"A",1,&rc,DT_CENTER | DT_VCENTER); EndPaint(&ps); return 0; } LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { m_pCallback->SendString(_T("A"),1); return 0; } };
これで完成。