HotKeyを使って右コントロールキー上下でPageUpDownさせたい

解決


Fusa  URL  2009-12-02 22:21:30  No: 36362

こんにちは。

右コントロールキーと、カーソルキーの組み合わせで
ホットキーを動作させたいと思いました。

なぜかというと、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とを
区別出来ないようなのですが、
何かよい解決策をご存知でしたら教えてください。
よろしくお願いします。

いろいろ工夫したんですが、うまくいかないんですよね....


GTR  2009-12-03 00:11:18  No: 36363

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

参考になりませんか?


GTR  2009-12-03 00:23:43  No: 36364

ごめんなさい。
VK_RCONTROLが拾えないんじゃなかったですね。
勘違いしてしまいました。


Fusa  2009-12-03 02:06:39  No: 36365

そうなんですよ。
右コントロールキーは拾えるのですが、それによって
そのキーの押しっぱなし状態を解除したいのです。

右Ctrl+[↑の連打]でも、PageUpの連打、と
同じ機能にできたらいいなと、思うのですが、なかなか難しい。


ttt  2009-12-03 02:46:53  No: 36366

でもまあ、キーの同時押し判定にはAsyncでないGetKeyStateを使うのが正しいですね。
ついでに、keybd_eventはもう古い、NT以降はSendInputを使えということになっているようですが、そちらは試してみたでしょうか?

> keybd_eventが、VK_RCONTROLとVK_LCONTROLとを区別出来ないようなのですが
実際そうだとしたら、第2引数のスキャンコードを指定してみるのはどうでしょう。
(キーコードに対するスキャンコードはMapVirtualKeyで得られます)
あとは、右Ctrlは後から追加キーだからKEYEVENTF_EXTENDEDKEYを指定するといいよ、という説もあるようですね。

どちらかというと、現にキーが押されているのにkeybd_eventで「押されていない」ことにできるのかどうか、という点が気になりますが……


Fusa  URL  2009-12-03 09:18:40  No: 36367

貴重な情報ありがとうございます。

>キーの同時押し判定には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は
この場合では、どちらも同じ動作をしています。


ttt  2009-12-03 10:41:33  No: 36368

解決したようで何よりです。

>>キーの同時押し判定にはAsyncでないGetKeyStateを使うのが正しいですね。
>これは、何か、情報源があるのでしょうか。
>どっちを使うといいとか。古いOS向けとかなんとか,,,

一応、MSDNのGetKeyStateの所で解説されてはいますが、
あれだけの説明では必然性が伝わりにくいかもしれませんね…

えーと、ユーザがキーやマウスを操作すると
(1) イベントがメッセージキューに入る
(2) アプリケーションがメッセージを取り出す
(3) メッセージハンドラに渡される
という流れで処理されます。
GetKeyStateでは(1)と一緒に保存された状態がとれますが、
Asyncだと関数を呼んだ時点、つまり(3)の状態になるので
タイミング次第では実際に行われた操作と異なる判定をしてしまいます。
(例えば 1+2=3 とタイプしたはずなのに 1;"-# になるといったことが起こります)


Fusa  URL  2009-12-03 11:18:40  No: 36369

メッセージハンドラというのが、微妙にわかりませんが
PostMessageとSendMessageの違いだかなんだかみたいなもんですかね。

GetKeyStateを使っていっておきます。
解決チェックも忘れていました。

非常に、ありがとうございます。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加