EXCELにキー送信を確実にするためには?


なな  2007-07-24 17:58:25  No: 27180

OLEコンテナにExcelを貼り付けて制御しています。
ボタンを押すと例えば、下記のコードのように
CTRL+Vを送信するなどExcelのショートカットキーを
使いたいと考えています。

下記の例のように貼り付けだけなら
Pasteのメソッドも使えるのですがそのほかにも
EXCELのメニュー(Alt+xx)も使いたいと思います。

ほとんどの場合、このコードでうまくいくのですが、
たまに、キー送信が失敗します。
(この場合、貼り付けが行われない)
パソコンに負荷がかかっていると頻繁に失敗します。
(XP+EXCEL2000ならうまくいくが、
  Vista+EXCEL2007だと失敗するなど)

確実にキーを送信する方法は、無いでしょうか?
キー送信を受け取るまで待機するなど・・・

var InputKey: array[0..3] of TInput;
    AWH, TWH, WinHnd: HWND;
    TTh, STh: DWORD;
    Rtn: Integer;
begin
      TWH := GetForegroundWindow;
      TTh := GetWindowThreadProcessId(TWH, nil);
      STh := GetCurrentThreadId;
      AttachThreadInput(STh, TTh, True);
      AWH := GetFocus();

      Application.ProcessMessages;
      Sleep(20);

      inputKey[0].Itype := INPUT_KEYBOARD;
      inputKey[0].ki.wVk := VK_CONTROL;
      inputKey[0].ki.wScan := MapVirtualKey(VK_CONTROL, 0);
      inputKey[0].ki.dwFlags := 0;
      inputKey[0].ki.dwExtraInfo := 0;
      inputKey[0].ki.time := 0;

      inputKey[1].Itype := INPUT_KEYBOARD;
      inputKey[1].ki.wVk := Ord('V');
      inputKey[1].ki.wScan := MapVirtualKey(Ord('V'), 0);
      inputKey[1].ki.dwFlags := 0;
      inputKey[1].ki.dwExtraInfo := 0;
      inputKey[1].ki.time := 0;

      inputKey[2].Itype := INPUT_KEYBOARD;
      inputKey[2].ki.wVk := Ord('V');
      inputKey[2].ki.wScan := MapVirtualKey(Ord('V'), 0);
      inputKey[2].ki.dwFlags := KEYEVENTF_KEYUP;
      inputKey[2].ki.dwExtraInfo := 0;
      inputKey[2].ki.time := 0;

      inputKey[3].Itype := INPUT_KEYBOARD;
      inputKey[3].ki.wVk := VK_CONTROL;
      inputKey[3].ki.wScan := MapVirtualKey(VK_CONTROL, 0);
      inputKey[3].ki.dwFlags := KEYEVENTF_KEYUP;
      inputKey[3].ki.dwExtraInfo := 0;
      inputKey[3].ki.time := 0;

      Rtn := SendInput(4, inputKey[0], sizeof(inputKey[0]));

      Application.ProcessMessages;
      Sleep(20);

      AttachThreadInput(STh, TTh, False);

何か、良い方法はないでしょうか?


まこと  2007-07-25 06:15:59  No: 27181

私のところではこれで問題なく動いていました。
(正確にはOLEコンテナ使用ではありませんが…)

※ もし既に試されていた場合はごめんなさい。

var
  A: Integer;
begin
  A := FindWindow('XLMAIN', nil);
  if A <> 0 then begin
    PostMessage(A, WM_KEYDOWN, VK_Tab, 0);
  end;
end;


まこと  2007-07-25 06:34:02  No: 27182

試しに上と類似の処理を20000回繰り返して実行してみました。
  (セルに色を付けて,エンターキーで下のセルに移動するというもの。)

その結果,20000回処理させたに関わらず,実際に入力が行われたのは,
5000回程度でした。ようやく質問の意図が見えました。回答のポイント
がずれてしまい,申し訳ありません。

  せっかくですので,もう少しいじってみて,分かったらその時点で
再度書き込みたいと思います。失礼しましたm(_ _)m


なな  2007-07-25 07:40:54  No: 27183

まことさんレス&検証ありがとうございます。

Mr.XRAYさんの所にあったサンプルより
キーフックの部分を参考に

SetWindowsHookExとUnhookWindowsHookExを使用し
キーコードを取得してみると
時々、キーがうまく送信できてないようでした。
とりあえずは、送信に失敗したら、成功するまで
ループさせるようにしてみました。

なぜ、送信に失敗するのでしょうか・・・


Wash  2007-07-25 09:23:33  No: 27184

負荷をかけている(常駐している)アプリを一つずつ止めて悪さをしている
アプリを洗い出したらどうなの?


なな  2007-07-28 04:07:47  No: 27185

Washさんレスありがとうございます。

常駐アプリを減らしてやってみましたが
あまり効果は見られませんでした。
根本的に何かが違っているかもしれません。
CTRLキーを送信すると失敗することがあるのでしょうか?

そもそも、OLEでEXCELを自動操縦することが
無謀なのでしょうか・・・
もう少し、いろいろとやってみます。


Mr.XRAY  URL  2007-07-28 08:06:24  No: 27186

ちょっと気になったので。横から失礼。

>CTRLキーを送信すると失敗することがあるのでしょうか?

これはあると言うか、考えられると思いますよ。

>とりあえずは、送信に失敗したら、成功するまで
>ループさせるようにしてみました。

これがその対策だと思います。もしこれでも(目的の動作が)成功しなければ
その目的の動作はその環境で不可ということになります。
OSやアプリの仕様が変わればメッセージの処理そのものや、メッセージの
プライオリティも変化するでしょうから。

例えば、IEのような俗に言う重いアプリのハンドルを取得する際もそのように
ループしないと取得できないことがあります(特に起動時。メモ帳ならば不要)

どうしても納得いかなければWindowsのメッセージ処理の構造を詳しく調べる
しかないですね。
Windows(に限らず)はメッセージの嵐でしょうから大変だとは思いますが。
(当然ですが、常に何百、何千のメッセージが飛交っているわけです)
もちろん、プログラミングの目的にもよるでしょう。そのような方も技術の
発展には必要とは考えます。少なくとも私は違いますが...


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

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






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