C# TSF 输入法的获取
起因:
「添雨跟打器」中存在一个问题。在 windows 8/10 里面,输入法就获取不到了。我一直没有去管这样的问题。但是也大致知道,可能是 TSF 架构的问题。
TSF:
Microsoft Windows 文本服务框架(TSF) 是一个包含在Windows XP 及其后继版本操作系统的系统服务。TSF为高级文本输入的通信以及自然语言技术提供了一个简单的可扩展的框架。
以上引自百度百科。
MSDN:
于是第一时间去 MSDN 查看了一下关于 TSF 。全英文的文档看得云里雾里。但是搞清了一点情况。TSF 它在 Windows 里面所对应的 dll 文件——msctf.dll。
使用 Visual Studio 自带的命令工具查看该 dll 的函数列表如下:
命令:
1 | dumpbin -exports msctf.dll |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | Dump of file msctf.dll File Type: DLL Section contains the following exports for MSCTF.dll 00000000 characteristics 5699C6A2 time date stamp Sat Jan 16 12:27:14 2016 0.00 version 1 ordinal base 73 number of functions 73 number of names ordinal hint RVA name 2 0 000183E0 CtfImeAssociateFocus 3 1 000633E0 CtfImeConfigure 4 2 00046A10 CtfImeConversionList 5 3 000042E0 CtfImeCreateInputContext 6 4 0000BF70 CtfImeCreateThreadMgr 7 5 000633F0 CtfImeDestroy 8 6 00038C40 CtfImeDestroyInputContext 9 7 00038840 CtfImeDestroyThreadMgr 10 8 00037D40 CtfImeDispatchDefImeMessage 11 9 00046A10 CtfImeEnumRegisterWord 12 A 00046A10 CtfImeEscape 13 B 00063410 CtfImeEscapeEx 14 C 00063490 CtfImeGetGuidAtom 15 D 00046A10 CtfImeGetRegisterWordStyle 16 E 00046A10 CtfImeInquire 17 F 00006E60 CtfImeInquireExW 18 10 000634F0 CtfImeIsGuidMapEnable 19 11 00063540 CtfImeIsIME 20 12 0001C540 CtfImeProcessCicHotkey 21 13 00018840 CtfImeProcessKey 22 14 00046A10 CtfImeRegisterWord 23 15 00046A10 CtfImeSelect 24 16 00036CC0 CtfImeSelectEx 25 17 000184A0 CtfImeSetActiveContext 26 18 000635C0 CtfImeSetCompositionString 27 19 000635D0 CtfImeSetFocus 28 1A 00063640 CtfImeToAsciiEx 29 1B 00046A10 CtfImeUnregisterWord 30 1C 00004A10 CtfNotifyIME 31 1D 000049F0 DllCanUnloadNow 32 1E 000018C0 DllGetClassObject 33 1F 00061430 DllRegisterServer 34 20 00061780 DllUnregisterServer 35 21 0003FDD0 SetInputScope 36 22 0008A580 SetInputScopeXML 37 23 0003FE50 SetInputScopes 38 24 0003FEA0 SetInputScopes2 39 25 00063650 TF_CUASAppFix 40 26 000056B0 TF_CanUninitialize 41 27 00046540 TF_CleanUpPrivateMessages 42 28 0003E5E0 TF_CreateCategoryMgr 43 29 00002BB0 TF_CreateCicLoadMutex 44 2A 000028E0 TF_CreateCicLoadWinStaMutex 45 2B 00063690 TF_CreateDisplayAttributeMgr 46 2C 000047F0 TF_CreateInputProcessorProfiles 47 2D 000636B0 TF_CreateLangBarItemMgr 48 2E 000396B0 TF_CreateLangBarMgr 49 2F 000636D0 TF_CreateThreadMgr 50 30 0000B110 TF_GetAppCompatFlags 51 31 000636F0 TF_GetCompatibleKeyboardLayout 52 32 0003EE90 TF_GetGlobalCompartment 53 33 0006A6F0 TF_GetInitSystemFlags 54 34 0003A7F0 TF_GetInputScope 55 35 0008E580 TF_GetShowFloatingStatus 56 36 00005410 TF_GetThreadFlags 57 37 0000D5C0 TF_GetThreadMgr 58 38 00002DB0 TF_InitSystem 59 39 00046A10 TF_InvalidAssemblyListCacheIfExist 60 3A 0006F4F0 TF_IsCtfmonRunning 61 3B 00063880 TF_IsLanguageBarEnabled 62 3C 00046A10 TF_IsThreadWithFlags 63 3D 00063980 TF_MapCompatibleHKL 64 3E 00063A10 TF_MapCompatibleKeyboardTip 65 3F 000191B0 TF_Notify 66 40 00005E90 TF_PostAllThreadMsg 1 41 00063330 TF_RunInputCPL 67 42 00063A20 TF_SendLangBandMsg 68 43 00040550 TF_SetDefaultRemoteKeyboardLayout 69 44 0008E590 TF_SetShowFloatingStatus 70 45 00046A10 TF_SetThreadFlags 71 46 00002D10 TF_UninitSystem 72 47 00002C50 TF_WaitForInitialized 73 48 000AA2E0 TextInputClientWrapperCreate Summary 3000 .data 1000 .didat D000 .pdata 25000 .rdata 2000 .reloc 42000 .rsrc DF000 .text |
百度:
继续百度搜索一翻。关键字:C#TextServiceFramework。第一篇《微软新一代输入法框架 TSF – Text Service Framework 小小的研究》,跳转至这篇博文时,则找到真正需要的东西。
Wrapper:
整体代码如下:
文件:TSF.cs
该文件定义了 TSF 的结构以及各个方法的接口。其来源来自 C++ 的头文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | /////////////////////////////////////////////////////////////////////////////////////////////// // Microsoft Text Service Framework Declaration // from C++ header file // ////////////////////////////////////////////////////////////////////////////////////////////// using System; using System.ComponentModel; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Security; namespace TSF { [StructLayout(LayoutKind.Sequential)] internal struct TF_LANGUAGEPROFILE { internal Guid clsid; internal short langid; internal Guid catid; [MarshalAs(UnmanagedType.Bool)] internal bool fActive; internal Guid guidProfile; } [ComImport, SecurityCritical, SuppressUnmanagedCodeSecurity, Guid("1F02B6C5-7842-4EE6-8A0B-9A24183A95CA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface ITfInputProcessorProfiles { [SecurityCritical] void Register(); //non-implement!! may be is wrong declaration. [SecurityCritical] void Unregister(); //non-implement!! may be is wrong declaration. [SecurityCritical] void AddLanguageProfile(); //non-implement!! may be is wrong declaration. [SecurityCritical] void RemoveLanguageProfile(); //non-implement!! may be is wrong declaration. [SecurityCritical] void EnumInputProcessorInfo(); //non-implement!! may be is wrong declaration. [SecurityCritical] int GetDefaultLanguageProfile(short langid, ref Guid catid, out Guid clsid, out Guid profile); [SecurityCritical] void SetDefaultLanguageProfile(); //non-implement!! may be is wrong declaration. [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 IntPtr desc); [SecurityCritical] void GetCurrentLanguage(out short langid); //non-implement!! may be is wrong declaration. [PreserveSig, SecurityCritical] int ChangeCurrentLanguage(short langid); //non-implement!! may be is wrong declaration. [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(); //non-implement!! may be is wrong declaration. [SecurityCritical] void SubstituteKeyboardLayout(); //non-implement!! may be is wrong declaration. } [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("3d61bf11-ac5f-42c8-a4cb-931bcc28c744")] internal interface IEnumTfLanguageProfiles { void Clone(out IEnumTfLanguageProfiles enumIPP); [PreserveSig] int Next(int count, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] TF_LANGUAGEPROFILE[] profiles, out int fetched); void Reset(); void Skip(int count); } internal static class TSF_NativeAPI { public static readonly Guid GUID_TFCAT_TIP_KEYBOARD; static TSF_NativeAPI() { GUID_TFCAT_TIP_KEYBOARD = new Guid(0x34745c63, 0xb2f0, 0x4784, 0x8b, 0x67, 0x5e, 0x12, 200, 0x70, 0x1a, 0x31); } [SecurityCritical, SuppressUnmanagedCodeSecurity, DllImport("msctf.dll")] public static extern int TF_CreateInputProcessorProfiles(out ITfInputProcessorProfiles profiles); } } |
文件:TSFWapper.cs
这个文件封装了 TSF 的你需要调用的静态方法。更多的方法,可以在接口定义(上面文件)处找到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | public class TSFWapper { public static short[] GetLangIDs() { List<short> langIDs = new List<short>(); ITfInputProcessorProfiles profiles; if (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); } return langIDs.ToArray(); } public static string[] GetInputMethodList(short langID) { List<string> imeList = new List<string>(); ITfInputProcessorProfiles profiles; if (TSF_NativeAPI.TF_CreateInputProcessorProfiles(out profiles) == 0) { try { 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) { bool enabled; if (profiles.IsEnabledLanguageProfile(ref langProfile[0].clsid, langProfile[0].langid, ref langProfile[0].guidProfile, out enabled) == 0) { if (enabled) imeList.Add(Marshal.PtrToStringBSTR(ptr)); } } Marshal.FreeBSTR(ptr); } } } } finally { Marshal.ReleaseComObject(profiles); } } return imeList.ToArray(); } /// /// 获取当前输入法 /// public static void GetCurrentLang(out string[] lang) { List<short> langIDs = new List<short>(); ITfInputProcessorProfiles profiles; short id = -1; if (TSF_NativeAPI.TF_CreateInputProcessorProfiles(out profiles) == 0) { IntPtr langPtrs; int fetchCount = 0; profiles.GetCurrentLanguage(out id); Marshal.ReleaseComObject(profiles); } lang = id > -1 ? GetInputMethodList(id) : null; } } |

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
C#: Get current keyboard layout\input language
原文https://yal.cc/csharp-get-current-keyboard-layout/ On some occasions, you may want to get a "global" input language - that is, the keyboard layout used by the current foreground window\application\whatever. Basically, simulating the behaviour of the language panel on Windows. The common use cases are on-screen keyboards, fullscreen applications, and widgets. While I wasn't able to find a premade function that get this particular thing during my searches, it turned out not to be too hard to as...
- 下一篇
JAVA 11 初体验
随着JAVA每半年发布一次新版本,前几天JAVA 11隆重登场。在JAVA 11中,增加了一些新的特性和api,同时也删除了一些特性和api,还有一些性能和垃圾回收的改进。 作为一名一线的开发人员,JAVA 11给我们带来哪些便利之处呢?下面我们来体验一下。 在Lambda表达式中使用var 本地变量类型var是java 10提出的新概念,它可以从上下文中推断出本地变量的类型,从而提高代码可读性。我们看看下面的例子: public class Main { public static void main(String[] args) throws Exception { URL url = new URL("http://www.oracle.com/"); URLConnection conn = url.openConnection(); Reader reader = new BufferedReader( new InputStreamReader(conn.getInputStream())); } } 使用var声明后,上面的代码可以改写成: public class Ma...
相关文章
文章评论
共有0条评论来说两句吧...