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 で動作確認
|
キーフック関数の DLL を使用して,システム関係の以下のキー操作を無効にするサンプルです.動作はシステム全体に有効となっていて,実行開始時に無効化するようになっています.
- [ALT] + [TAB] タスクの切換え
- [VK_APPS] アプリケーションキー(メニューキー)
- [VK_LWIN] 左のウィンドウズ(スタートメニュー)キー
- [VK_RWIN] 右のウィンドウズ(スタートメニュー)キー
システム関係のキー操作に,[Ctrl] + [Alt] + [Delete] がありますが,このサンプルで使用している低レベルキーフックでも無効にはできません.
低レベルのキーボードフックである WH_KEYBOARD_LL を使用しています.このフックは DLL にしなくてもグローバルフックを実現できます.
|
 |
図1
設計時画面
|
リスト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.
|