『转载』微软新一代输入法框架 TSF - Text Service Framework 小小的研究

原文转载自: https://blog.csdn.net/puncha/article/details/13293665

 

虽说是转载的,但是其中,有很多我自己的评论,我会用红色的字标出来,参考的博文有:

 

TSF架构:http://blog.csdn.net/mspinyin/article/details/6137709

TSF代码实例:http://www.dotblogs.com.tw/code6421/archive/2010/09/27/17909.aspx

TSF的一个C# Wrapper库:http://social.technet.microsoft.com/Forums/office/zh-CN/002efcfc-8d21-4674-b93b-53c8424d448e/vista-api-immgetdescription?forum=2087

 

下面内容来自第一篇被引用的文章,TSF架构:

 

几个关于TSF的术语

 

TIP (Text Input Processor), a Text service in TSF

Cicero,TSF的开发代号,所以微软内部通常称呼TSF框架为Cicero

CUAS (Cicero Unaware Application Support),为所有应用程序和控件提供基本的TIP支持

AIMM(Active Input Method Manager),和CUAS一起工作

 

 

输入法框架

 

先讲一点点历史,Windows提供了两套输入法框架: Windows XP及之前,是IMM (Input Method Manager),基于纯函数API的。目前市面上非微软中文输入法基本上都是只实现IMM框架。

Windows XP开始及以后,Windows提供新的输入框架TSF,是基于COM的。实际上,到了Windows Vista,Windows 7,所有的应用程序和各种输入控件都是优先使用TSF的实现。但之所以Windows Vista,Windows 7用户还能使用各种基于IMM的输入法,是因为Windows提供了一个组件来将所有TSF的请求转为IMM的API。(PunCha:难道Win8开始就不提供了吗?!很有可能,因为Win8下很多Imm的函数都不能使用了)

按照微软的说法,TSF会最终取代IMM框架。而微软拼音基于兼容,功能和性能方面的原因,将这两个框架都实现了。(PunCha:Win8下,只有你们一家还能用Imm的API,很厚道!)

下面主要介绍TSF框架的输入法及与应用程序的交互。

 

TSF框架

 

Cicero它的目标是提供一套简单通用并易扩展的框架,用于高级文本输入和自然语言处理。一个TSF text service能够提供多语言支持和处理:键盘输入(我们通常讲的输入法),手写识别,语言识别等。

TSF的构架图

从上面构架图可以看到,TSF提供一个位于应用和输入法实现的间接层(一个Text service/TIP可以是一个输入法,或语音识别,PunCha:记住,TIP就是一个输入法提供的一个服务,比如百度输入法提供“语音、手写、键盘”输入,那就是3个Tip,但是一般我们都是用键盘输入,所以可以简单认为Tip是一种输入法。)。所以,TSF的优点在于,它是一个设备无关,语言中立,可扩展的系统;同时给用户提供一致的输入体验。任何TSF-enabled应用程序都能从任何text service接受文字输入,而不用考虑Text source的具体细节。同时,Text service也不用考虑各种不同应用的差别。譬如下面的应用场景:

 

上图可以看出, 应用程序收到”The boy ate the red apple”这段文字, 但是它不知道这些字是哪个TIP输入的,有可能是”Keyboard TIP”, 或是”Speech TIP”等。

 

与应用程序的交互

 

那么,这个框架是如何工作的?看看下面的组件交互图

在windows XP下,默认是CUAS关闭的,其交互如下

 

从上图可见,所有“edit control”(包括Notepad)都是直接调用IMM的API,最后调用IMM输入法,而4.1版本后的“RichEdit control”(包括WordPad等)是直接用TSF实现的输入法)

(PunCha:注意,是TSF依赖于Imm)

在Windows XP下如果打开CUAS,或者在Windows Vista和Windows 7下,则如下交互:

可以看到,IMM32和CUAS中多了一条交互,这意味着如果一个输入法实现了TIP,在Windows Vista和Windows 7,或CUAS打开的Windows XP下,应用程序的所有输入由TIP实现完成。

(PunCha:注意,Imm依赖于CUAS了!即可以使用用TSF实现的输入法了,应该就是高级输入)

 

下面内容来自第二篇被引用的文章的一条注释,使用TSF COM API获得输入法信息:

 

 

 1 #include <windows.h>  
 2 #include <msctf.h>  
 3   
 4 int _tmain(int argc, TCHAR* argv[])  
 5 {  
 6     CoInitialize(0);  
 7     HRESULT hr = S_OK;  
 8   
 9     //PunCha:创建Profiles接口被  
10     ITfInputProcessorProfiles *pProfiles;  
11     hr = CoCreateInstance(  CLSID_TF_InputProcessorProfiles,  
12         NULL,  
13         CLSCTX_INPROC_SERVER,  
14         IID_ITfInputProcessorProfiles,  
15         (LPVOID*)&pProfiles);  
16   
17     if(SUCCEEDED(hr))  
18     {  
19         IEnumTfLanguageProfiles* pEnumProf = 0;  
20         //PunCha:枚举所有输入法咯  
21         hr = pProfiles->EnumLanguageProfiles(0x804, &pEnumProf);  
22         if (SUCCEEDED(hr) && pEnumProf)  
23         {  
24             TF_LANGUAGEPROFILE proArr[2];  
25             ULONG feOut = 0;  
26             //PunCha:其实proArr这里应该写成 &proArr[0],因为里面只需要一个TF_LANGUAGEPROFILE变量!而且,proArr[1]都没用到过!  
27             while (S_OK == pEnumProf->Next(1, proArr, &feOut))  
28             {  
29                 //PunCha:获取他的名字  
30                 BSTR bstrDest;  
31                 hr = pProfiles->GetLanguageProfileDescription(proArr[0].clsid, 0x804, proArr[0].guidProfile, &bstrDest);  
32                 OutputDebugString(bstrDest);  
33                 wprintf(bstrDest); printf("\n");  
34   
35                 BOOL bEnable = false;  
36                 hr = pProfiles->IsEnabledLanguageProfile(proArr[0].clsid, 0x804, proArr[0].guidProfile, &bEnable);  
37                 if (SUCCEEDED(hr))  
38                 {  
39                     printf("Enabled %d\n", bEnable);  
40                 }  
41                 SysFreeString(bstrDest);  
42             }  
43         }  
44   
45         pProfiles->Release();  
46     }  
47   
48     CoUninitialize();  
49     return 0;  
50 }  

 

以下 C# -TSF 的 COM 组件代码 转载自: https://dotblogs.com.tw/code6421/archive/2010/09/27/17909.aspx

 

 1 ///////////////////////////////////////////////////////////////////////////////////////////////
 2 //               Microsoft Text Service Framework Declaration
 3 //                        from C++ header file
 4 //
 5 //////////////////////////////////////////////////////////////////////////////////////////////
 6 using System;
 7 using System.ComponentModel;
 8 using System.Collections.Generic;
 9 using System.Text;
10 using System.Runtime.InteropServices;
11 using System.Security;
12  
13 namespace TSF
14 {
15     [StructLayout(LayoutKind.Sequential)]
16     internal struct TF_LANGUAGEPROFILE
17     {
18         internal Guid clsid;
19         internal short langid;
20         internal Guid catid;
21         [MarshalAs(UnmanagedType.Bool)]
22         internal bool fActive;
23         internal Guid guidProfile;
24     }
25  
26     [ComImport, SecurityCritical, SuppressUnmanagedCodeSecurity,
27 Guid("1F02B6C5-7842-4EE6-8A0B-9A24183A95CA"),
28      InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
29     internal interface ITfInputProcessorProfiles
30     {
31         [SecurityCritical]
32         void Register(); //non-implement!! may be is wrong declaration.
33         [SecurityCritical]
34         void Unregister(); //non-implement!! may be is wrong declaration.
35         [SecurityCritical]
36         void AddLanguageProfile(); //non-implement!! may be is wrong declaration.
37         [SecurityCritical]
38         void RemoveLanguageProfile(); //non-implement!! may be is wrong declaration.
39         [SecurityCritical]
40         void EnumInputProcessorInfo(); //non-implement!! may be is wrong declaration.
41         [SecurityCritical]
42         int GetDefaultLanguageProfile(short langid,ref Guid catid,out Guid clsid,out Guid profile);
43         [SecurityCritical]
44         void SetDefaultLanguageProfile(); //non-implement!! may be is wrong declaration.
45         [SecurityCritical]
46         int ActivateLanguageProfile(ref Guid clsid, short langid, ref Guid guidProfile);
47         [PreserveSig, SecurityCritical]
48         int GetActiveLanguageProfile(ref Guid clsid, out short langid, out Guid profile);
49         [SecurityCritical]
50         int GetLanguageProfileDescription(ref Guid clsid,short langid,ref Guid profile,out IntPtr desc);
51         [SecurityCritical]
52         void GetCurrentLanguage(out short langid); //non-implement!! may be is wrong declaration.
53         [PreserveSig, SecurityCritical]
54         int ChangeCurrentLanguage(short langid); //non-implement!! may be is wrong declaration.
55         [PreserveSig, SecurityCritical]
56         int GetLanguageList(out IntPtr langids, out int count);
57        [SecurityCritical]
58         int EnumLanguageProfiles(short langid, out IEnumTfLanguageProfiles enumIPP);
59         [SecurityCritical]
60         int EnableLanguageProfile();
61         [SecurityCritical]
62         int IsEnabledLanguageProfile(ref Guid clsid, short langid, ref Guid profile, out bool enabled);
63         [SecurityCritical]
64         void EnableLanguageProfileByDefault(); //non-implement!! may be is wrong declaration.
65         [SecurityCritical]
66         void SubstituteKeyboardLayout(); //non-implement!! may be is wrong declaration.
67     }
68  
69     [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
70  Guid("3d61bf11-ac5f-42c8-a4cb-931bcc28c744")]
71     internal interface IEnumTfLanguageProfiles
72     {
73         void Clone(out IEnumTfLanguageProfiles enumIPP);
74         [PreserveSig]
75         int Next(int count, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
76 TF_LANGUAGEPROFILE[] profiles, out int fetched);
77         void Reset();
78         void Skip(int count);
79     }
80  
81     internal static class TSF_NativeAPI
82     {
83         public static readonly Guid GUID_TFCAT_TIP_KEYBOARD;
84  
85         static TSF_NativeAPI()
86         {
87             GUID_TFCAT_TIP_KEYBOARD = new Guid(0x34745c63, 0xb2f0,
88 0x4784, 0x8b, 0x67, 0x5e, 0x12, 200, 0x70, 0x1a, 0x31);
89         }
90  
91         [SecurityCritical, SuppressUnmanagedCodeSecurity, DllImport("msctf.dll")]
92         public static extern int TF_CreateInputProcessorProfiles(out ITfInputProcessorProfiles profiles);
93     }
94 }

 

 1 public static short[] GetLangIDs()
 2 {
 3        List<short> langIDs = new List<short>();
 4        ITfInputProcessorProfiles profiles;
 5        if (TSF_NativeAPI.TF_CreateInputProcessorProfiles(out profiles) == 0)
 6       {
 7            IntPtr langPtrs;
 8            int fetchCount = 0;
 9            if (profiles.GetLanguageList(out langPtrs, out fetchCount) == 0)
10            {
11                for (int i = 0; i < fetchCount; i++)
12                {
13                    short id = Marshal.ReadInt16(langPtrs, sizeof(short) * i);
14                    langIDs.Add(id);
15                }
16            }
17            Marshal.ReleaseComObject(profiles);
18        }
19        return langIDs.ToArray();
20 }
21  
22 public static string[] GetInputMethodList(short langID)
23 {
24      List<string> imeList = new List<string>();
25      ITfInputProcessorProfiles profiles;
26     if (TSF_NativeAPI.TF_CreateInputProcessorProfiles(out profiles) == 0)
27      {
28          try
29          {
30              IEnumTfLanguageProfiles enumerator = null;
31              if (profiles.EnumLanguageProfiles(langID, out enumerator) == 0)
32              {
33                 if (enumerator != null)
34                 {
35                      TF_LANGUAGEPROFILE[] langProfile = new TF_LANGUAGEPROFILE[1];
36                      int fetchCount = 0;
37                      while (enumerator.Next(1, langProfile, out fetchCount) == 0)
38                      {
39                                 IntPtr ptr;
40                                 if (profiles.GetLanguageProfileDescription(ref langProfile[0].clsid,
41 langProfile[0].langid, ref langProfile[0].guidProfile, out ptr) == 0)
42                                 {
43                                     bool enabled;
44                                     if (profiles.IsEnabledLanguageProfile(ref langProfile[0].clsid,
45  langProfile[0].langid, ref langProfile[0].guidProfile, out enabled) == 0)
46                          {
47                             if (enabled)
48                               imeList.Add(Marshal.PtrToStringBSTR(ptr));
49                          }
50                       }
51                      Marshal.FreeBSTR(ptr);
52                  }
53               }
54           }
55        }
56        finally
57       {
58            Marshal.ReleaseComObject(profiles);
59        }
60     }
61     return imeList.ToArray();
62  }

 

 1 private short[] langIDs;
 2 ………
 3 private void button1_Click(object sender, EventArgs e)
 4 {
 5      langIDs = TSFWrapper.GetLangIDs();
 6      if (langIDs.Length > 0)
 7      {
 8          string[] list = TSFWrapper.GetInputMethodList(langIDs[0]);
 9          foreach (string desc in list)
10            listBox1.Items.Add(desc);
11       }
12 }

 

 

 1 public static bool ActiveInputMethodWithDesc(short langID, string desc)
 2 {
 3     ITfInputProcessorProfiles profiles;
 4     if (TSF_NativeAPI.TF_CreateInputProcessorProfiles(out profiles) == 0)
 5     {
 6        try
 7        {
 8           IEnumTfLanguageProfiles enumerator = null;
 9           if (profiles.EnumLanguageProfiles(langID, out enumerator) == 0)
10           {
11               if (enumerator != null)
12               {
13                     TF_LANGUAGEPROFILE[] langProfile = new TF_LANGUAGEPROFILE[1];
14                     int fetchCount = 0;
15                     while (enumerator.Next(1, langProfile, out fetchCount) == 0)
16                     {
17                         IntPtr ptr;
18                         if (profiles.GetLanguageProfileDescription(ref langProfile[0].clsid,
19  langProfile[0].langid, ref langProfile[0].guidProfile, out ptr) == 0)
20                         {
21                              bool enabled;
22                              if (profiles.IsEnabledLanguageProfile(ref langProfile[0].clsid,
23 langProfile[0].langid, ref langProfile[0].guidProfile, out enabled) == 0)
24                              {
25                                 if (enabled)
26                                 {
27                                     string s = Marshal.PtrToStringBSTR(ptr);
28                                     if (s.Equals(desc))
29                                        return profiles.ActivateLanguageProfile(ref langProfile[0].clsid,
30  langProfile[0].langid, ref langProfile[0].guidProfile) == 0;
31                                  }
32                               }
33                               Marshal.FreeBSTR(ptr);
34                          }
35                        }
36                    }
37                 }
38             }
39             finally
40             {
41                 Marshal.ReleaseComObject(profiles);
42             }
43        }
44        return false;
45 }

 

1 private void button2_Click(object sender, EventArgs e)
2 {
3     if (langIDs != null)
4     {
5            if (listBox1.SelectedIndex != -1)
6                TSFWrapper.ActiveInputMethodWithDesc(langIDs[0], (string)listBox1.SelectedItem);
7      }
8  }

 

 1 public static bool DeActiveInputMethod(short langID)
 2 {
 3      List<string> imeList = new List<string>();
 4      ITfInputProcessorProfiles profiles;
 5      if (TSF_NativeAPI.TF_CreateInputProcessorProfiles(out profiles) == 0)
 6     {
 7         try
 8         {
 9              Guid clsid = Guid.Empty;
10              return profiles.ActivateLanguageProfile(ref clsid, langID, ref clsid) == 0;
11         }
12         finally
13        {
14              Marshal.ReleaseComObject(profiles);
15         }
16      }
17      return false;
18  }

 

转载的源码下载: VistaIMEHelperNet.zip  

 

以上内容,来自网络,

感谢各位原作者为 Xp、Win7、Win8、Win10 的 IME、TSF 输入法 API技术 所 作出的努力。

 

posted on 2018-05-05 09:59  InkFx  阅读(2528)  评论(2编辑  收藏  举报