VISTA 与输入法程式介面

原文: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 上几行叙述,希望笔者此篇文章能多少帮助诸位,

少走一些冤枉路,下次再见了!

范例下载: http://code6421.myweb.hinet.net/Old/VistaIME/VistaIMEHelperNet.zip

原文地址:https://www.cnblogs.com/lonelyxmas/p/9186485.html

时间: 2024-10-16 05:22:34

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

关于操作系统的综述

一.WINDOWS操作系统 MS Windows由Microsoft公司出品,是历史上最成功的桌面操作系统.Microsoft公司在1983年开始研究开发Windows操作系统,并于1985年和1987年推出的Windows1.03版和Windows2.0版,但是并没有取得很大的成功.在对Windows的内存管理.图形界面进行了重大改进并且支持虚拟内存以后,于1990年推出的Windows3.0才取得了重大成功. 1995年,Microsoft公司里程碑似的作品Windows95,提供了更加优秀

SDK、API、类库的最明显的联系与区别是什么

SupportDevelopmentKit.形容词SupportDevelopment形容的名字是Kit,是一个套件一个工具的组合. SDK就是Kit,工具箱,IOS平台上就是XCode,一系列的工具组合在一起,比如你在编辑器里敲代码的时候它会自动补全代码,自动错误检查,你点一下Run,它会调用编译器来自动编译,编译完它会调用iPhone的模拟器来运行,这就是SDK,提供整套的开发工具供开发者使用.API:ApplicationProgrammingInterface.落脚点是Interface

[Intel XDK]基于AppMobi的XDK跨平台应用开发

使用AppMobi* XDK 的跨平台应用程式开发入门 跨平台应用程式开发意指仅编写一次程式,便可于多个平台上部署.应用程式开发者可善加利用倾向跨平台设计与开发的开发工具,使获利更丰.Android* 与iOS* 为目前应用程式开发者最看好的两大行动装置应用程式开发平台,两者在运作上的软体堆叠(stack) 架构和装置硬体效能等皆有所差异,使得应用程式的跨平台开发困难重重.HTML5 为可使跨平台应用程式开发更为轻松的一项技术,提供不同平台间的可移植性(portability). AppMobi

OpenVG简介

原文链接:http://www.tuicool.com/articles/rAFR7b 参考文献1:http://www.doc88.com/p-008309425812.html 参考文献2:http://wenku.baidu.com/link?url=fKmRtafeRtB685fOCld3S85K3CKaDfwoGUL7dMxb3FGKHMLiHdU4WCu2EpasZ_W7iWcvX6EJkC80XJyx-59K4uqwRcKr6jWHM2y-rMgePk_ 参考文献3:http://

MCU VR班會(01)記錄

MCU VR應用班 <翻轉式> 線上討論課堂(01) 記錄如下:-- 舉行時間: 2016/6/29 晚上 21:30 ~ 22:30 mandy: 各位師長好陳信宏: 大家晚安高煥堂: 大家好陳信宏: 老師好..高煥堂: 各位可以用語音講話蘇瑞元: 高老師好.我下載了 entiti 軟體,感覺和 unity3D 很像高煥堂: 各位可先試試語音高煥堂: 我聽到了高煥堂: entiti需要買正式版?高煥堂: 有試用版?蘇瑞元: 是的,連操作方式都很像.蘇瑞元: 目前是 beta 版高煥堂: 我還

winsock编程宝典

TCP连接建立与关闭 相信各位读者现在对於 Winsock 的定义.系统环境,以及一些 Winsock Stack及 Winsock 应用程式,都有基本的认识了.接下来笔者希望能分几期为各位读者介绍一下简单的 Winsock 网路应用程式设计.我们将以 Winsock 1.1 规格所定义的 46 个应用程式介面(API)为基础,逐步来建立一对 TCP socket 主从架构(Client / Server)的程式.在这两个程式中,Server 将使用 Winsock 提供的「非同步」(async

python文件编码说明 coding

权威来源:http://www.python.org/dev/peps/pep-0263/Python源文件的头部声明(声明在文件的第一行或第二行) # coding=utf-8 # coding:utf-8 # -*- coding:utf-8 -*- 要符合正则规范"coding[:=]\s*([-\w.]+)"  注意 :或=前后没有空格 Defining the Encoding Python will default to ASCII as standard encoding

前端设计师常用的五个开发工具

过去几年一直不断地提到 Web 应用新世代的成长,这些 App 内容变得越来越丰富,带动了前端整体的复杂度大幅增加. 但为了能持续面对复杂度的新挑战,开发者已经创造更多工具使整体开发过程更加流畅,从测试框架到分析工具,这些成熟又有用的工具把最棒的体验带给我们的使用者. 本文要分享给大家 5 个我们每天面对挑战所使用最棒的工具和框架 Chrome dev tools 这是目前最好的前端开发工具! Chrome 自从它诞生后,就已经大量投资在开发者工具软体,至今仍不断的在改善它,每一个发布会(每六周

LINUX诞生

编辑 Linux[1]  是一类Unix计算机操作系统的统称.Linux操作系统也是自由软件和开放源代码发展中最著名的例子.在1991 年的十月,由一个名为 Linus Torvalds的年轻芬兰大学生带头开发的作业系统. 中文名 LINUX诞生 时    间 1991年的10月 属    性 操作系统 起源地 芬兰 目录 1 历史背景 2 介绍 3 Linux组成 4 主要优点 历史背景 编辑 1991 年的十月,当初网际网路还不像现在这么普遍,上网的人大部分都隶属于一些研究机构,或者是大学里