本來這個blog是記錄開發輸入法的點滴的,後來越來越雜,現在什麼都記錄了。

2008年10月24日 星期五

微軟公佈的 TSF 輸入法例子的問題

微軟公佈的 TSF 輸入法例子的問題

在之前版本的歪林輸入法,其中一個問題就是當在某程式打字時,切換去另一個程式,歪林輸入法的視窗不會自動隱藏。

其原因是歪林輸入法是基於微軟的"Sample Code for Text Services Framework",裡面沒有將視窗自動隱藏的部分。

後來在網友zhangruisen 的電郵中,得知只要在程式碼中,添加IID_ITfThreadFocusSink事件接收器,然后在相應的函數里(即STDAPI CTextService::OnSetThreadFocus() 和 STDAPI CTextService::OnKillThreadFocus() )顯示/隱藏窗口即可。在 Windows SDK 的 Samples\WinUI\TSFcase 有範例程式碼。

得此訊息後,就在歪林輸入法加了:

  BOOL
  CTextService::_InitThreadFocusSink() {
      HRESULT hr;
      ITfSource *pSource = NULL;
      if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) != S_OK)
          return FALSE;
      hr = pSource->AdviseSink(IID_ITfThreadFocusSink, (ITfThreadFocusSink *)this,
&_dwThreadFocusSinkCookie);
      pSource->Release();
      return (hr == S_OK);
  }

  // _UninitThreadFocusSink
  // Unadvise a Focus sink.  Assumes a sink has been advised already.
  void
  CTextService::_UninitThreadFocusSink() {
      ITfSource *pSource;
      if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) != S_OK)
          return;
      pSource->UnadviseSink(_dwThreadFocusSinkCookie);
      pSource->Release();
  }

  STDAPI
  CTextService::OnSetThreadFocus() {
      if (_pCandidateWin)
          _pCandidateWin->Show(TRUE);
      return S_OK;
  }

  STDAPI
  CTextService::OnKillThreadFocus()
  {
      if (_pCandidateWin)
          _pCandidateWin->Show(FALSE);
      return S_OK;
  }

在加入上面程式碼後,以為在 CTextService::Activate() 裡面呼叫 CTextService::_InitThreadFocusSink() 就可以了。怎料,每次呼叫這函數,都 返回 S_FAILED 值。這問題一直困擾我很久,超越大半年時間。(也是因為懶惰)

直到昨天晚上下班後,又提起精神,再仔細比較 Windows SDK TSF Case 和我 歪林輸入法程式碼的分別。終於找到了!原來:

STDAPI
CTextService::QueryInterface(REFIID riid, void **ppvObj) {
    if (ppvObj == NULL)
        return E_INVALIDARG;

    *ppvObj = NULL;

    if (IsEqualIID(riid, IID_IUnknown) ||
        IsEqualIID(riid, IID_ITfTextInputProcessor))
    {
        *ppvObj = (ITfTextInputProcessor *)this;
    } else if (IsEqualIID(riid, IID_ITfThreadMgrEventSink)) {
        *ppvObj = (ITfThreadMgrEventSink *)this;
    }
     else if (IsEqualIID(riid, IID_ITfThreadFocusSink)) {
        *ppvObj = (ITfThreadFocusSink *)this;
    }

else if (IsEqualIID(riid, IID_ITfTextEditSink)) {
        *ppvObj = (ITfTextEditSink *)this;
    } else if (IsEqualIID(riid, IID_ITfKeyEventSink)) {
        *ppvObj = (ITfKeyEventSink *)this;
    } else if (IsEqualIID(riid, IID_ITfCompositionSink)) {
        *ppvObj = (ITfKeyEventSink *)this;
    } else if (IsEqualIID(riid, IID_ITfDisplayAttributeProvider)) {
        *ppvObj = (ITfDisplayAttributeProvider *)this;
    }

    if (*ppvObj) {
        AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}
 

Windows SDK 的 TSF Case 有「框」的部分,而歪林輸入法和其他 TSF Sample code 就沒有。一加了上面的 code,歪林輸入法就可以在切換視窗時,自動隱蔽起來。

天啊!一耽誤就是半年!

沒有留言: