yury_finkel (yury_finkel) wrote,
yury_finkel
yury_finkel

Переключалка раскладок клавиатуры для Windows на AutoHotKey

Итак, объясняю, как под виндой поймать переключение языка ввода любого (в том числе и консольного) окна.

Ставится глобальный хук на события WH_SHELL (а раз он глобальный, то это необходимо делать в dll). Когда код события равен HSHELL_LANGUAGE, это значит, произошло переключение языка в окне, при этом в lParam содержится код языка (а в wParam — дескриптор окна, но он нам не нужен). Чтобы передать эту информацию нашему приложению (напомню, что переключение языка происходит в другом приложении, поэтому никакие глобальные переменные не помогут), можно просто послать широковещательное сообщение с wParam и lParam в качестве параметров, а наше приложение должно будет поймать это сообщение и предпринять необходимые действия (например, отобразить флаг, соответствующий языку, в трее).

Привожу полностью код dll на Delphi (компилируется также Lazarus'ом). Можно и на C написать, но мне удобнее так.


hooksh.dpr:

{$ifdef fpc}
{$MODE DELPHI}
{$endif}

library hook;

uses Windows, Messages, SysUtils;

var
  mtHook: THandle;
  uiWM_CapturedKeyboardLayout: UINT;

function HookProc (code: Integer; w: WParam; l: LParam): Longint; stdcall;
  var dwRecipients: DWORD;
begin
  if (code = HSHELL_LANGUAGE) then
  begin
    dwRecipients := BSM_APPLICATIONS;
    BroadcastSystemMessage(BSF_POSTMESSAGE, @dwRecipients, uiWM_CapturedKeyboardLayout, w, l);
  end;

  result := CallNextHookEx (mtHook, code, w, l);
end;

procedure RemoveHook; stdcall;
begin
  if mtHook=0 then exit;
  if UnHookWindowsHookEx(mtHook) then
    mtHook:=0;
end;

function SetHook(): THandle; stdcall;
begin
    RemoveHook;
    mtHook := SetWindowsHookEx(WH_SHELL, @HookProc, hInstance, 0);
    result := mtHook;
end;

procedure DllEntryPoint(dwReason : DWORD);
begin
  case dwReason of
    Dll_Process_Attach:
      begin
        uiWM_CapturedKeyboardLayout := RegisterWindowMessage('KBHook_WM_CapturedKeyboardLayout');
      end;
    Dll_Process_Detach:
      begin
      end;
  end;
end;

procedure Process_Detach_Hook(dllparam: longint);
begin
  DLLEntryPoint(DLL_PROCESS_DETACH);
end;

procedure Thread_Attach_Hook(dllparam: longint);
begin
  DLLEntryPoint(DLL_THREAD_ATTACH);
end;

procedure Thread_Detach_Hook(dllparam: longint);
begin
  DLLEntryPoint(DLL_THREAD_DETACH);
end;

exports SetHook, RemoveHook;

begin
    mtHook:=0;
{$ifdef fpc}
    Dll_Process_Detach_Hook:= @Process_Detach_Hook;
    Dll_Thread_Attach_Hook := @Thread_Attach_Hook;
    Dll_Thread_Detach_Hook := @Thread_Detach_Hook;
{$else }
    DLLProc:= @DLLEntryPoint;
{$endif}
    DllEntryPoint(Dll_Process_Attach);
end.


В скрипте на AutoHotKey это событие можно обработать, например, так (в начале идёт инициализация dll):


Фрагмент cxswitch.ahk:

if (A_PtrSize = 8) {
  hModuleShell := DllCall("LoadLibrary", Str, "hooksh64.dll")
  hhs := DllCall("hooksh64\SetHook")
} else {
  hModuleShell := DllCall("LoadLibrary", Str, "hooksh32.dll")
  hhs := DllCall("hooksh32\SetHook")
}

MsgNum := DllCall("RegisterWindowMessage", Str, "KBHook_WM_CapturedKeyboardLayout" )
OnMessage(MsgNum, "ShellMessage")

OnExit, Unhook

Return

ShellMessage(wParam,lParam) {
  global setkbd
  setkbd := lParam
  //...
}

Unhook:
  DllCall("UnhookWindowsHookEx", "Ptr", hhs)
  DllCall("FreeLibrary", "Ptr", hModuleShell)
ExitApp



Полностью исходный код моей самопальной переключалки клавиатуры можно скачать здесь, а работающие скомпилированные бинарники (для 32- и 64-разрядной винды) — здесь. Переключалка клавиатуры работает не по циклическому принципу, а по принципу «одна клавиша — один язык». У меня, например, на левый Shift назначен английский, а на правый — русский. (Кстати, срабатывает только короткое нажатие — я специально так сделал во избежание ошибочных нажатий). Флаги языков и звуки переключения можно добавлять самому, просто назвав соответствующие файлы по коду языка (например, enu.ico, enu.wav и rus.ico, rus.wav). Настройки — в файле cxswitch.ini. Кроме того, в переключалку встроен ввод эсперантских букв с диакритиками (включается и выключается по Alt+Ctrl+Space), ввод кавычек-лапок по RAlt+< / RAlt+> и длинного тире по RAlt+-, а также ввод некоторых европейских диакритик, например, «a RAlt+'» даёт «á» (если включен режим «Диакритика», см. меню в трее).

В целом, всё сделано так, как удобно лично мне, если кому-то что-то не нравится — исходники открыты.
Tags: #эсперанто, autohotkey, winapi, клавиатура, компьютеры, программазм, эсперанто, языки
Subscribe

  • Post a new comment

    Error

    Comments allowed for friends only

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 2 comments