//========================================================================
//TITLE:
// 漫谈WinCE输入法的编写(一)
//AUTHOR:
// norains
//DATE:
// Saturday 10-February -2007
//Environment:
// EVC4.0 + Standard SDK
//========================================================================
WinCE的输入法编写,说复杂也不复杂,说简单亦不简单.说复杂嘛,是因为输入法是一个COM,需要实现某些接口,并且由于其特殊性,不能像普通应用程序一样进行调试;说简单嘛,只要把层次搞清,剩下的就和普通应用程序没什么太多的区别.
我觉得如果光谈概念,以我的文字功底,让人明白内中的机理,估计不是一件非常容易的事情.所以,我还是实际点,以我写过的一个输入法来做为例子.
因为这篇文章不是专门介绍COM接口技术的原理,我们的目的仅仅是让输入法给跑起来,因此我们只需要了解输入法用到了什么接口,以及我们应该如何定义该接口的功能即可.
为了代码层次分明,我这个例子采用了三个类:CClassFactory,CInputMethod和CIMWnd.前面两个类分别继承于IClassFactory和IInputMethod,是能让系统正常调用输入法所必须的com接口;而CIMWnd则是输入法的窗口,和普通的应用程序无异.
这三个类的关系是CClassFactory通过自身的CreateInstance()函数来创建一个CInputMethod类的对象,然后CInputMethod类中会调用CIMWnd类来显示一个输入法窗口.以图片的形式来说,三者的关系如下:
现在就让我们来看看CClassFactory类的声明: class CClassFactory : public IClassFactory
...{
public:
CClassFactory(long *plDllCnt,HINSTANCE hInst);
virtual ~CClassFactory();
//IUnknown methods
STDMETHODIMP QueryInterface (THIS_ REFIID riid, LPVOID *ppv);
STDMETHODIMP_(ULONG) AddRef (THIS);
STDMETHODIMP_(ULONG) Release (THIS);
//IClassFactory methods
STDMETHODIMP CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid,LPVOID *ppv);
STDMETHODIMP LockServer (BOOL fLock);
private:
long *m_plDllCnt;//point to the Global DLL reference count
long m_lRef;
HINSTANCE m_hInst;
};
IUnknown标准接口:
QueryInterface:该函数用来查看对象支持哪种方法,这也是COM接口最先调用的函数.
AddRef:增加一次对象调用计数.
Release:减少一次对象调用计数.
IClassFactory特有的接口:
CreateInstance:系统调用该函数去创建另一个所需的实例.本文的例子中,这个函数体内创建了一个CInputMethod类的对象.
LockServer:用来标识该DLL是否被卸载.本文中,只有检测到m_plDllCnt变量为0值,也就是说该DLL已经没有被引用,此时管理器才会卸载该DLL.
接下来就让我们再来看看CInputMethod类: class CInputMethod : public IInputMethod
...{
public:
//IUnknown methods
STDMETHODIMP_(ULONG) Release(THIS);
STDMETHODIMP_(ULONG) AddRef(THIS);
STDMETHODIMP QueryInterface(THIS_ REFIID riid, LPVOID *ppv);
//IInputMethod
HRESULT STDMETHODCALLTYPE SetImData (DWORD dwSize, void *pvImData);
HRESULT STDMETHODCALLTYPE GetImData (DWORD dwSize, void *pvImData);
HRESULT STDMETHODCALLTYPE RegisterCallback(IIMCallback *pIMCallback);
HRESULT STDMETHODCALLTYPE ReceiveSipInfo(SIPINFO *psi);
HRESULT STDMETHODCALLTYPE GetInfo(IMINFO *pimi);
HRESULT STDMETHODCALLTYPE Hiding();
HRESULT STDMETHODCALLTYPE Showing();
HRESULT STDMETHODCALLTYPE Deselect();
HRESULT STDMETHODCALLTYPE Select(HWND hWndSip);
HRESULT STDMETHODCALLTYPE UserOptionsDlg (HWND hwndParent);
CInputMethod(long *plDllCnt,HINSTANCE hInst);
virtual ~CInputMethod();
protected:
CIMWnd *m_pIMWnd; //The input method window pointer
HINSTANCE m_hInst; //The dll instance
long *m_plDllCnt;//point to the Global DLL reference count
long m_lRef;
};
CInputMethod的IUnknown标准接口的作用和CClassFactory相同,在此就不赘述.
IInputMethod特有接口:
SetImData:应用程序可以通过该接口来获取输入法(IM)的信息.
GetImData:传递特定的信息给输入法.
RegisterCallback:SIP提供给输入法的回调函数接口.通过该回调函数接口,输入法可以给调用输入法的应用程序发送字符或键盘信息等等.这个是比较重要的一个函数,输入法就是通过该函数传递过来的接口函数来建立和外部的联系.
ReceiveSipInfo:SIP通过该接口传递消息给输入法.
GetInfo:SIP通过该接口来获取输入法的信息.
Hiding:隐藏输入法.
Showing:显示输入法.
Deselect:输入法即将从内存中卸载的时候将调用该接口.
Select:输入法装载到内存之前调用该接口.最明显的用户感受是在输入法管理器中选择某种输入法时,该接口即被调用.
UserOptionsDlg:控制面板通过该接口用来设置输入法的属性.
//========================================================================
//TITLE:
// 漫谈WinCE输入法的编写(二)
//AUTHOR:
// norains
//DATE:
// Monday 11-February -2007
//Environment:
// EVC4.0 + Standard SDK
//========================================================================
在上一节中我们了解了CClassFactory和CInputMethod的基本架构功能,现在就让我们来看看具体是如何实现的吧.
首先是CClassFactory的实现:
CClassFactory::CClassFactory(long *plDllCnt,HINSTANCE hInst)
...{
//初始化设置为1次
m_lRef = 1;
//Dll的引用次数,该指针指向的是一个外部的变量
m_plDllCnt = plDllCnt;
//保存句柄
m_hInst = hInst;
}
CClassFactory::~CClassFactory()
...{
}
//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, LPVOID *ppv)
...{
//返回IUnknown或IClassFactory对象
if (IsEqualIID (riid, IID_IUnknown) || IsEqualIID (riid, IID_IClassFactory))
...{
//返回指向对象的指针
*ppv = (LPVOID)this;
//增加计数,以避免返回的时候卸载该对象.
AddRef(); // Increment ref to prevent delete on return.
//情况正常,成功返回
return NOERROR;
}
*ppv = NULL;
//但接口不是调用者所需的,则失败返回.
return (E_NOINTERFACE);
}
//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP_(ULONG) CClassFactory::AddRef()
...{
ULONG cnt;
cnt = (ULONG)InterlockedIncrement (&m_lRef);
return cnt;
}
//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP_(ULONG) CClassFactory::Release()
...{
ULONG cnt;
cnt = (ULONG)InterlockedDecrement (&m_lRef);
if (cnt == 0)
...{
delete this;
}
return cnt;
}
//---------------------------------------------------------------------
//Description:
// CreateInstance - Called to have class factory object create other objects
//----------------------------------------------------------------------
STDMETHODIMP CClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppv)
...{
//创建一个指向CInputMethod对象的指针.
CInputMethod *pInputMethod;
HRESULT hr;
if (pUnkOuter)
...{
return (CLASS_E_NOAGGREGATION);
}
if (IsEqualIID (riid, IID_IUnknown) || IsEqualIID (riid, IID_IInputMethod) || IsEqualIID (riid, IID_IInputMethod2))
...{
// 创建一个输入法对象
pInputMethod = new CInputMethod(m_plDllCnt,m_hInst);
if (!pInputMethod)
...{
//内存分配失败
return E_OUTOFMEMORY;
}
//查看该输入法对象的接口是否支持是我们所需要的
hr = pInputMethod->QueryInterface (riid, ppv);
//如果不是我们所需的接口方法,那么下面这个函数将会删除刚刚创建的对象
pInputMethod->Release ();
return hr;
}
return E_NOINTERFACE;
}
//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP CClassFactory::LockServer(BOOL fLock)
...{
if (fLock)
...{
InterlockedIncrement (m_plDllCnt);
}
else
...{
InterlockedDecrement (m_plDllCnt);
}
return NOERROR;
}
然后我们来看看CInputMethod类的实现
CInputMethod::CInputMethod(long *plDllCnt, HINSTANCE hInst)
...{
//获取输入法窗口的实例
m_pIMWnd = CIMWnd::GetInstance();
m_hInst = hInst;
m_plDllCnt = plDllCnt;
//增加一次计数
(*m_plDllCnt)++;
m_lRef = 1; // Set ref count to 1 on create.
}
CInputMethod::~CInputMethod()
...{
//销毁时减少一次计数
(*m_plDllCnt)--;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to create the windows and image list for the input method (IM).
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::Select(HWND hWndSip)
...{
//初始化输入法界面窗口
if(m_pIMWnd->Initialize(m_hInst,hWndSip) == FALSE)
...{
return E_FAIL;
}
return S_OK;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to select the input method (IM) out of the software-based
//input panel window and to destroy the IM windows.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::Deselect()
...{
//销毁输入法界面窗口
m_pIMWnd->DestroyWindow();
return S_OK;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to perform any initialization before the software-based
//input panel window is displayed
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::Showing()
...{
//显示输入法界面窗口
m_pIMWnd->ShowWindow(TRUE);
return S_OK;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to perform any saving routines before the software-based
//input panel is hidden.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::Hiding()
...{
//隐藏输入法界面窗口
m_pIMWnd->ShowWindow(FALSE);
return S_OK;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to return information about the current input
//method (IM) to the operating system.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::GetInfo(IMINFO *pimi)
...{
pimi->cbSize = sizeof (IMINFO);
pimi->hImageNarrow = 0;
pimi->hImageWide = 0;
pimi->iNarrow = 0;
pimi->iWide = 0;
pimi->fdwFlags = SIPF_DOCKED;
pimi->rcSipRect.left = 0;
pimi->rcSipRect.top = 0;
pimi->rcSipRect.right = SIP_WND_WIDTH;
pimi->rcSipRect.bottom = SIP_WND_HEIGHT;
return S_OK;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented for the IM to receive information about the size,
//placement, and docked status of the software-based input panel.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::ReceiveSipInfo(SIPINFO *psi)
...{
return S_OK;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to receive a pointer to an IIMCallback interface.
//An input method (IM) uses the IIMCallback interface to send keystrokes to applications
//and to change the icons on the Input Panel button.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::RegisterCallback(IIMCallback *pIMCallback)
...{
//发送一条自定义消息传递回调函数的地址给输入法界面窗口
HWND hWnd = m_pIMWnd->GetWindow();
SendMessage(hWnd,MYMSG_REGCALLBACK,(WPARAM)pIMCallback,0);
return S_OK;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to send data from the current
//input method (IM) to the current application.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::GetImData(DWORD dwSize, void *pvImData)
...{
return S_OK;
}
//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to respond to an application's request to
//set input method (IM)-specific data within the IM.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::SetImData(DWORD dwSize, void *pvImData)
...{
return S_OK;
}
//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP CInputMethod::QueryInterface(REFIID riid, LPVOID *ppv)
...{
//如果当前的接口符合要求,则返回一个接口对象
if (IsEqualIID (riid, IID_IUnknown) || IsEqualIID (riid, IID_IInputMethod) || IsEqualIID (riid, IID_IInputMethod2))
...{
*ppv = (IInputMethod *)this;
AddRef();
return NOERROR;
}
*ppv = NULL;
return (E_NOINTERFACE);
}
//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP_(ULONG) CInputMethod::AddRef()
...{
ULONG cnt;
cnt = (ULONG)InterlockedIncrement (&m_lRef);
return cnt;
}
//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP_(ULONG) CInputMethod::Release()
...{
ULONG cnt;
cnt = (ULONG)InterlockedDecrement (&m_lRef);
if (cnt == 0)
...{
delete this;
return 0;
}
return cnt;
}
//---------------------------------------------------------------------
//Description:
// The SIP Control Panel applet is asking for a configuration dialog box to be displayed.
//----------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::UserOptionsDlg(HWND hwndParent)
...{
//显示设置窗口
m_pIMWnd->ShowUserOptionsDlg(hwndParent,m_hInst);
return S_OK;
}
//========================================================================
//TITLE:
// 漫谈WinCE输入法的编写(三)
//AUTHOR:
// norains
//DATE:
// Friday 2-March -2007
//Environment:
// EVC4.0 + Standard SDK
//========================================================================
这次就让我们来看看一个简单的输入法界面窗口的实现.如果以类的观点来看,输入法界面窗口和普通的应用程序的窗口没有什么不同,可能也是唯一最大的差异就是输入法窗口不能也不应该发送PostQuiteMessage,否则将导致输入法管理器的退出.
闲话不表,直接来看看一个最简单的输入法窗口架构: //**************************************************************
//IMWnd.h
//*************************************************************
//The input method window class
class CIMWnd
...{
public:
//获取对象实例
static CIMWnd * GetInstance();
//显示输入法的设置窗口
void ShowUserOptionsDlg(HWND hWndParent,HINSTANCE hInst = NULL);
//显示输入法界面
void ShowWindow(BOOL bShow);
//销毁输入法界面
void DestroyWindow();
//初始化窗口,hWndSip是输入法管理器的句柄
BOOL Initialize(HINSTANCE hInst, HWND hWndSip);
//析构函数
virtual ~CIMWnd();
protected:
//构造函数
CIMWnd();
//注册回调函数
void OnRegCallback(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
//窗口过程
static LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
//指向对象的指针
static CIMWnd *m_pInstance;
//输入法窗口句柄
HWND m_hWnd;
//DLL实例
HINSTANCE m_hInst;
//输入法管理器窗口句柄
HWND m_hWndSip;
//保存对外回调函数的指针
IIMCallback *m_pIMCallback; //Pointer to the callback function
};
//**************************************************************
//IMWnd.cpp
//*************************************************************
//Initialize
CIMWnd *CIMWnd::m_pInstance = NULL;
//----------------------------------------------------------------------
//Description:
// Get the object instance
//----------------------------------------------------------------------
CIMWnd * CIMWnd::GetInstance()
...{
if(m_pInstance == NULL)
...{
m_pInstance = new CIMWnd();
}
return m_pInstance;
}
//----------------------------------------------------------------------------
//Decription:
// On message MSG_USEROPTIONSDLG
//-----------------------------------------------------------------------------
void CIMWnd::ShowUserOptionsDlg(HWND hWndParent,HINSTANCE hInst)
...{
CSettingDlg *pSettingDlg;
pSettingDlg = CSettingDlg::GetInstance();
if(hInst == NULL)
...{
pSettingDlg->ShowDlg(m_hInst,hWndParent,&m_iTimeFresh);
}
else
...{
pSettingDlg->ShowDlg(hInst,hWndParent,&m_iTimeFresh);
}
}
//----------------------------------------------------------------------
//Description:
// Show the window
//----------------------------------------------------------------------
void CIMWnd::ShowWindow(BOOL bShow)
...{
if(bShow == TRUE)
...{
::ShowWindow(m_hWnd,SW_SHOW);
}
else
...{
::ShowWindow(m_hWnd,SW_HIDE);
}
}
//----------------------------------------------------------------------
//Description:
// Destroy the window
//----------------------------------------------------------------------
void CIMWnd::DestroyWindow()
...{
::DestroyWindow(m_hWnd);
UnregisterClass(WINDOW_CLASS,m_hInst);
}
//----------------------------------------------------------------------
//Description:
// Initialize
//----------------------------------------------------------------------
BOOL CIMWnd::Initialize(HINSTANCE hInst, HWND hWndSip)
...{
m_hInst = hInst;
m_hWndSip = hWndSip;
WNDCLASS wc;
wc.style = CS_VREDRAW | CS_HREDRAW;;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = WINDOW_CLASS;
if(RegisterClass(&wc) == 0)
...{
return FALSE;
}
// Create SIP window.
m_hWnd = CreateWindowEx(0,
WINDOW_CLASS,
WINDOW_TITLE,
WS_CHILD | WS_BORDER ,
CW_USERDEFAULT,
CW_USERDEFAULT,
CW_USERDEFAULT,
CW_USERDEFAULT,
hWndSip,
NULL,
m_hInst,
NULL
);
if(IsWindow(m_hWnd)==FALSE)
...{
return FALSE;
}
return TRUE;
}
CIMWnd::~CIMWnd()
...{
DeleteCriticalSection(&m_CriticalForWaitFresh);
if(m_pInstance != NULL)
...{
delete m_pInstance;
m_pInstance = NULL;
}
}
CIMWnd::CIMWnd()
...{
m_pIMCallback = NULL;
m_hWnd = NULL;
m_hInst = NULL;
m_hWndSip = NULL;
}
//----------------------------------------------------------------------------
//Decription:
// On message MYMSG_REGCALLBACK
//-----------------------------------------------------------------------------
void CIMWnd::OnRegCallback(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
...{
m_pIMCallback = (IIMCallback *)wParam;
}
//----------------------------------------------------------------------
//Description:
// Window process
//----------------------------------------------------------------------
LRESULT CIMWnd::WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
...{
switch(wMsg)
...{
...
case MYMSG_REGCALLBACK:
m_pInstance->OnRegCallback(hWnd,wMsg,wParam,lParam);
return 0;
...
}
return DefWindowProc(hWnd,wMsg,wParam,lParam);
}
这里需要注意的是m_pIMCallback指针,它保存的是输入法管理器传回来的函数地址,我们可以通过这个回调函数来像外发送字符.例如:m_pIMCallback->SendString(TEXT(","),1);
MYMSG_REGCALLBACK是自定义的消息,用来通知输入法窗口来保存函数地址.该消息的发送是在CInputMethod类的RegisterCallback()函数中.
一个简单的输入法架构基本上已经完成,在下一章中将介绍一些输入法的调试技巧和需要注意的问题.
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/norains/archive/2007/03/02/1519663.aspx