Text Services Framework

since 2010-03-01

nvdajp pyaa msaa python_windows windows 等に関する調査。

情報収集ふたたび

since 2013-05-22

情報収集したのでメモしておく。

http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms629013%28v=vs.85%29.aspx

GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE

http://msdn.microsoft.com/ja-jp/library/windows/desktop/aa380396%28v=vs.85%29.aspx

TF_SENTENCEMODE_NONE 	0x0000	No information for sentence.
TF_SENTENCEMODE_PLAURALCLAUSE 	0x0001	The IME uses plural clause information to carry out conversion processing.
TF_SENTENCEMODE_SINGLECONVERT 	0x0002	The IME carries out conversion processing in single-character mode.
TF_SENTENCEMODE_AUTOMATIC 	0x0004	The IME carries out conversion processing in automatic mode.
TF_SENTENCEMODE_PHRASEPREDICT 	0x0008	The IME uses phrase information to predict the next character.
TF_SENTENCEMODE_CONVERSATION 	0x0010	The IME uses conversation mode. This is useful for chat applications.
This is equivalent with IME_SMODE values for IMM32

http://blogs.msdn.com/b/tsfaware/archive/2007/05/30/what-is-a-keyboard.aspx

ブックマーク

Windows Input Method の歴史 http://d.hatena.ne.jp/NyaRuRu/20070309/p1

IME (Input Method Editor) と Text Services Framework のアクセシビリティ

    • readcomp というデモプログラムが入手できる。rcomplib 側の tsf.cpp, tsf.h が参考になりそう。ATOKだとIMMの情報は得られるがTSFの情報は得られない、といったことも確認できる。
    • readcomp は WinEvent の取得処理は含んでいない。

TSF (コードネーム Cicero)

IMM32 : TSFよりも古い時代のAPIで、Vista からは TSF に本格移行(??)

SDK の定義

C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include にあるもの
msctf.h
msctf.idl

Pythonで叩くには

調査中

from comtypes.client import GetModule
GetModule("shdocvw.dll")
GetModule(["{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 1, 1])

のようなことができるらしいが、msctf.dll を GetModule するのはうまくいかなかったので、気持ちを切り替える。

nvda の generate.py が typelibs の中身から comInterfaces の中身を作っている。

http://www.python.jp/doc/contrib/ctypes/sum_sample_jp.html

によれば

midl コンパイラを midl sum.idl /tlb sum.tlb として動作させると, 型ライブラリ sum.tlb が生成されます.

となっている。midl は Visual Studio 2008 に入っている。

が、midl の実行で躓く。msctf.idl を SDK のディレクトリからコピーしてきて midl msctf.idl /tlb mctf.tlb を実行したが何も出力されない。

aa366839(VS.85).aspx

COMプログラミング : IDL 超入門「タイプライブラリは IDL をバイナリトークンにしたもの」

NVDA の generate.py を真似る

generate.py をコピーして改変する。

#a test code of TSF (based on NVDA code) by nishimotz
#
#generate.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/>
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
 
import comtypes.client
comtypes.client.gen_dir='.\\comInterfaces'
import comtypes
import sys
sys.modules['comtypes.gen']=comtypes.gen=__import__("comInterfaces",globals(),locals(),[])
 
import os
import sys
from glob import glob
 
COM_INTERFACES = (
	("sum", comtypes.client.GetModule, "sum.tlb"),
)
 
def main():
	print "COM interfaces:"
	for desc, func, interface in COM_INTERFACES:
		print "%s:" % desc,
		try:
			func(interface)
			print "done."
		except:
			print "not found."
	print
 
if __name__ == "__main__":
	main()
	from comInterfaces.SumLib import _1F2B08DF_E59C_4F10_98D9_16078342F74C_0_1_0
	o = _1F2B08DF_E59C_4F10_98D9_16078342F74C_0_1_0.ITfThreadMgr() 
	print "ITfThreadMgr:"
	print dir(o)
	p = _1F2B08DF_E59C_4F10_98D9_16078342F74C_0_1_0.ITfThreadMgrEventSink()
	print "ITfThreadMgrEventSink:"
	print dir(p)

comInterfaces ディレクトリを作り、その中に init.py という名前の空ファイルを作る。 (nvdaのソースの真似)

ここにある sum.idl を改変する。

/* http://www.python.jp/doc/contrib/ctypes/sum_sample_jp.html */
/* A TypeLibrary, compiled to sum.tlb */

import "oaidl.idl";
import "comcat.idl";
import "textstor.idl";
import "ctfutb.idl";

[
	uuid(1F2B08DF-E59C-4f10-98D9-16078342F74C), // generated by nishimotz
	version(1.0),
	helpstring("Sum 1.0 Type Library")
]
library SumLib
{
	importlib("stdole2.tlb");
	[
	  object,
	  uuid(aa80e801-2021-11d2-93e0-0060b067b86e),
	  pointer_default(unique)
	]
	interface ITfThreadMgr;
	
	[
	  object,
	  uuid(aa80e80e-2021-11d2-93e0-0060b067b86e),
	  pointer_default(unique)
	]
	interface ITfThreadMgrEventSink;
};

どうやら上記の4つのincludeでインタフェースの定義は終わっていて、library 宣言だけやればよいらしい。

名前は sum のままだが guidgen を実行してタイプライブラリの uuid は作り直した。 Interface の uuid は msctf.idl からコピーした。

Visual Studio 2008 のコマンドプロンプトで下記を実行:

>midl sum.idl
Microsoft (R) 32b/64b MIDL Compiler Version 7.00.0500
Copyright (c) Microsoft Corporation 1991-2006. All rights reserved.
Processing .\sum.idl
sum.idl
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\oaidl.idl
oaidl.idl
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\objidl.idl
objidl.idl
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\unknwn.idl
unknwn.idl
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\wtypes.idl
wtypes.idl
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\basetsd.h
basetsd.h
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\guiddef.h
guiddef.h
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\comcat.idl
comcat.idl
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\textstor.idl
textstor.idl
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\ctfutb.idl
ctfutb.idl
Processing .\msctf.idl
msctf.idl
Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\oaidl.acf
oaidl.acf

>python generate.py
COM interfaces:
sum: # Generating comtypes.gen._1F2B08DF_E59C_4F10_98D9_16078342F74C_0_1_0
# Generating comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0
# Generating comtypes.gen.stdole
# Generating comtypes.gen.SumLib
done.

ITfThreadMgr:
['Activate', 'AddRef', 'AssociateFocus', 'CreateDocumentMgr', 'Deactivate', 'Enu
mDocumentMgrs', 'EnumFunctionProviders', 'GetFocus', 'GetFunctionProvider', 'Get
GlobalCompartment', 'IsThreadFocus', 'QueryInterface', 'Release', 'SetFocus', '_
AddRef', '_ITfThreadMgr__com_Activate', '_ITfThreadMgr__com_AssociateFocus', '_I
TfThreadMgr__com_CreateDocumentMgr', '_ITfThreadMgr__com_Deactivate', '_ITfThrea
dMgr__com_EnumDocumentMgrs', '_ITfThreadMgr__com_EnumFunctionProviders', '_ITfTh
readMgr__com_GetFocus', '_ITfThreadMgr__com_GetFunctionProvider', '_ITfThreadMgr
__com_GetGlobalCompartment', '_ITfThreadMgr__com_IsThreadFocus', '_ITfThreadMgr_
_com_SetFocus', '_IUnknown__com_AddRef', '_IUnknown__com_QueryInterface', '_IUnk
nown__com_Release', '_QueryInterface', '_Release', '__class__', '__delattr__', '
__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__',
'__map_case__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduc
e_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
 '__weakref__', '_case_insensitive_', '_idlflags_', '_iid_', '_methods_']
ITfThreadMgrEventSink:
['AddRef', 'OnInitDocumentMgr', 'OnPopContext', 'OnPushContext', 'OnSetFocus', '
OnUninitDocumentMgr', 'QueryInterface', 'Release', '_AddRef', '_ITfThreadMgrEven
tSink__com_OnInitDocumentMgr', '_ITfThreadMgrEventSink__com_OnPopContext', '_ITf
ThreadMgrEventSink__com_OnPushContext', '_ITfThreadMgrEventSink__com_OnSetFocus'
, '_ITfThreadMgrEventSink__com_OnUninitDocumentMgr', '_IUnknown__com_AddRef', '_
IUnknown__com_QueryInterface', '_IUnknown__com_Release', '_QueryInterface', '_Re
lease', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__geta
ttribute__', '__hash__', '__init__', '__map_case__', '__metaclass__', '__module_
_', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__size
of__', '__str__', '__subclasshook__', '__weakref__', '_case_insensitive_', '_idl
flags_', '_iid_', '_methods_']

メソッドの名前を拾えているということは、うまくいくかも。。。と思ったのだが。。。

ITfThreadMgrEventSink をどうやって登録すればいいのか分からない。

ReadCompを理解する

方針を変えて、ReadComp の tsf.{cpp,h} を眺めてみることにしたい。

CReadCompTSF::_AppendCompositionText() に文字列を取り出す処理がある。

ようするに ITfRange::GetText() メソッドがゴールだ。

readcomp ソリューション

ソリューション readcomp は2つのプロジェクト rcomplib (Win32 DLL) と readcomp (Win32 EXE) から構成される。

VC++2008 でビルドするときに static 変数名 = 値 という箇所がエラーになったので static int に修正。

実行するためには、両プロジェクトの Release ディレクトリに作られる rcomplib.dll と readcomp.exe を同じディレクトリにコピー。

ReadComp プログラムを実行し、別途 notepad.exe を実行すると、IMM と TSF の情報が画面右下に表示される。

MS-IME 2002 の場合は IMM と TSF の両方で表示される。ATOK 2009 の場合は IMM しか表示されない。

readcomp を WinMain からさかのぼる

readcomp.cpp の WinMain() の中身:

InitInstance() は CreateWindow(), ShowWindow(), UpdateWindow() を呼び出す。いずれも Win32 API である。

InitApp() はやはり readcomp.cpp の中にあり、InitReadComp() をコールする。だがこの BOOL WINAPI InitReadComp() は rcomplib.cpp の中で実装されており、EXE から DLL の呼び出しになっている。

readcomp.cpp の先頭には #include "..\inc\rcomplib.h" という記述があり、この rcomplib.h に

BOOL WINAPI InitReadComp();
BOOL WINAPI UninitReadComp();

が宣言されている。

また readcomp プロジェクトのプロパティによると、リンカの引数に "..\dll\release\rcomplib.lib" がある。

DLL 側の処理

BOOL WINAPI InitReadComp() の重要な仕事は SetWindowsHookEx(WH_GETMESSAGE, …) を呼ぶこと。

http://msdn.microsoft.com/ja-jp/library/cc430103.aspx

HHOOK SetWindowsHookEx(
  int idHook,        // フックタイプ
  HOOKPROC lpfn,     // フックプロシージャ
  HINSTANCE hMod,    // アプリケーションインスタンスのハンドル
  DWORD dwThreadId   // スレッドの識別子
);

WH_GETMESSAGE については 「メッセージキューへポストされたメッセージを監視する 1 個のフックプロシージャをインストールします。詳細については、GetMsgProc フックプロシージャの説明を参照してください。」 とされている。

http://msdn.microsoft.com/ja-jp/library/cc429822.aspx

LRESULT CALLBACK GetMsgProc(
  int code,       // フックコード
  WPARAM wParam,  // 削除オプション
  LPARAM lParam   // メッセージ
);

フックプロシージャと ImmGet*** 呼び出し

rcomplib.cpp で SysGetMsgProc() というフックプロシージャが定義される。

細かいところを飛ばすと、さらに UINT _SysGetMsgProc(WPARAM wParam, LPARAM lParam) が呼ばれる。

    CReadCompTSF::Init();
    IncrementDllRefCount();
 
    switch (uMsg)
    {
        case WM_IME_STARTCOMPOSITION:
        case WM_IME_ENDCOMPOSITION:
        case WM_IME_COMPOSITION:
        case WM_KEYDOWN:
        case WM_KEYUP:
            UpdateIMMCompositionString();
            break;
        default:
            break;
    }

ということで、やっと CReadCompTSF が出てきたが、けっきょくフックは IME がらみのキーイベントを引っかけている。

UpdateIMMCompositionString() の中身を見る。

ここでは GetFocus() して ImmGetContext() して ImmGetCompositionStringW() している。

Imm系のAPIは時代遅れという話だったが、ATOK2009 は Imm 系にしか対応していない。

  • 2011-10-30 追記:ATOK2011 は TSF に対応している。

いずれにせよ TSF 系の処理は絡んでいない。TSF を使うだけならウィンドウメッセージのフックは不要? と一瞬思ったが、大事なことが CReadCompTSF::Init() にあるはずなので後でじっくり見る。

TLS:スレッドローカル記憶域

TLS というクラスが登場する。tls.h で static メソッドを実装している。 TlsAlloc() や TlsGetValue() などが呼ばれている。

以下のメンバ変数が TLS クラスにある。

    CReadCompTSF *_pReadCompTSF;
    CCompositionPopup *_pCompositionPopup;

http://msdn.microsoft.com/ja-jp/library/cc429388.aspx

TLS とはスレッドローカル記憶域のこと。関連APIは

ヘッダ : winbase.h で宣言されています。
インポートライブラリ : kernel32.lib とリンクします。

とのこと。

tls.cpp には BOOL TLS::InternalDestroyTLS() メソッドの定義がある。

TlsGetValue() の返り値を TLS* ptls に cast している。 そして ptls→_pReadCompTSF を Release() したり ptls→_pCompositionPopup を delete したり。。 最後に LocalFree(ptls) を実行。

TLSの解説(英語) http://support.microsoft.com/?scid=kb;en-us;94804&x=12&y=12

スレッドとTLSの参考資料 http://www7a.biglobe.ne.jp/~tsuneoka/win32tech/19.html

「スレッドはプロセスの仮想アドレス空間とグローバル変数を共有します. しかし,場合によっては各スレッドにローカルな静的記憶域があった方がよいことも あります」

「TLS(スレッドローカル記憶域)を利用して, プロセスの任意のスレッドがスレッドごとに異なる値を格納・取得できるようにする ことができます」

「(2) TLSインデックスを使わなければならないスレッドは,動的記憶域を割り当てて から,Win32 API TlsSetValueで,その記憶域を指すポインタとインデックスを 関連付ける」

「(3) スレッドは,記憶域をアクセスするとき,TLSインデックスを指定して Win32 API TlsGetValue を呼び出してポインタを取得する」

CCompositionPopup クラスは Imm 系APIで得た情報をウィンドウに表示するクラスと思われる。 IME側のスレッドからフックされて呼ばれるので readcomp 側のスレッドとの記憶域の分離が必要なのだろう。。

なお、最近の VC++ では __declspec(thread) という記述で TLS を簡単に扱えるらしい。

見落としていたことがあったので _SysGetMsgProc(WPARAM wParam, LPARAM lParam) に戻る。

CReadCompTSFの初期化

_SysGetMsgProc(WPARAM wParam, LPARAM lParam) で先に呼ばれていた CReadCompTSF::Init() を確認。VC++2008の右クリックメニューで追っていく。

tsf.h では static BOOL Init(); として宣言されている。

tsf.cpp に BOOL CReadCompTSF::Init() がある。

やはり最初に TLS::GetTLS() を呼んでいた。

返り値の _pReadCompTSF がもし NULL であれば ptls→_pReadCompTSF = new CReadCompTSF; を実行して初期化。

その直後にやっと出てくるのがテキストサービスのCOMインタフェースだ。TSFスレッドマネージャのオブジェクトを作成している。 生成されたオブジェクトのポインタを ptim で受け取る。

    ITfThreadMgr* ptim;
    HRESULT hr;
 
    hr = CoCreateInstance(CLSID_TF_ThreadMgr, 
                          NULL, 
                          CLSCTX_INPROC_SERVER, 
                          IID_ITfThreadMgr, 
                          (void**)&ptim);

http://yokohama.cool.ne.jp/chokuto/urawaza/api/CoCreateInstance.html

CoCreateInstance() により「指定されたCLSIDに関連付けられたクラスの1つの未初期化オブジェクトを作成」する。

うまくいったら _InitThreadMgrSink() が呼び出される。

    ptls->_pReadCompTSF->_InitThreadMgrSink();

中身は後で見る。

続き:こんどはドキュメントマネージャだ。スレッドマネージャのGetFocus()メソッドで取り出すらしい。

    ITfDocumentMgr *pDocMgr;
    if (SUCCEEDED(ptim->GetFocus(&pDocMgr)))
    {
        if (pDocMgr)
        {
           ptls->_pReadCompTSF->_InitTextEditSink(pDocMgr);
           pDocMgr->Release();
        }
    }

成功したら _InitTextEditSink() を実行。

_InitThreadMgrSink() と _InitTextEditSink() の中身が興味深い。

_InitThreadMgrSink

BOOL CReadCompTSF::_InitThreadMgrSink() を見る。

まず ThreadMgr に対して QueryInterface をかけて、ITfSource インタフェースを取得。

    ITfSource *pSource;
    BOOL fRet;
 
    if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) != S_OK)
        return FALSE;

MSDN の ms628941 によれば、下記に対して ITfSource の QueryInterface をかけることができ、それぞれ機能が違うと書かれている。

ITfThreadMgr
ITfContext
ITfCompartment
ITfInputProcessorProfiles
ITfLangBarItem

ここでは pSource は ITfThreadMgr の ITfSource であることを確認。

続きのコードは、pSource に対して AdviseSink を実行し、ITfThreadMgrEventSink として this を登録している。

    fRet = FALSE;
 
    if (pSource->AdviseSink(IID_ITfThreadMgrEventSink, (ITfThreadMgrEventSink *)this, &_dwThreadMgrEventSinkCookie) != S_OK)
    {
        // make sure we don't try to Unadvise _dwThreadMgrEventSinkCookie later
        _dwThreadMgrEventSinkCookie = TF_INVALID_COOKIE;
        goto Exit;
    }
 
    fRet = TRUE;
 
Exit:
    pSource->Release();
    return fRet;

念のため tsf.h を確認すると確かに CReadCompTSF は ITfThreadMgrEventSink を実装している:

class CReadCompTSF : public ITfThreadMgrEventSink, 
                     public ITfTextEditSink

ms628945 によると最後の引数は

  • Address of a DWORD value that receives an identifying cookie. This value is used to uninstall the advise sink in a subsequent call to ITfSource::UnadviseSink. Receives (DWORD)-1 if a failure occurs.

であり、コールバックを解除するときに必要な情報の格納場所。

pSource は AdviceSink だけのために使われる。終わったら IUnknown のメソッド Release() を呼んでいる。

TF_INVALID_COOKIE は SDK の msctf.h で定義されている。

  • 2011-10-30 追記:TF_INVALID_COOKIE は ReadComp の中で「初期化に失敗したことを表す」フラグである可能性。

まとめると、スレッドマネージャの Source インタフェースに ITfThreadMgrEventSink として CReadCompTSF のインスタンスを登録。

ms628973 で確認。これで CReadCompTSF の以下のメンバ関数が呼ばれるようになるわけだ。

  • OnInitDocumentMgr :
    • Called when the first context is added to the context stack
    • Sink called by the framework just before the first context is pushed onto a document.
  • OnUninitDocumentMgr :
    • Called when the last context is removed from the context stack
    • Sink called by the framework just after the last context is popped off a document.
  • OnSetFocus :
    • Called when a document view receives or loses the focus
    • Sink called by the framework when focus changes from one document to another. Either document may be NULL, meaning previously there was no focus document, or now no document holds the input focus.
    • この呼び出しを使ってテキストを追跡する。
    • 最後に OnSetFocus(NULL, ..) が呼ばれるので、そこでリソース解放処理をすればよい。
    • TextEditSink の登録を行うなど。
  • OnPushContext :
    • Called when a context is added to the context stack
    • Sink called by the framework when a context is pushed.
  • OnPopContext :
    • Called when a context is removed from the context stack
    • Sink called by the framework when a context is popped.

OnSetFocus

STDAPI CReadCompTSF::OnSetFocus(ITfDocumentMgr *pDocMgrFocus, ITfDocumentMgr *pDocMgrPrevFocus)

を見ておく。

if (GetSharedMemory()→fReadCompRunning) の場合に下記を実行。

        _InitTextEditSink(pDocMgrFocus);

けっきょく CReadCompTSF の初期化時に加えて ITfThreadMgrEventSink が OnSetFocus を受け取ったときにも、後述の _InitTextEditSink が呼ばれる。

ついでにGetSharedMemory()について見ると globals.h で以下の記述。

extern CReadCompSharedMem g_SharedMemory;
 
inline SHAREDMEM *GetSharedMemory() { return g_SharedMemory.GetPtr(); }
inline void FlushSharedMemory() { g_SharedMemory.Flush(0); }

このクラスは CReadCompFileMapping がスーパークラスであり filemap.h で実装されている。 OpenFileMapping という API が使われている。 cc430187 に説明があるが。。。

http://wisdom.sakura.ne.jp/system/winapi/win32/win150.html

によると「プロセス間データ共有」とのこと。

ここでは TEXT("ReadCompSharedMem") という名前の共有メモリを作成して、 下記の構造体のポインタを GetSharedMemory() が返す、と理解しておく。

typedef struct
{
    HHOOK hSysGetMsgHook;
    BOOL  fReadCompRunning;
} SHAREDMEM;

_InitTextEditSink

BOOL CReadCompTSF::_InitTextEditSink(ITfDocumentMgr *pDocMgr) を見る。

  • Init a text edit sink on the topmost context of the document. Always release any previous sink.

引数は ITfThreadMgr の GetFocus() で得たオブジェクトである。

最初に

  // clear out any previous sink first

につづく行がある。2回目以降に呼ばれるときの処理。

_dwTextEditSinkCookie != TF_INVALID_COOKIE の場合に _pTextEditSinkContext を使って ITfSource に UnadviseSink をかける。

引数 pDocMgr == NULL のときは、Unadvice だけ行って終了。 caller just wanted to clear the previous sink

1回目から必ず実行されるのはその下。ThreadMgrEventSink と同様だ。

  • setup a new sink advised to the topmost context of the document

pDocMgr→GetTop(&_pTextEditSinkContext) で NULL が返ったら抜ける:

  • empty document, no sink possible

最後に pSource→AdviseSink する:

    if (_pTextEditSinkContext->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK)
    {
        if (pSource->AdviseSink(IID_ITfTextEditSink, (ITfTextEditSink *)this, &_dwTextEditSinkCookie) == S_OK)
        {
            fRet = TRUE;
        }
        else
        {
            _dwTextEditSinkCookie = TF_INVALID_COOKIE;
        }
        pSource->Release();
    }

OnEndEdit から GetText まで

ITfTextEditSink は ms628962 に情報。

メソッドは1つだけだ。

  • OnEndEdit :
    • Receives a notification upon completion of an ITfEditSession::DoEditSession method that has read/write access to the context.
    • Called by the system whenever anyone releases a write-access document lock.

STDAPI CReadCompTSF::OnEndEdit(ITfContext *pContext, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord) をみる。

    FlushSharedMemory();
    if (GetSharedMemory()->fReadCompRunning)
        _CheckComposition(pContext, ecReadOnly);
  • if we get here, only property values changed とされている。

_CheckComposition

BOOL CReadCompTSF::_CheckComposition(ITfContext *pContext, TfEditCookie ecReadOnly) をさらに見る。

    _ClearCompositionText();
 
    hr = pContext->QueryInterface(IID_ITfContextComposition,
                                  (void **)&pContextComposition);

ここで ITfContext に QueryInterface して ITfContextComposition を取り出す。

ITfContextComposition の EnumCompositions で EnumCompositionView を取り出す。

EnumCompositionView の Next で ITfCompositionView を取り出す。

ITfCompositionView の GetRange で ITfRange を取り出す。

_AppendCompositionText() に ITfRange を渡す。

_AppendCompositionText

void CReadCompTSF::_AppendCompositionText(ITfRange *pRange, TfEditCookie ecReadOnly) も見てみる。

pRange->GetText(ecReadOnly, TF_TF_MOVESTART, wstr, ulcch, &ulcch);

これでやっと入力文字列が取り出せたことになる。

OnEndEdit() で受け取った ecReadOnly が最後まで使われていることになる。

ITfRange は ms628908 に情報。

TSF情報の表示

BOOL CReadCompTSF::_CheckComposition(ITfContext *pContext, TfEditCookie ecReadOnly)

の最後に void CCompositionPopup::SetTSFCompositionString(WCHAR *psz) の呼び出しが出てくる。

ptls->_pCompositionPopup->SetTSFCompositionString(_pszCompositionText);

中では _pszTSFComposition にコピーするだけだ。

表示は

void CCompositionPopup::OnPaint(HWND hwnd, HDC hdc)

にて

TextOutW(hdc, 50, 1, _pszTSFComposition, lstrlenW(_pszTSFComposition));

によって実行される。

OnPaint() の呼び出しは

LRESULT CALLBACK CCompositionPopup::_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

の中の case WM_PAINT: の処理である。Win32 のイベント処理がクラス化されている。

text_services_framework.txt · 最終更新: 2013/05/22 18:23 by Takuya Nishimoto
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0