こんにちは。
右コントロールキーと、カーソルキーの組み合わせで
ホットキーを動作させたいと思いました。
なぜかというと、dynabook UX というネットブックのFnキーの位置が
かっこよかったので、
(ダイナブックシリーズは右下にFnというのが採用されているようですね)
それを自分のノートPCでも、模擬的にまねできないかと思ったからです。
Ctrl+↑・↓、を、PageUp・PageDown、に割り当て
Ctrl+←・→、を、Home・Endキーに割り当てたいと考えています。
右にあるCtrlキーだけです。
ところが、
ホットキーは、Ctrlキーの左右を別にして登録などできない(らしい)ので
なかなか難しく実現できていません。
上手なやり方をご存じでしたら、教えてください。
次のコードを書いたのですが、うまく動きません。
-----
unit Unit12;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm12 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
private
procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;
public
{ Public declarations }
end;
var
Form12: TForm12;
implementation
{$R *.dfm}
procedure SetHotKeyToOS(Handle: THandle; HotKeyNumber: Integer; VirtualKey: Word;
Shift, Ctrl, Alt, Win: Boolean);
var
Modifiers: Word;
begin
Modifiers := 0;
if Shift then Modifiers := Modifiers or MOD_SHIFT;
if Ctrl then Modifiers := Modifiers or MOD_CONTROL;
if Alt then Modifiers := Modifiers or MOD_ALT;
if Win then Modifiers := Modifiers or MOD_WIN;
RegisterHotKey(Handle, HotKeyNumber, Modifiers, VirtualKey);
end;
function GetAsyncKeyState_Delphi(iKey: Integer): Boolean;
begin
Result := BOOL(Hi(GetAsyncKeyState(iKey)));
end;
procedure TForm12.WMHotKey(var Msg: TWMHotKey);
begin
case Msg.HotKey of
1: begin
if GetAsyncKeyState_Delphi(VK_RCONTROL) then
begin
//keybd_event(VK_RCONTROL, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_PRIOR , 0, 0, 0);
keybd_event(VK_PRIOR , 0, KEYEVENTF_KEYUP, 0);
//keybd_event(VK_RCONTROL, 0, 0, 0);
end else
begin
UnregisterHotKey(Self.Handle, 1);
keybd_event(VK_CONTROL, 0, 0, 0);
keybd_event(VK_UP , 0, 0, 0);
keybd_event(VK_UP , 0, KEYEVENTF_KEYUP, 0);
SethotKeyToOS(Self.Handle, 1, VK_UP, False, True, False, False);
end;
end;
2: begin
if GetAsyncKeyState_Delphi(VK_RCONTROL) then
begin
//keybd_event(VK_RCONTROL, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_NEXT , 0, 0, 0);
keybd_event(VK_NEXT , 0, KEYEVENTF_KEYUP, 0);
//keybd_event(VK_RCONTROL, 0, 0, 0);
end else
begin
UnregisterHotKey(Self.Handle, 2);
keybd_event(VK_CONTROL, 0, 0, 0);
keybd_event(VK_DOWN , 0, 0, 0);
keybd_event(VK_DOWN , 0, KEYEVENTF_KEYUP, 0);
SethotKeyToOS(Self.Handle, 2, VK_DOWN, False, True, False, False);
end; end;
end;
end;
procedure TForm12.FormCreate(Sender: TObject);
begin
SethotKeyToOS(Self.Handle, 1, VK_UP, False, True, False, False);
SethotKeyToOS(Self.Handle, 2, VK_DOWN, False, True, False, False);
end;
procedure TForm12.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if CanClose then
begin
UnregisterHotKey(Self.Handle, 1);
UnregisterHotKey(Self.Handle, 2);
end;
end;
end.
-----
動作としては、左コントロールキー+上下キーなら、HOTKETが効いて
WMHOTKEYに処理がきて
改めてコード上で左コントロールキー+上下キーを送信しています。
こちらはうまくうごいています。
右コントロールキー+上下キーの場合
ページアップ、ダウンだけを送りたいのに
コードで制御しようとしても、
コントロールキー+ページアップダウンになってしまいます。
keybd_eventが、VK_RCONTROLとVK_LCONTROLとを
区別出来ないようなのですが、
何かよい解決策をご存知でしたら教えてください。
よろしくお願いします。
いろいろ工夫したんですが、うまくいかないんですよね....
GetKeyState()でキーの状態が取れると思いますが・・・
if GetKeyState( VK_RCONTROL ) and $8000 <> 0 then begin
ShowMessage('R-CONTROL');
end;
http://delfusa.main.jp/delfusafloor/archive/www.nifty.ne.jp_forum_fdelphi/faq/00259.htm
↑
参考になりませんか?
ごめんなさい。
VK_RCONTROLが拾えないんじゃなかったですね。
勘違いしてしまいました。
そうなんですよ。
右コントロールキーは拾えるのですが、それによって
そのキーの押しっぱなし状態を解除したいのです。
右Ctrl+[↑の連打]でも、PageUpの連打、と
同じ機能にできたらいいなと、思うのですが、なかなか難しい。
でもまあ、キーの同時押し判定にはAsyncでないGetKeyStateを使うのが正しいですね。
ついでに、keybd_eventはもう古い、NT以降はSendInputを使えということになっているようですが、そちらは試してみたでしょうか?
> keybd_eventが、VK_RCONTROLとVK_LCONTROLとを区別出来ないようなのですが
実際そうだとしたら、第2引数のスキャンコードを指定してみるのはどうでしょう。
(キーコードに対するスキャンコードはMapVirtualKeyで得られます)
あとは、右Ctrlは後から追加キーだからKEYEVENTF_EXTENDEDKEYを指定するといいよ、という説もあるようですね。
どちらかというと、現にキーが押されているのにkeybd_eventで「押されていない」ことにできるのかどうか、という点が気になりますが……
貴重な情報ありがとうございます。
>キーの同時押し判定にはAsyncでないGetKeyStateを使うのが正しいですね。
これは、何か、情報源があるのでしょうか。
どっちを使うといいとか。古いOS向けとかなんとか,,,
> ついでに、keybd_eventはもう古い、NT以降はSendInputを使えということになっているようですが、そちらは試してみたでしょうか?
いえ、全然知りませんでした。
・・・で・・・も!
KEYEVENTF_EXTENDEDKEY
これを、追加してみたら見事に動きました!!
ありがとうございます。
ソースは
function GetAsyncKeyState_Delphi(iKey: Integer): Boolean;
begin
Result := BOOL(Hi(GetAsyncKeyState(iKey)));
end;
function GetKeyState_Delphi(VKey: Integer): Boolean;
begin
Result := (GetKeyState( VKey ) and $8000 <> 0)
end;
procedure TForm12.WMHotKey(var Msg: TWMHotKey);
begin
case Msg.HotKey of
1: begin
if GetKeyState_Delphi(VK_RCONTROL) then
begin
keybd_event(VK_RCONTROL, 0, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0);
keybd_event(VK_PRIOR , 0, 0, 0);
keybd_event(VK_PRIOR , 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_RCONTROL, 0, KEYEVENTF_EXTENDEDKEY, 0);
end else
begin
UnregisterHotKey(Self.Handle, 1);
keybd_event(VK_UP , 0, 0, 0);
keybd_event(VK_UP , 0, KEYEVENTF_KEYUP, 0);
SethotKeyToOS(Self.Handle, 1, VK_UP, False, True, False, False);
end;
end;
2: begin
if GetKeyState_Delphi(VK_RCONTROL) then
begin
keybd_event(VK_RCONTROL, 0, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0);
keybd_event(VK_NEXT , 0, 0, 0);
keybd_event(VK_NEXT , 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_RCONTROL, 0, KEYEVENTF_EXTENDEDKEY, 0);
end else
begin
UnregisterHotKey(Self.Handle, 2);
keybd_event(VK_DOWN , 0, 0, 0);
keybd_event(VK_DOWN , 0, KEYEVENTF_KEYUP, 0);
SethotKeyToOS(Self.Handle, 2, VK_DOWN, False, True, False, False);
end; end;
end;
end;
このようにすると、
左Ctrl+↑↓なら、そのまま、Ctrl+↑↓の機能
右Ctrl+↑↓なら、PgUp PgDn の機能
が実現できました!
KEYEVENTF_EXTENDEDKEYは知っていたのですが、うっかりしていましたよ。
ありがとうございます。
GetAsyncKeyState_DelphiとGetKeyState_Delphiは
この場合では、どちらも同じ動作をしています。
解決したようで何よりです。
>>キーの同時押し判定にはAsyncでないGetKeyStateを使うのが正しいですね。
>これは、何か、情報源があるのでしょうか。
>どっちを使うといいとか。古いOS向けとかなんとか,,,
一応、MSDNのGetKeyStateの所で解説されてはいますが、
あれだけの説明では必然性が伝わりにくいかもしれませんね…
えーと、ユーザがキーやマウスを操作すると
(1) イベントがメッセージキューに入る
(2) アプリケーションがメッセージを取り出す
(3) メッセージハンドラに渡される
という流れで処理されます。
GetKeyStateでは(1)と一緒に保存された状態がとれますが、
Asyncだと関数を呼んだ時点、つまり(3)の状態になるので
タイミング次第では実際に行われた操作と異なる判定をしてしまいます。
(例えば 1+2=3 とタイプしたはずなのに 1;"-# になるといったことが起こります)
メッセージハンドラというのが、微妙にわかりませんが
PostMessageとSendMessageの違いだかなんだかみたいなもんですかね。
GetKeyStateを使っていっておきます。
解決チェックも忘れていました。
非常に、ありがとうございます。
ツイート | ![]() |