原文:VISTA 与输入法程式介面

VISTA 与输入法程式介面

 
 
 
文/黄忠成
 
   近日,我所兼职顾问的公司开始将旧有的Win32 程式及新开发的.NET 应用程式移转到VISTA 系统上测试,由于我们的应用程式多半是商用套装软体,
相当然尔对于以程式切换输入法的需求是一定存在的,对于客户来说,在焦点移往该输入中文的栏位时,由系统自动为其切换适当的输入法是种便利的设计!
只是这些原本在Windows XP/2000/2003 上运作的相当正常的程式,到了VISTA 后,却不约而同出现了同样的问题,那就是自动切换输入法的功能全部失效了,
这不只出现在旧有的Win32 应用程式,连新开发的.NET Framework 2.0 应用程式也无法幸免!当工程师们向我询问关于此问题的解决办法时,我直觉的认为,
这可能是VISTA 在输入法的程式介面上做了变动,也就是旧有的API 已经失去功能,由另外一种介面来取代了!只是,我毫无头绪,不知该如何去找出这个
新介面是什么,更别谈说提出一个可以解决此问题的办法了。我与多数设计师一样,立刻就打开google ,企图在搜寻引擎上找到一点蛛丝马迹,
很不幸的!google 上找不到任何有关此问题的线索,在这种情况下,我想到了.NET Framework 3. 0 ,这是目前最新的.NET Framework版本,或许里面
已经使用到了这个新的API,但测试的结果仍然是一样,原本于.NET Framework的Windows Form应用程式中,我们可以利用以下的程式码来列出系统
中所安装的输入法。
public void GetLanguages()
{
   (在InputLanguage.InstalledInputLanguages中的InputLanguage lang)
{
      textBox1.Text + = lang.Culture.EnglishName +'\ n';
   }
}
基本上,此方法通用于.NET Framework 1.0/1.1/2.0/3.0,在Windows XP/2000/2003上都可以正常运作,但在VISTA下,这个方式只能列出该系统所安装的语言,
而非输入法!事实上,这个物件是利用Windows API:GetKeyboardLayoutList函式来取得输入法列表,而此函式目前看来,已经无法在VISTA上正常运作了。
既然在Windows Form Framework下无法找到线索,我转往新的Framework:Windows Presentation Foundation,也就是WPF!这是Windows最新的UI介面,
总该有些线索了吧?答案很令我意外,WPF中虽然也存在着InputLanguageManager物件,但一样也只能列出系统所安装的语言,无法进一步的列出输入法。
最后!我将脑筋动到了正处于Beta的.Net Framework 3.5上,虽然结果仍然相同,但于其中我发现了一个Framework的踪迹,那就是TSF(Text Service Framework),
看来!在VISTA中的Imm32.dll(用来管理、切换输入法介面所在的DLL)所有功能皆已被此Framework完全取代。既然已经找到了一点蛛丝马迹,接下来就只要搞清楚
TSF的设计概念及使用方式,就能够解决当下所遭遇到的问题了。TSF是一组以COM物件组成的Framework,主要目的在提供更具延展性、安全性的语言服务,
与旧有的Imm32.dll以输入法为中心的设计不同,TSF一开始就设计成可于单一系统中安装多个语言,而每个语言可以拥有多个输入法,从此点看来,在以多语言
支援所设计的VISTA环境下,Imm32.dll会失效的理由就不难理解了。好了!这就是前半部的探索过程,现在就让我们进入问题的核心,TSF所提供的功能相当多,
但目前我们只需要列出输入法、切换输入法这些功能,所以本文就将焦点集中于此,待日后有机会再与读者们分享TSF其它的运用。
 
 
与TSF相遇,列出特定语言下的输入法
 
 
 在现在所能找到的TSF资讯,皆是以C++做为基准所撰写的,所有范例也都以C++来撰写的,因此要于.NET中运用TSF的话,首先得先将Windows SDK中所提供
的TSF C++ Header file以P/Invoke方式宣告成.NET语言可用的格式,本文中以C#为例,如下所示:
 
[msctf.cs]
///////////////////////////////////////////////////////////////////////////////////////////////
// Microsoft文本服务框架声明
//从C ++头文件
//
//////////////////////////////////////////////////////////////////////////////////////////////
使用系统;
使用System.ComponentModel;
使用System.Collections.Generic;
使用System.Text;
使用System.Runtime.InteropServices;
使用System.Security;
 
命名空间TSF
{
    [ StructLayout(LayoutKind .Sequential)]
    内部 结构 TF_LANGUAGEEPROFILE
    {
        内部 Guid clsid;
        内部 短 LANGID;
        内部 Guid catid;
        [ MarshalAs(UnmanagedType .Bool)]
        内部 布尔功能;
        内部 Guid guidProfile;
    }
 
    [ ComImport,SecurityCritical,SuppressUnmanagedCodeSecurity,
Guid (“1F02B6C5-7842-4EE6-8A0B-9A24183A95CA”),
     InterfaceType(ComInterfaceType .InterfaceIsIUnknown)]
    内部 接口 ITfInputProcessorProfiles
    {
        [ SecurityCritical ]
        void Register(); //非执行!可能是错误的声明。
        [ SecurityCritical ]
        void Unregister(); //非执行!可能是错误的声明。
        [ SecurityCritical ]
        void AddLanguageProfile(); //非执行!可能是错误的声明。
        [ SecurityCritical ]
        void RemoveLanguageProfile(); //非执行!可能是错误的声明。
        [ SecurityCritical ]
        void EnumInputProcessorInfo(); //非执行!可能是错误的声明。
        [ SecurityCritical ]
        int  GetDefaultLanguageProfile(short langid,ref Guid catid,out Guid clsid,out Guid profile);
        [ SecurityCritical ]
        void SetDefaultLanguageProfile(); //非执行!可能是错误的声明。
        [ SecurityCritical ]
        int ActivateLanguageProfile(ref Guid clsid,short langid,ref Guid guidProfile);
        [ PreserveSig,SecurityCritical ]
        int GetActiveLanguageProfile(ref Guid clsid,out short langid,out Guid profile);
        [ SecurityCritical ]
        int GetLanguageProfileDescription(ref Guid clsid,short langid,ref Guid profile,out IntPtrdesc);
        [ SecurityCritical ]
        void GetCurrentLanguage(out short langid); //非执行!可能是错误的声明。
        [ PreserveSig,SecurityCritical ]
        int ChangeCurrentLanguage(short langid); //非执行!可能是错误的声明。
        [ PreserveSig,SecurityCritical ]
        int GetLanguageList (out IntPtr langids,out int count);
       [ SecurityCritical ]
        int EnumLanguageProfiles(short langid,out IEnumTfLanguageProfiles enumIPP);
        [ SecurityCritical ]
        int EnableLanguageProfile();
        [ SecurityCritical ]
        int IsEnabledLanguageProfile(ref guid clsid,short langid,ref Guid profile,out bool enabled);
        [ SecurityCritical ]
        void EnableLanguageProfileByDefault(); //非执行!可能是错误的声明。
        [ SecurityCritical ]
        void SubstituteKeyboardLayout(); //非执行!可能是错误的声明。
    }
 
    [ ComImport,InterfaceType(ComInterfaceType .InterfaceIsIUnknown),
 Guid(“3d61bf11-ac5f-42c8-a4cb-931bcc28c744”)]
    内部 接口 IEnumTfLanguageProfiles
    {
        无效克隆(输出 IEnumTfLanguageProfiles enumIPP);
        [ PreserveSig ]
        int Next(int count,[ Out,MarshalAs(UnmanagedType .LPArray,SizeParamIndex = 2)]
TF_LANGUAGEPROFILE [] profiles,out int fetched);
        无效重置();
        无效跳过(int count);
    }
 
    内部 静态 类 TSF_NativeAPI
    {
        公共 静态 只读 guid GUID_TFCAT_TIP_KEYBOARD;
 
        静态 TSF_NativeAPI()
        {
            GUID_TFCAT_TIP_KEYBOARD = 新的 Guid(0x34745c63,0xb2f0,
0x4784,0x8b,0x67,0x5e,0x12,200x,0x1a,0x31);
        }
 
        [ SecurityCritical,SuppressUnmanagedCodeSecurity,DllImport(“msctf.dll”)]
        public static extern int TF_CreateInputProcessorProfiles(out ITfInputProcessorProfiles profiles);
    }
}
OK !我知道,这段程式码对于不熟悉COM、P/Invoke的读者而言,就像是无字天书般难懂,不过请放心,我们后面会再撰写一个Wrapper物件,
简化使用TSF的过程。在这个程式码中,有几个函式值得注意,第一个就是GetLanguageList,她可以列出系统中所安装的语言,并传回一个
LANGID型别的阵列,一般来说,预设的语言会排在阵列中的第一个,透过LANGID,我们就能够呼叫另一个函式:EnumLanguageProfiles
来取得该语言下所安装的输入法了,如下例所示:
 
[TSFWrapper.cs]
public static short [] GetLangIDs()
{
       List < short > langIDs = new List < short >();
       ITfInputProcessorProfiles配置文件;
       如果(TSF_NativeAPI .TF_CreateInputProcessorProfiles(out profiles)== 0)
      {
           IntPtr langPtrs;
           int fetchCount = 0;
           if(profiles.GetLanguageList(out langPtrs,out fetchCount)== 0)
           {
               for(int i = 0; i <fetchCount; i ++)
               {
                   short id = Marshal .ReadInt16(langPtrs,sizeof(short)* i);
                   langIDs.Add(ID);
               }
           }
           Marshal .ReleaseComObject(profiles);
       }
       返回 langIDs.ToArray();
}
 
public static string [] GetInputMethodList(short langID)
{
     List < string > imeList = new List < string >();
     ITfInputProcessorProfiles配置文件;
    如果(TSF_NativeAPI .TF_CreateInputProcessorProfiles(out profiles)== 0)
     {
         尝试
         {
             IEnumTfLanguageProfiles enumerator = null ;
             if(profiles.EnumLanguageProfiles(langID,out enumerator)== 0)
             {
                if(enumerator!= null)
                {
                     TF_LANGUAGEPROFILE [] langProfile = new TF_LANGUAGEPROFILE [1];
                     int fetchCount = 0;
                     while(enumerator.Next(1,langProfile,out fetchCount)== 0)
                     {
                                IntPtr ptr;
                                if(profiles.GetLanguageProfileDescription(ref langProfile [0] .clsid,
langProfile [0] .langid,ref langProfile [0] .guidProfile,out ptr)== 0)
                                {
                                    布尔启用;
                                    if(profiles.IsEnabledLanguageProfile(ref langProfile [0] .clsid,
 langProfile [0] .langid,ref langProfile [0] .guidProfile,out enabled)== 0)
                         {
                            如果(启用)
                              imeList.Add(Marshal .PtrToStringBSTR(ptr));
                         }
                      }
                     Marshal.FreeBSTR(ptr);
                 }
              }
          }
       }
       最后
      {
           Marshal .ReleaseComObject(profiles);
       }
    }
    return imeList.ToArray();
 }
上例是节录自TSFWapper,笔者所设计的TSF Wrapper物件,利用此物件,设计师可以在不了解TSF的情况下,取得输入法列表及切换输入法。
在使用上,设计师得先呼叫GetLangIDs函式来取得目前系统所安装的语言,再针对特定语言呼叫GetInputMethodList函式来取得所安装的输入法列表,
如下面的程式片段所示。
私人短 [] langIDs;
………
private void button1_Click(object sender,EventArgs e)
{
     langIDs = TSFWrapper .GetLangIDs();
     如果(langIDs.Length> 0)
     {
         string [] list = TSFWrapper .GetInputMethodList(langIDs [0]);
         的foreach(字符串递减的列表)
           listBox1.Items.Add(降序);
      }
}
 
下面是此范例于VISTA上运行的画面。
 
与TSF 相遇II ,切换输入法
 
 在可以取得输入法列表后,接下来的工作当然是实作切换输入法的功能了,在前面的msctf.cs中,ActivateLanguageProfile函式就是作此用途,
同样的,TSFWrapper物件中也实作了简单的函式来协助设计师完成此工作。
public static bool ActiveInputMethodWithDesc(short langID,string desc)
{
    ITfInputProcessorProfiles配置文件;
    如果(TSF_NativeAPI .TF_CreateInputProcessorProfiles(out profiles)== 0)
    {
       尝试
       {
          IEnumTfLanguageProfiles enumerator = null ;
          if(profiles.EnumLanguageProfiles(langID,out enumerator)== 0)
          {
              if(enumerator!= null)
              {
                    TF_LANGUAGEPROFILE [] langProfile = new TF_LANGUAGEPROFILE [1];
                    int fetchCount = 0;
                    while(enumerator.Next(1,langProfile,out fetchCount)== 0)
                    {
                        IntPtr ptr;
                        if(profiles.GetLanguageProfileDescription(ref langProfile [0] .clsid,
 langProfile [0] .langid,ref langProfile [0] .guidProfile,out ptr)== 0)
                        {
                             布尔启用;
                             if(profiles.IsEnabledLanguageProfile(ref langProfile [0] .clsid,
langProfile [0] .langid,ref langProfile [0] .guidProfile,out enabled)== 0)
                             {
                                如果(启用)
                                {
                                    string s = Marshal .PtrToStringBSTR(ptr);
                                    if(s.Equals(desc))
                                       返回 profiles.ActivateLanguageProfile(参考 langProfile [0] .clsid,
 langProfile [0] .langid,ref langProfile [0] .guidProfile)== 0;
                                 }
                              }
                              Marshal.FreeBSTR(ptr);
                         }
                       }
                   }
                }
            }
            最后
            {
                Marshal .ReleaseComObject(profiles);
            }
       }
       返回 false ;
}
使用此函式的方法很简单,只需传入欲切换的输入法名称及语言(LANGID)即可。
 
private void button2_Click(object sender,EventArgs e)
{
    如果(langIDs!= null)
    {
           if(listBox1.SelectedIndex!= -1)
               TSFWrapper .ActiveInputMethodWithDesc(langIDs [0],(string)listBox1.SelectedItem);
     }
 }
 
 
与TSF暂别,取得现行输入法及关闭输入法
 
 能切过去,也要能切回来,本文最后的工作就是得将输入法切回英文输入,在进入正题前,笔者先介绍TSFWrapper 中的另一个函式:GetCurrentInputMethodDes c ,
此函式会传回目前系统作用中的输入法名称,这有何用呢?一般来说,在设计自动输入法切换时,会有两种模式,一是要求使用者选择一种输入法做为主要输入法,
当焦点所在栏位需要输入中文时,系统自动切换至此输入法。另一种模式是是不硬性要求使用者选择输入法,而是以最近所切换的输入法为准,在这种模式下,
GetCurrentInputMethodDesc就可以派上用场了。好了,回到正题来,在中文栏位切成中文输入法,在英文栏位时当然就得切回英数输入法了,
TSFWrapper提供了此函式。
public static bool DeActiveInputMethod(short langID)
{
     List < string > imeList = new List < string >();
     ITfInputProcessorProfiles配置文件;
     如果(TSF_NativeAPI .TF_CreateInputProcessorProfiles(out profiles)== 0)
    {
        尝试
        {
             Guid clsid = Guid .Empty;
             返回 profiles.ActivateLanguageProfile(ref clsid,langID,ref clsid)== 0;
        }
        最后
       {
             Marshal .ReleaseComObject(profiles);
        }
     }
     返回 false ;
 }
 
后记
 
   TSF 目前所能取得的资讯相当的少,于google 上讨论此课题的文章也极其稀少,仅只有MSDN 上几行叙述,希望笔者此篇文章能多少帮助诸位,
少走一些冤枉路,下次再见了!
 

VISTA 与输入法程式介面的更多相关文章

  1. C# TSF 输入法的获取

    原文 C# TSF 输入法的获取 起因: 「添雨跟打器」中存在一个问题.在 windows 8/10 里面,输入法就获取不到了.我一直没有去管这样的问题.但是也大致知道,可能是 TSF 架构的问题. ...

  2. Backbone.js

    Backbone.js是一套JavaScript框架與RESTful JSON的應用程式介面.也是一套大致上符合MVC架構的編程範型.Backbone.js以輕量為特色,只需依賴一套Javascrip ...

  3. LINUX诞生

    编辑 Linux[1]  是一类Unix计算机操作系统的统称.Linux操作系统也是自由软件和开放源代码发展中最著名的例子.在1991 年的十月,由一个名为 Linus Torvalds的年轻芬兰大学 ...

  4. facebook api介绍

    转自(http://sls.weco.net/node/10773) 一.Facebook API 基礎概念 Facebook API 概論 : API 最大的好處在於可以讓程式開發人員只需要根據 A ...

  5. PAM 認 證 模 組

    作者:陳柏菁 E-mail 作用:限制哪些用户或者组可以从哪里登陆,或者可以建立/etc/nologin立即阻止一般用户登陆,限制 user可以使用的资源.例如cpu,文件,登陆数量,某些服务的登陆时 ...

  6. socket 中午吃的啥 socket 并发服务器 fork

    http://www.cnblogs.com/thinksasa/archive/2013/02/26/2934206.html zh.wikipedia.org/wiki/網路插座 在作業系統中,通 ...

  7. 修改 /var/lib/locales/supported.d/local 文件(使用 locale -a 命令查看系统中所有已配置的 locale)

    转自:http://zyxhome.org/wp/cc-prog-lang/c-stdlib-setlocale-usage-note/ http://www.west263.com/info/htm ...

  8. OpenCV之响应鼠标(二):函数cvSetMouseCallback()和其副程式onMouse()的使用(OpenCV2.4.5)

    每當滑鼠在視訊視窗介面點擊一下的時候,都會有固定三個動作 1.點擊(Click) 2.放開(Down)3.滑動(move) 因此,程式執行滑鼠在點擊的時候onMouse()都會連續跑三次,代表滑鼠在點 ...

  9. Android InputMethodManager输入法简介

    正文 一.结构 public final class InputMethodManager extends Object Java.lang.Object android.view.inputmeth ...

随机推荐

  1. [Typescript] Generics using TypeScript

    In this lesson we cover the key reason why programming languages need generics. We then show how use ...

  2. Django之模板过滤器

    Django 模板过滤器也是我们在以后基于 Django 网站开发过程中会经常遇到的,如显示格式的转换.判断处理等.以下是 Django 过滤器列表,希望对为大家的开发带来一些方便. 一.形式:小写 ...

  3. 小强的HTML5移动开发之路(52)——jquerymobile中的触控交互

    当使用移动设备进行触控操作时,最常用的就是轻击.按住屏幕或者手势操作,jQuery Mobile可以通过绑定的触控事件来响应使用者的特定触控行为. 一.轻击与按住 直接上代码(一切皆在代码中,细细品吧 ...

  4. 【oracle11g ,19】索引管理

    一.索引的分类: 1.逻辑上分为:  单列索引和复合索引  唯一索引和非唯一索引  函数索引 domain索引 2.物理上分:  分区索引和非分区索引 b-tree  bitmap 注意:表和索引最好 ...

  5. winfrom RichTextBox每行字体的颜色

    public static void AppendTextColorful(this RichTextBox rtBox, string text, Color color, bool addNewL ...

  6. Html Vedio标签全屏

    http://ask.csdn.net/questions/221701 http://www.zhangxinxu.com/wordpress/2010/03/every-browser-suppo ...

  7. php实现 明明的随机数

    php实现 明明的随机数 一.总结 一句话总结: 1.asort是干嘛的? asort — 对数组进行排序并保持索引关系 2.从控制台取数据怎么取? trim(fgets(STDIN)) 3.多组测试 ...

  8. [Django] Creating an app, models and database

    To add a new app, first cd to the project. Then run: python manage.py startapp scrumboard After that ...

  9. 囚徒困境、价格大战与 iPhone 的价格

    静态/动态,完全/不完全: 完全信息静态博弈: 不完全信息静态博弈: 完全信息动态博弈: 不完全信息动态博弈: 囚徒困境实际上反映了一个深刻的哲学问题:个人利益与集体利益的矛盾.个人为了自己利益的最大 ...

  10. 极光推送Jpush功能(具体参照官网说明文档,注意此文红色字体)

    1.导入框架 2. //推送 #import "APService.h" - (BOOL)application:(UIApplication *)application didF ...