使用.NET 4.5.2从C#代码更改键盘布局

我正在编写我的SDL Trados Studio插件。

插件的最后一部分需要一些API完全没有公开的自动化,所以我所拥有的(保持某些东西)是自动化默认的键盘快捷键。

我的代码完全适用于英文键盘布局(以及匈牙利语!),但它当然不适用于希腊语,俄语等等。

我一直在寻找解决方案,但直到现在我都找不到它,不是在网上也不是在SO上,比如这篇文章: 通过代码改变键盘布局c#

我需要将键盘布局更改为英语,以便它可以采用正确的快捷键(和其他字符串)。 然后我需要将其切换回之前的状态。 我正在使用非常有限的API,因此我只有SendKeys我使用。

这是工作代码:

 //Save the document SendKeys.SendWait("^s"); //Open files view SendKeys.SendWait("%v"); SendKeys.SendWait("i"); SendKeys.SendWait("1"); Application.DoEvents(); //get url and credentials from a custom input form string[] psw = UploadData.GetPassword( Settings.GetValue("Upload", "Uri", ""), Vars.wsUsername == null ? Settings.GetValue("Upload", "User", "") : Vars.wsUsername, Vars.wsPassword == null ? "" : Vars.wsPassword ); Application.DoEvents(); if (psw != null) { try { //start upload SendKeys.SendWait("%h"); SendKeys.Send("r"); //select all files SendKeys.Send("%a"); SendKeys.Send("%n"); //enter login url SendKeys.Send("%l"); SendKeys.Send("{TAB}"); SendKeys.Send(psw[0]); SendKeys.Send("{TAB}"); SendKeys.Send("{ENTER}"); //enter username SendKeys.Send("%l"); SendKeys.Send("+{END}"); SendKeys.Send(psw[1]); //enter credentials SendKeys.Send("%p"); SendKeys.Send(SendEscape(psw[2])); SendKeys.Send("{ENTER}"); //start upload SendKeys.SendWait("%f"); } catch (Exception) { MessageBox.Show("Cannot do automatic upload, please use the default method of Trados Studio.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } finally { //switch back to editor view SendKeys.SendWait("%vd"); } } 

所以我有问题:

  1. 有人可以帮我一个代码来实际存储当前的键盘布局并切换到英文,然后在最后切换回来吗?

  2. 有更简单的解决方案吗? 我试着看看本机方法,但它对我来说太高了,所以我真的很感激任何帮助将我的代码转换为原生代码,如果这是转向而不是切换键盘布局。 有什么建议?

切换键盘布局需要一些P / Invoke; 您至少需要以下Windowsfunction才能使其正常工作: LoadKeyboardLayoutGetKeyboardLayoutActivateKeyboardLayout 。 以下import声明对我有用……

 [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "LoadKeyboardLayout", SetLastError = true, ThrowOnUnmappableChar = false)] static extern uint LoadKeyboardLayout( StringBuilder pwszKLID, uint flags); [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "GetKeyboardLayout", SetLastError = true, ThrowOnUnmappableChar = false)] static extern uint GetKeyboardLayout( uint idThread); [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "ActivateKeyboardLayout", SetLastError = true, ThrowOnUnmappableChar = false)] static extern uint ActivateKeyboardLayout( uint hkl, uint Flags); static class KeyboardLayoutFlags { public const uint KLF_ACTIVATE = 0x00000001; public const uint KLF_SETFORPROCESS = 0x00000100; } 

每当我必须使用本机API方法时,我会尝试将它们封装在一个类中,该类将其声明隐藏在项目代码库的其余部分中。 所以,我想出了一个名为KeyboardLayout的类; 该类可以通过给定的CultureInfo加载和激活布局,这可以派上用场……

 internal sealed class KeyboardLayout { ... private readonly uint hkl; private KeyboardLayout(CultureInfo cultureInfo) { string layoutName = cultureInfo.LCID.ToString("x8"); var pwszKlid = new StringBuilder(layoutName); this.hkl = LoadKeyboardLayout(pwszKlid, KeyboardLayoutFlags.KLF_ACTIVATE); } private KeyboardLayout(uint hkl) { this.hkl = hkl; } public uint Handle { get { return this.hkl; } } public static KeyboardLayout GetCurrent() { uint hkl = GetKeyboardLayout((uint)Thread.CurrentThread.ManagedThreadId); return new KeyboardLayout(hkl); } public static KeyboardLayout Load(CultureInfo culture) { return new KeyboardLayout(culture); } public void Activate() { ActivateKeyboardLayout(this.hkl, KeyboardLayoutFlags.KLF_SETFORPROCESS); } } 

如果您只需要让布局在短时间内处于活动状态 – 并且您希望确保在完成后正确恢复布局,则可以使用IDiposable接口编写某种范围类型。 例如…

 class KeyboardLayoutScope : IDiposable { private readonly KeyboardLayout currentLayout; public KeyboardLayoutScope(CultureInfo culture) { this.currentLayout = KeyboardLayout.GetCurrent(); var layout = KeyboardLayout.Load(culture); layout.Activate(); } public void Dispose() { this.currentLayout.Activate(); } } 

你可以像这样使用它……

 const int English = 1033; using (new KeyboardLayoutScope(CultureInfo.GetCultureInfo(English)) { // the layout will be valid within this using-block } 

您应该知道,在较新版本的Windows(从Windows 8开始)中,不能再为某个进程设置键盘布局,而是为整个系统全局设置 – 并且布局也可以由其他应用程序更改,或者通过用户(使用Win + 空格键快捷键)。

我还建议不要使用SendKeys (或其原生对应的SendInput ),因为它模拟键盘输入,它将被路由到活动/聚焦窗口。 使用SendMessage函数是合适的,但您可能希望将其与可以正确确定目标窗口的function相结合; 但解释这种技术将超出这个问题和答案的范围。 这里的答案说明了一个可能的解决方案: 如何向窗口发送击键?

改变class级@Matze,

 internal class KeyboardLayoutScope : IDisposable, IKeyboardLayoutScope { private readonly KeyboardLayout currentLayout; public KeyboardLayoutScope() { } public KeyboardLayoutScope(CultureInfo culture) { currentLayout = KeyboardLayout.GetCurrent(); KeyboardLayout.Load(culture).Activate(); } public KeyboardLayoutScope(KeyboardLayout currentLayout) => this.currentLayout = currentLayout; public void Dispose() => currentLayout.Activate(); } internal interface IKeyboardLayoutScope { void Dispose(); } 

其他类

 internal sealed class KeyboardLayout { private readonly uint hkl; private KeyboardLayout(CultureInfo cultureInfo) => hkl = NativeMethods.LoadKeyboardLayout(new StringBuilder(cultureInfo.LCID.ToString("x8")), KeyboardLayoutFlags.KLF_ACTIVATE); private KeyboardLayout(uint hkl) => this.hkl = hkl; public uint Handle => hkl; public static KeyboardLayout GetCurrent() => new KeyboardLayout(NativeMethods.GetKeyboardLayout((uint)Thread.CurrentThread.ManagedThreadId)); public static KeyboardLayout Load(CultureInfo culture) => new KeyboardLayout(culture); public void Activate() => NativeMethods.ActivateKeyboardLayout(hkl, KeyboardLayoutFlags.KLF_SETFORPROCESS); } 

试试这个:

  public void switch_keyboard(string lang) { string keyboard_lang = InputLanguage.CurrentInputLanguage.Culture.TwoLetterISOLanguageName; if (keyboard_lang != lang) { int en_index = -1; for (int i = 0; i < System.Windows.Forms.InputLanguage.InstalledInputLanguages.Count - 1; i++) { try { if (System.Windows.Forms.InputLanguage.InstalledInputLanguages[i].Culture.TwoLetterISOLanguageName == lang) { en_index = i; break; } } catch { break; } } if (en_index != -1) { InputLanguage.CurrentInputLanguage = System.Windows.Forms.InputLanguage.InstalledInputLanguages[en_index]; } } }