2010年7月23日

Windowsのシステムキーコンビネーションを無効にする

全画面で動作するようなアプリケーションなどでは、[Win]、[Ctrl]+[ESC]、[ALT]+[TAB]、[ALT]+[ESC]、[Ctrl]+[Shift]+[ESC]、[ALT]+[F4]のようなWindowsのシステムキーコンビネーションを無効にしたいような状況があります。そのような場合はキーボードを低レベルフックします。フックの設定はSetWindowsHookEx(ja)で、解除はUnhookWindowsHookEx(ja)で行い、フック関数はLowLevelKeyboardProc(ja)に従って作成します。フック関数が呼び出されるときにlParamにはKBDLLHOOKSTRUCT構造体へのポインタが格納されており、この中のvkCodeで押されたキーの仮想キーコードを、flagsのLLKHF_ALTDOWNビット(bit5)で[ALT]キーが押されているかどうかを、それぞれ知ることができます。ここで無効化したいキーだった場合は戻値を1としてそのままリターンします。一方でそのままにしたいキーだった場合はCallNextHookEx(ja)で次のフックチェーンに処理を委ねる必要があります。また[ALT]以外のキー([Ctrl]や[Shift]など)とのコンビネーションを知りたい場合はGetAsyncKeyState(ja)で取得します(bit15が現在そのキーが押されているかどうかを示しています)。
まずキーボードの低レベルフックに必要な定義を行います。
uses
  Windows;

type
  LPKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;
  {$EXTERNALSYM LPKBDLLHOOKSTRUCT}
  tagKBDLLHOOKSTRUCT = record
    vkCode: DWORD;
    scanCode: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: Pointer;
  end;
  {$EXTERNALSYM tagKBDLLHOOKSTRUCT}
  KBDLLHOOKSTRUCT = tagKBDLLHOOKSTRUCT;
  {$EXTERNALSYM KBDLLHOOKSTRUCT}
  TKbDllHookStruct = KBDLLHOOKSTRUCT;
  PKbDllHookStruct = LPKBDLLHOOKSTRUCT;

const
  WH_KEYBOARD_LL = 13;
  {$EXTERNALSYM WH_KEYBOARD_LL}

  LLKHF_EXTENDED = KF_EXTENDED shr 8;
  {$EXTERNALSYM LLKHF_EXTENDED}
  LLKHF_INJECTED = $00000010;
  {$EXTERNALSYM LLKHF_INJECTED}
  LLKHF_ALTDOWN  = KF_ALTDOWN shr 8;
  {$EXTERNALSYM LLKHF_ALTDOWN}
  LLKHF_UP       = KF_UP shr 8;
  {$EXTERNALSYM LLKHF_UP}

次にフック関数を用意します。
type
  TSystemKeyCombination  = (skLWin,          // [WIN] (Left)             - Open Start menu
  skRWin,          // [WIN] (Right)            - Open Start menu
  skCtrlEsc,       // [CTRL] + [ESC]           - Open Start menu
  skAltTab,        // [ALT] + [TAB]            - Switch programs
  skAltEsc,        // [ALT] + [ESC]            - Next program
  skCtrlShiftEsc,  // [CTRL] + [SHIFT] + [ESC] - Open task manager
  skAltF4);        // [ALT] + [F4]             - Quit program
  TSystemKeyCombinations = set of TSystemKeyCombination;

var
  hh: HHOOK;
  InvalidCombinations: TSystemKeyCombinations;

function LowLevelKeyboardProc(nCode: Integer;
                              wP: WPARAM; lP: LParam): LRESULT; stdcall; export;
var
  pkbhs: PKbDllHookStruct;
  CtrlKey: Boolean;
  ShiftKey: Boolean;
begin

  pkbhs := PKbDllHookStruct(lP);

  case nCode of
    HC_ACTION:
    begin
      case pkbhs^.vkCode of
        VK_LWIN:
        begin
          if skLWin in InvalidCombinations then
          begin
            { LWIN }
            Result := 1;
            Exit;
          end;
        end;

        VK_RWIN:
        begin
          if skRWin in InvalidCombinations then
          begin
            { RWIN }
            Result := 1;
            Exit;
          end;
        end;

        VK_TAB:
        begin
          if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and
             (skAltTab in InvalidCombinations) then
          begin
            { ALT + TAB }
            Result := 1;
            Exit;
          end;
        end;

        VK_ESCAPE:
        begin
          CtrlKey  := ((GetAsyncKeyState(VK_CONTROL) and $8000) <> 0);
          ShiftKey := ((GetAsyncKeyState(VK_SHIFT)   and $8000) <> 0);

          if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and
             (skAltEsc in InvalidCombinations) then
          begin
            { ALT + ESC }
            Result := 1;
            Exit;
          end;

          if ShiftKey = False then
          begin
            if (CtrlKey = True) and (skCtrlEsc in InvalidCombinations) then
            begin
              { CTRL + ESC }
              Result := 1;
              Exit;
            end;
          end
          else
          begin
            if (CtrlKey = True) and (skCtrlShiftEsc in InvalidCombinations) then
            begin
              { CTRL + SHIFT + ESC }
              Result := 1;
              Exit;
            end;
          end;
        end;

        VK_F4:
        begin
          if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and
             (skAltF4 in InvalidCombinations) then
          begin
            { ALT + F4 }
            Result := 1;
            Exit;
          end;
        end;
      end;
    end;
  end;

  Result := CallNextHookEx(hh,nCode,wP,lP);

end;

ここでは集合型のInvalidCombinationsに含まれるキーコンビネーションであればResultを1にしてそのままリターン、そうでなければCallNextHookExでフックチェーンの呼び出しを行うようにしています。
最後にキーボードの低レベルフックを設定、解除する関数は
procedure HookKeyboardLL;
begin

  if hh <> 0 then
  begin
    Exit;
  end;

  hh := SetWindowsHookEx(WH_KEYBOARD_LL,LowLevelKeyboardProc,HInstance,0);

end;

procedure UnhookKeyboardLL;
begin

  if hh = 0 then
  begin
    Exit;
  end;

  UnhookWindowsHookEx(hh);
  hh := 0;

end;

と単にSetWindowsHookExおよびUnhookWindowsHookExを呼び出すだけです。
なお[Ctrl]+[ALT]+[Del]のキーストロークだけはキーボードの低レベルフックでも捕捉できません(GINAの置き換えが必要になります)。

0 件のコメント: