別アプリへの仮想キーを送るには?

解決


slash  2005-09-11 00:51:16  No: 17508  IP: 192.*.*.*

ホットキーのようにあるボタンを押すと同時に二つのキーを押すにはどうすればいいのでしょうか?
*特定のアプリではなく、手前に来てる別のアプリに反映させたいのですが。。

if GetAsyncKeyState(VK_NONCONVERT)<>0 then
begin
keybd_event(Ord('1'), 0, 0, 0);
keybd_event(Ord('2'), 0, 0, 0);
keybd_event(Ord('2'), 0, KEYEVENTF_KEYUP, 0);
keybd_event(Ord('1'), 0, KEYEVENTF_KEYUP, 0);

上のですとForm上でしか対応できません。
GetForegroundWindowの使い方も分からないので出来れば詳しく教えていただくとありがたいです。

編集 削除
Mr.XRAY  URL  2005-09-11 01:19:44  No: 17509  IP: 192.*.*.*

>*特定のアプリではなく、手前に来てる別のアプリに反映させたいのですが。。

この別のアプリというのは,
(1) 自分がDelphiで開発した
(2) 他人がDelphiで開発した
(3) 何の言語で開発したかわらないアプリ

のどれでしょう.また,Delphiのバージョンは,開発のWindowsは?

いずれにしても,大変むずかしいです.
ちょっと考えてみて下さい.まず,キーコードを送る時に,もし送る側の
ボタンクリックで行うとすると,このアプリが前面にきてしまいます.
この時,手前のアプリというのは何なのか,ということになります.

では,常に手前の,ということになると,PC(パソコン)が起動している
間,ずっと,キーが押されたのを監視しなければなりません.
そういう仕組みが必要です.

もう少し具体的に,どんな動作を望んでいるのでしょか.

編集 削除
slash  2005-09-11 05:59:35  No: 17510  IP: 192.*.*.*

Mr.XRAYさん早速返信ありがとうございます。

すいません。今考えたらちょっと別物かもしれませんが
アプリに送るというよりは、
キーボードの'A'を押したら'A'とでる所を
'A'を押したら別の'AB'と出力ような感じです。

猫まねきというフリーソフトに似ていますが、
二つのキーを押す作業ができないので自分で作ろうかと思っているところです。

猫まねき
ttp://www.remus.dti.ne.jp/~kurotora/

編集 削除
Mr.XRAY  URL  2005-09-11 12:49:55  No: 17511  IP: 192.*.*.*

>二つのキーを押す作業ができないので

この意味がよく理解できませんが.つまり

>'A'を押したら別の'AB'と出力ような感じです。

あるキーを押したら,そのキーを判断して,ある処理を行う
ということですね.例えば'A'を押したら'AB'にしてしまいうとか.
任意のアプリでキーの検出を行いたいということですから,二つも三つ
も関係ありません.猫まねきもそうですよね.

これは,プログラムとしては難しい部類になると思います.
これを実現するためには,システム全体でキーの押下を検出する必要が
あります.これをフック(操作や動作の盗み取り)といいます.フック関数
というものを使用します.

フックしてどんなキーが押下されたかを検出するだけならば,比較的簡単
にできますが,その操作内容を変更して,目的のアプリに送るにはDLLと
いう別のファイルを用意する必要があります.

まず,ネットやこの掲示板で「フック」とか「フック関数」を勉強して
みて下さい.私のサイトにも情報が少しあります.また,ここのMADIAさん
のところにもあります(以下のページ)
http://madia.world.coocan.jp/delphi/tokusen.htm

編集 削除
Mr.XRAY  URL  2005-09-11 13:00:12  No: 17512  IP: 192.*.*.*

>アプリに送るというよりは、

最初の質問の内容から判断すると,これは「アプリに送る」のです.
見掛け上は,他のアプリ,例えばメモ帳で'A'とタイプしたら'AB'と
なったとしても,それはメモ帳が行うのではなく,'A'を検出したら
'AB'をメモ帳に「送る」ことになります.
(自分で開発する自分自身のアプリなら別ですが)

このような動作の仕組みを理解することも,プログラミングでは重要です.
もちろん,他の方に質問する際に,意味を理解してもらうためにも...

編集 削除
slash  2005-09-11 15:14:07  No: 17513  IP: 192.*.*.*

こういう事はなかなか難しいみたいですね;
勉強不足のようなので色々調べてみます。

編集 削除
Mr.XRAY  URL  2005-09-11 16:31:19  No: 17514  IP: 192.*.*.*

>こういう事はなかなか難しいみたいですね;

でも,できればいろいろと面白いかも知れません.一つ例を...
新規プロジェクトを作成し,FormのOnCreateとDestroyのイベントを次の
ようにして実行し,メモ帳を起動します.
このメモ帳で何かキーを押してみて下さい.フックというのはこんな感じ
です.usesにClipbrdを忘れないように.

uses Clipbrd;

var
   Hook: HHOOK;

//=============================================================================
//  WH_JOURNALRECORDのコールバック関数
//  文字列を送るのにクリップボードを使用した例
//=============================================================================
function JournalRecordProc(nCode:Integer;wParam:WPARAM;
                            lParam:LPARAM):LRESULT;stdcall;
var
  pevent: pEVENTMSG;
  h     : HWND;
  hh    : HWND;
  A     : Word;
  S     : String;
begin
     if nCode < 0 then begin
       Result := CallNextHookEx(Hook, nCode, wParam, lParam)
     end else begin
       Result := 0;
       if nCode = HC_ACTION then begin
         pEvent := pEVENTMSG(lParam);
         h      := GetForegroundWindow;
         hh     := GetWindow(h,GW_CHILD);
         case pEvent.Message of
           WM_KEYDOWN:
           begin
             A:=pEvent.paramL and $FF;
             S:='  文字 '+Chr(A)+' を押しましたね.'+#13#10;
             //クリップボード経由
             ClipBoard.SetTextBuf(PChar(S));
             SendMessage(hh, WM_PASTE,0,0);
           end;
         end;
       end;
     end;
end;
//=============================================================================
//  フック開始
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
     Hook:=SetWindowsHookEx(WH_JOURNALRECORD,Addr(JournalRecordProc),
                              hInstance,0);
end;
//=============================================================================
//  アプリ終了時はフック関数をアンインストール(登録解除)
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
     UnhookWindowsHookEx(Hook);
end;

編集 削除
slash  2005-09-12 18:05:04  No: 17515  IP: 192.*.*.*

わざわざ例までありがとうございます。
難しすぎてあまり理解できませんが;;

色々調べてみた結果なんとかそれっぽいものができました。

var
  Form1: TForm1;

implementation

{$R *.DFM}

const id_W = 101;

procedure TForm1.WMHotKey( var Msg: TWMHotKey );
begin
   if GetAsyncKeyState(VK_NONCONVERT)<>0 then

      keybd_event(Byte('0'), 0, 0, 0);
      keybd_event(Byte('0'), 0, KEYEVENTF_KEYUP, 0);
      keybd_event(Byte('1'), 0, 0, 0);
      keybd_event(Byte('1'), 0, KEYEVENTF_KEYUP, 0);
      keybd_event(Byte('2'), 0, 0, 0);
      keybd_event(Byte('2'), 0, KEYEVENTF_KEYUP, 0);
      
end;

procedure TForm1.FormCreate(Sender : TObject );
begin
      RegisterHotkey(Form1.Handle, id_W, 0, Byte('W'));
end;

procedure TForm1.FormDestroy(Sender : TObject );
begin
      UnRegisterHotkey(Form1.Handle, id_W);
end;

end.

Borland Q&Aを参考にしました。
無変換押さないと正しく012と入力されないですが;

編集 削除
slash  2005-09-12 18:51:35  No: 17516  IP: 192.*.*.*

ちょっと問題がありました
    else if GetAsyncKeyState(VK_NONCONVERT)<>1 then
      keybd_event(Byte('W'), 0, 0, 0);
      keybd_event(Byte('W'), 0, KEYEVENTF_KEYUP, 0);
にすると固まってしまいます。
無変換を押していないときはホストキーWをWと返したいのですが。。

あと、ホストキーに設定したキーを二回押すようにしたいのですが可能でしょうか?この例だと  W+W

編集 削除
Mr.XRAY  URL  2005-09-12 21:34:31  No: 17517  IP: 192.*.*.*

>難しすぎてあまり理解できませんが;;

実行してみたのでしょうか.

>      keybd_event(Byte('W'), 0, KEYEVENTF_KEYUP, 0);
>にすると固まってしまいます。

これは当然です.何故ならホットキーが'W'ですから,それを検出したら
また'W'を送っていますから,またホットキーを検出....
と永久に終了しません.

>無変換を押していないときはホストキーWをWと返したいのですが。。
どこに返すのでしょうか.

>あと、ホストキーに設定したキーを二回押すようにしたいのですが
どういう状況を意味するのかわかません.

申し訳ありませが,slashさんのなさりたいことが見えません.

編集 削除
slash  2005-09-12 21:58:35  No: 17518  IP: 192.*.*.*

色々ありがとうございます。
自己解決しました。

編集 削除