现在的位置: 首页 > 综合 > 正文

漫谈WinCE输入法

2013年08月13日 ⁄ 综合 ⁄ 共 20042字 ⁄ 字号 评论关闭

//========================================================================
//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

抱歉!评论已关闭.