MFC切换输入法(c++ 对指定的窗口使用指定的输入法)

Published
 

 对指定的窗口使用指定的输入法

 

::PostMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)0x04090409);


	//::PostMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)0x04090409);

	//::SendMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGEREQUEST, true, 0x08040804);
	//::SendMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGEREQUEST, true, 0x04090409);

	//::PostMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGEREQUEST, (WPARAM)TRUE, (LPARAM)0x0409);

	//0x04090409 英语(美国) 美式键盘
	//0x08040804 ; 中文(中国) 简体中文-美式键盘
	//#DeFine WM_INPUTLANGCHANGEREQUEST 0x50当用户选择某种输入语言,或输入语言的热键改变
	
	//模拟发送按下'CTRL+SPACE'键的消息,切换到英文输入法
	/*keybd_event(17, 0, 0, 0);
	keybd_event(32, 0, 0, 0);
	keybd_event(17, 0, KEYEVENTF_KEYUP, 0);
	keybd_event(32, 0, KEYEVENTF_KEYUP, 0);*/

	// See MSDN LoadKeyboardLayout():
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646305(v=vs.85).aspx
	//调用API函数 LoadKeyboardLayout 把指定的输入法激活,该函数返回指定输入法的键盘布局句柄
	HKL keyboard = LoadKeyboardLayout(_T("00000409"), KLF_ACTIVATE);
	::ActivateKeyboardLayout(keyboard, 0);

	DWORD dwNewKeybLayout = 0x00000409; // Layout must be already loaded!
	//向当前的活动窗口发送切换输入法的消 息,DllCall("SendMessage", UInt, 活动窗口的句柄, UInt, 80, UInt, 1, UInt, 输入法键盘布局句柄) 。这样就完成了调用指定的输入法
	::PostMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)dwNewKeybLayout);
	::PostMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGE, 0, (LPARAM)dwNewKeybLayout);

 

LoadKeyboardLayout("00000409", KLF_ACTIVATE);
	DWORD dwNewKeybLayout = 0x00000409; // Layout must be already loaded!
	::PostMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)dwNewKeybLayout);
	::PostMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGE, 0, (LPARAM)dwNewKeybLayout);

 

http://www.360doc.com/content/18/0807/17/31081271_776401851.shtml

用AHK代码实现输入法的切换,和获取当前的输入法,这只是一个示例,你可以发挥把它做成更有用的程序,例如对指定的窗口使用指定的输入法,可以为 每个输入法设置一个单独的快捷键等。不过有个 BUG,当输入法切换到 微软输入法后,再获取当前输入法状态,会出错。不知道是不是我输入法的问题!

系统中已安装的输入法信息

0x04090409

在注册表 HKEY_USERS/.DEFAULT/Keyboard Layout/Preload 。里面只有输入法的键盘布局名称,如 E0040840 左E004说明该输入法的标识(智能ABC)右0804 说明该输入法为 中文输入法。其输入法的具体名称可到注册表HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control /Keyboard Layouts/ 输入法的键盘布局名称 。Layout Text键值为该输入法的名称。

 

//根据输入法名称查找输入法
 

 
//根据输入法代码查找输入法


注意:可能显示的已安装的输入法比任务栏的输入法列表少,你可以到HKEY_LOCAL_MACHINE/SYSTEM /CurrentControlSet/Control/Keyboard Layouts/ 找到所有在系统中已注册的输入法。

  • 调用指定输入法:首先需要获得输入法的键盘布局,再调用API函数 LoadKeyboardLayout 把指定的输入法激活,该函数返回指定输入法的键盘布局句柄,最后还需要向当前的活动窗口发送切换输入法的消 息,DllCall("SendMessage", UInt, 活动窗口的句柄, UInt, 80, UInt, 1, UInt,输入法键盘布局句柄) 。这样就完成了调用指定的输入法。
  • 切换输入法:与调用相类似,调用API函数 ActivateKeyboardLayout 完成 。当然最后也需要发送切换输入法的消息。函数 ActivateKeyboardLayout ( HKL hkl,UINT Flags);其中当参数 hkl 的值为 1 表示切换下一个输入法,0 为上一个输入法。
  • 获取当前的输入法:直接调用API函数 GetKeyboardLayout,该函数直接返回当前键盘布局的名称,如需要确切的输入法名字,到注册表中查找即可。
_dword System::GetKeyboardLanguage( )
{
	return GetLanguageID( (_dword) ::GetKeyboardLayout( 0 ) & 0x0000FFFF );
}

_bool System::SetKeyboardLanguage( _dword langid )
{
	StringPtr langname = L"00000409";

	switch ( langid )
	{
		case Language::LANGUAGE_ZH_CN:	langname = L"00000804"; break;
		case Language::LANGUAGE_ZH_TW:	langname = L"00000404"; break;
		case Language::LANGUAGE_DE_DE:	langname = L"00000407"; break;
		case Language::LANGUAGE_FR_FR:	langname = L"0000040C"; break;
		case Language::LANGUAGE_ES_ES:	langname = L"0000040A"; break;
		case Language::LANGUAGE_TR_TR:	langname = L"0000041F"; break;
	}

	HKL keyboard = ::LoadKeyboardLayout( langname, KLF_ACTIVATE );
	if ( keyboard == _null )
		return _false;

	::ActivateKeyboardLayout( keyboard, 0 );

	return _true;
}

代码如下:

Gui, Add, GroupBox, x6 y4 w230 h10 , 已安装的输入法(双击切换)
Gui, Add, ListView, r20 x6 y24 w230 h120 vListIME gSetIME ,序号|键盘布局|名称
Gui, Add, Button, x6 y144 w80 h30 gPreIME, 上一输入法
Gui, Add, Button, x156 y144 w80 h30 gNextIME, 下一输入法
Gui, Add, Button, x86 y144 w70 h30 gStateIME, 当前状态
; Generated using SmartGUI Creator 4.0
Gui, Show, x397 y213 h190 w247,输入法切换
Gosub,ReadIME
Return
GuiClose:
ExitApp
ReadIME:
LV_ModifyCol(3,300)
Loop,HKEY_USERS,.DEFAULT/Keyboard Layout/Preload, 1, 1
{
    RegRead,Layout
    RegRead,IMEName,HKEY_LOCAL_MACHINE,SYSTEM/CurrentControlSet/Control/Keyboard Layouts/%Layout%,Layout Text
    RegRead,Layout
     ListContent=%A_LoopRegName%丨%IMEName%丨 %Layout%
    LV_Insert(1,"Vis",A_LoopRegName,Layout,IMEName)
}
Return
StateIME:
Result:=DllCall("GetKeyboardLayout","int",0,UInt)
SetFormat, integer, hex
Result += 0
SetFormat, integer, D
MsgBox 当前键盘布局为 %Result%
return

SetIME:
If (A_GuiEvent<>"DoubleClick")
{
    Return
}
Gui,Submit,Nohide
LV_GetText(Layout,A_EventInfo,2)
;~ MsgBox %Layout%

SwitchIME(Layout)
Return

SwitchIME(dwLayout)
{
    DllCall("SendMessage", UInt, WinActive("A"), UInt, 80, UInt, 1, UInt, DllCall("LoadKeyboardLayout", Str, dwLayout, UInt, 1))
}

NextIME:
DllCall("SendMessage", UInt, WinActive("A"), UInt, 80, UInt, 1, UInt, DllCall("ActivateKeyboardLayout", UInt, 1, UInt, 256))
;-- 对当前窗口激活下一输入法
Return
PreIME:
DllCall("SendMessage", UInt, WinActive("A"), UInt, 80, UInt, 1, UInt, DllCall("ActivateKeyboardLayout", UInt, 0, UInt, 256))
;-- 对当前窗口激活上一输入法
Return

 

 

WM_INPUTLANGCHANGE:当窗口接收到WM_INPUTLANGCHANGE消息时,表示输入法发生了改变。
LoadKeyboardLayout:该函数给系统中装入一种新的键盘布局,可以同时装入几种不同的键盘布局,任一时刻仅有一个进程是活动的,装入多个键盘布局使得在多种布局间快速切换。
pwszKLID:缓冲区中的存放装入的键盘布局名称,名称是由语言标识符(低位字)和设备标识符(高位字)组成的十六进制值串,例如 U.S.英语对应的语言标识符为DX0409,则基本的U.S.英语键盘布局命名为“0000409”。U.S.英语键盘布局的变种(例如Dvorak布局)命名为“00010409”,“00020409”等。
  Flags:指定如何装入键盘布局,该参数可以是如下的值。
  KLF_ACTIVATE:若指定布局尚未装入,该函数为当前线程装入并激活它。

 

LoadKeyboardLayout doesn't change keyboard layout

Working with keyboard layout is rather tricky and it is different for Console and GUI applications. And LoadKeyboardLayout just make the layout "available" for activation unfortunately.

GUI

1.Application must have GetMessage, TranslateMessage, DispatchMessage cycle. (Sublime has GetMessage cycle)

2.Layout must be already loaded!

 LoadKeyboardLayout("00000409", KLF_ACTIVATE);

2.You may switch layout with two sequental calls

DWORD dwNewKeybLayout = 0x00000409; // Layout must be already loaded!
PostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)dwNewKeybLayout);
PostMessage(hWnd, WM_INPUTLANGCHANGE, 0, (LPARAM)dwNewKeybLayout);

 

::PostMessage是windows API 的函数。PostMessage是MFC的函数,其实就是对::PostMessage进行了一下封装。

::是c++作用域运算符。

PostMessage:不需要指定接收消息的窗口,但是消息只能发往本窗口
::PostMessage:需要指定接收消息的窗口,但是消息可以发往任意窗口

https://blog.csdn.net/weixin_33805557/article/details/94681168

 

Useful links

LoadKeyboardLayoutA function (c++)

https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-loadkeyboardlayouta?redirectedfrom=MSDN

ActivateKeyboardLayout