サイトのトップページへ リンクのページへ ロゴマーク - Delphi Programming
[掲載 2005年01月24日]  [更新 2015年09月26日] Delphi サンプルプログラム集
 
250_システム関係キーを無効化する
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
250_SystemKeyHook.zip [1,085 KB] 2015年09月26日版(EXE同梱)



  • 2010年08月21日
  • メッセージを表示するサンプルを追加してコード整備
  • 2010年08月24日
  • DLL にしないサンプルコードを追加
  • 2015年09月26日
  • DLL を使用しないローレベルのキーフックのサンプルだけとした
  • Windows 7 U64(SP1) + Delphi XE(UP1) Pro で動作確認




01_システム関係キーを無効化


キーフック関数の DLL を使用して,システム関係の以下のキー操作を無効にするサンプルです.動作はシステム全体に有効となっていて,実行開始時に無効化するようになっています.

  • [ALT] + [TAB] タスクの切換え
  • [VK_APPS]    アプリケーションキー(メニューキー)
  • [VK_LWIN]    左のウィンドウズ(スタートメニュー)キー
  • [VK_RWIN]    右のウィンドウズ(スタートメニュー)キー

システム関係のキー操作に,[Ctrl] + [Alt] + [Delete] がありますが,このサンプルで使用している低レベルキーフックでも無効にはできません.

低レベルのキーボードフックである WH_KEYBOARD_LL を使用しています.このフックは DLL にしなくてもグローバルフックを実現できます.




図1
設計時画面
  • [Stop KeyHook] で通常の動作に戻る

リスト1
グローバルキーフックを使用して,システム関係キーを無効化
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
 TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
 private
   { Private 宣言 }
 public
   { Public 宣言 }
 end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//-----------------------------------------------------------------------------
//  低レベルキーフックのキー情報構造体と定数
//-----------------------------------------------------------------------------
const
  LLKHF_EXTENDED = KF_EXTENDED shr 8;
  LLKHF_INJECTED = $00000010;
  LLKHF_ALTDOWN  = KF_ALTDOWN shr 8;
  LLKHF_UP       = KF_UP shr 8;
  LLMHF_INJECTED = $00000001;

type
  LPKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;
  tagKBDLLHOOKSTRUCT = record
    vkCode      : DWORD;
    scanCode    : DWORD;
    flags       : DWORD;
    time        : DWORD;
    dwExtraInfo : ULONG_PTR;
  end;
  KBDLLHOOKSTRUCT  = tagKBDLLHOOKSTRUCT;
  TKbDllHookStruct = KBDLLHOOKSTRUCT;
  PKbDllHookStruct = LPKBDLLHOOKSTRUCT;

var
  KeyHookHandle : HHOOK=0;

//-----------------------------------------------------------------------------
//  フックのコールバック関数
//
//  [ALT]+[TAB] タスク切換え
//  [VK_APPS]   アプリケーションキー
//  [VK_LWIN]   左側のウィンドウズ(メニュー)キー
//  [VK_RWIN]   右側のウィンドウズ(メニュー)キー
//
//  の本来動作を無効(Result := 1 0以外)にする
//-----------------------------------------------------------------------------
function LowLevelKeyProc(Code:Integer; wPar:wParam; lPar:LParam): LRESULT; stdcall;
var
  Lkbdll : PKBDLLHOOKSTRUCT;
begin
  Result := 0;

  if Code < 0 then begin
    Result := CallNextHookEx(KeyHookHandle, Code, wPar, lPar);
    exit;
  end;

  Lkbdll := Pointer(lPar);
  if Code = HC_ACTION then begin

    //キーが[VK_TAB]+[ALT]の時はResult := 1にしてここで処理終了
    if (Lkbdll.vkCode in [VK_TAB]) and
       ((Lkbdll.flags and (LLKHF_ALTDOWN) <> 0)) then begin
      Result := 1;
    end;

    //[VK_XXX]のリストのキーの時はResult := 1にしてメッセージ処理終了
    if (Lkbdll.vkCode in [VK_APPS, VK_LWIN, VK_RWIN]) then begin
      Result := 1;
    end;
  end;

  if Result = 0 then begin
    Result := CallNextHookEx(KeyHookHandle, Code, wPar, lPar);
  end;
end;

//=============================================================================
//  フック開始
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1Click(nil);
end;

//=============================================================================
//  終了時にはキーフックを解除
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  Button2Click(nil);
end;

//=============================================================================
//  キーフック開始.フック内で指定キーの本来動作を無効にする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if KeyHookHandle = 0 then begin
    KeyHookHandle := SetWindowsHookEx(WH_KEYBOARD_LL,
                                      @LowLevelKeyProc,
                                      HInstance,
                                      0);
  end;
end;

//=============================================================================
//  キーフックを解除して通常の動作に戻す
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if KeyHookHandle <> 0 then begin
    UnhookWindowsHookEx(KeyHookHandle);
    KeyHookHandle := 0;
  end;
end;

end.




02_システム関係キーを無効化  -  メッセージを表示


上のサンプルでは,無効にしたキーを押すと,もちろん何も起こりません.これは通常のウィンドウズの動作と違っています.ユーザを惑わせないためには,情報表示のダイアログを表示するといい場合もあります.
このサンプルでは,指定した無効キーを押下すると,ダイアログを表示するようにしてみました.
ダイアログを表示すると,フォーカスを持つウインドウが,本サンプルのウィンドウになってしまいます.そこで,ダイアログを閉じたら直前に入力フォーカスがあったウィンドウを最前面に表示するようにしています.この処理に AttachThreadInput 関数を使用した,スレッドアタッチという手法を用いています.



図2
実行時の画面
  • 無効化したキーを押すと,このダイアログを表示する
  • [Stop KeyHook] で通常の動作に戻る

リスト2
グローバルキーフックを使用して,システム関係キーを無効化
無効化したキーを押下したらメッセージを表示
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
 TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
 private
   { Private 宣言 }
   procedure WMAppMessage(var Message: TMessage); message WM_APP+100;
 public
   { Public 宣言 }
 end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//-----------------------------------------------------------------------------
//  低レベルキーフックのキー情報構造体と定数
//-----------------------------------------------------------------------------
const
  LLKHF_EXTENDED = KF_EXTENDED shr 8;
  LLKHF_INJECTED = $00000010;
  LLKHF_ALTDOWN  = KF_ALTDOWN shr 8;
  LLKHF_UP       = KF_UP shr 8;
  LLMHF_INJECTED = $00000001;

type
  LPKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;
  tagKBDLLHOOKSTRUCT = record
    vkCode      : DWORD;
    scanCode    : DWORD;
    flags       : DWORD;
    time        : DWORD;
    dwExtraInfo : ULONG_PTR;
  end;
  KBDLLHOOKSTRUCT  = tagKBDLLHOOKSTRUCT;
  TKbDllHookStruct = KBDLLHOOKSTRUCT;
  PKbDllHookStruct = LPKBDLLHOOKSTRUCT;

var
  KeyHookHandle : HHOOK=0;

//-----------------------------------------------------------------------------
//  フックのコールバック関数
//
//  [ALT]+[TAB] タスク切換え
//  [VK_APPS]   アプリケーションキー
//  [VK_LWIN]   左側のウィンドウズ(メニュー)キー
//  [VK_RWIN]   右側のウィンドウズ(メニュー)キー
//
//  の本来動作を無効(Result := 1 0以外)にする
//-----------------------------------------------------------------------------
function LowLevelKeyProc(Code:Integer; wPar:wParam; lPar:LParam): LRESULT; stdcall;
var
  Lkbdll : PKBDLLHOOKSTRUCT;
begin
  Result := 0;

  if Code < 0 then begin
    Result := CallNextHookEx(KeyHookHandle, Code, wPar, lPar);
    exit;
  end;

  Lkbdll := Pointer(lPar);
  if Code = HC_ACTION then begin

    //キーが[VK_TAB]+[ALT]の時はResult := 1にしてここで処理終了
    if (Lkbdll.vkCode in [VK_TAB]) and
       ((Lkbdll.flags and (LLKHF_ALTDOWN) <> 0)) then begin
      Result := 1;
    end;

    //[VK_XXX]のリストのキーの時はResult := 1にしてメッセージ処理終了
    if (Lkbdll.vkCode in [VK_APPS, VK_LWIN, VK_RWIN]) then begin
      Result := 1;
    end;

    if Result = 1 then begin
      PostMessage(Form1.Handle, WM_APP+100, wPar, lPar);
    end;
  end;

  if Result = 0 then begin
    Result := CallNextHookEx(KeyHookHandle, Code, wPar, lPar);
  end;
end;

//=============================================================================
//  フック開始
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1Click(nil);
end;

//=============================================================================
//  終了時にはキーフックを解除
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  Button2Click(nil);
end;

//=============================================================================
//  キーフック開始.フック内で指定キーの本来動作を無効にする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if KeyHookHandle = 0 then begin
    KeyHookHandle := SetWindowsHookEx(WH_KEYBOARD_LL,
                                      @LowLevelKeyProc,
                                      HInstance,
                                      0);
  end;
end;

//=============================================================================
//  キーフックを解除して通常の動作に戻す
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if KeyHookHandle <> 0 then begin
    UnhookWindowsHookEx(KeyHookHandle);
    KeyHookHandle := 0;
  end;
end;

{$WRITEABLECONST ON}
//-----------------------------------------------------------------------------
//  フックのコールバック関数から送られてきたメッセージの処理
//-----------------------------------------------------------------------------
procedure TForm1.WMAppMessage(var Message: TMessage);
const
  LShowFlag : Boolean = False;
var
  LForegroundWnd : HWND;
  LThreadForeID  : Cardinal;
  LThreadSelfID  : Cardinal;
  LMsgText       : String;
begin
  //以下の処理実行中はフックからのメッセージ処理はしないためのフラグ
  if LShowFlag = False then begin
    LShowFlag := True;

    //現在の前面ウィンドウのバンドル取得
    LForegroundWnd := GetForegroundWindow;
    if LForegroundWnd <> Handle then begin
      //そのスレッドIDを取得
      LThreadForeID := GetWindowThreadProcessId(LForegroundWnd);
      //前面表示するウィンドウのスレッドIDを取得
      LThreadSelfID := GetCurrentThreadId;
      //スレッドのアタッチ
      AttachThreadInput(LThreadSelfID, LThreadForeID, True);
      //アタッチしたハンドルのウィドウを前面に
      SetForegroundWindow(Handle);
      //終了したらアタッチを切り離す
      AttachThreadInput(LThreadSelfID, LThreadForeID, False);
    end;

    LMsgText := '申し訳ありませんが、'+ sLineBreak
              + 'このキーはご利用になれません.';
    MessageBox(Handle, PChar(LMsgText), '情報', MB_ICONINFORMATION);

    //直前のアプリにフォーカスを戻す
    SetForegroundWindow(LForegroundWnd);
    LShowFlag := False;
  end;
end;
{$WRITEABLECONST OFF}

end.