外部プログラムの起動後の処理


みお  2004-02-25 17:43:21  No: 82276  IP: [192.*.*.*]

VB6で、プログラムを作っています。
コマンドボタンを押したら、メモ長がひらかれるようにしたいんです。
ヘルプとかをしらべて、メモ帳が開かれるところまではできました。
そのあと、手入力でテキスト入力をするんじゃなくて、
プログラム内で、メモ帳が開かれたときに、
「ここに文字をいれてください」とすでにテキスト表示をさせたいのですが、
プログラムでは不可能でしょうか?
それを応用して、コマンドプロンプトにもテキストをいれたり、
エクセルにテキストを入れたりしたいんです。
ご存知のかた教えてください。m(__)m

編集 削除
punto  2004-02-25 19:47:40  No: 82277  IP: [192.*.*.*]

最も簡単な方法:

    Shell("notepad.exe", vbNormalFocus)
    SendKeys "ここに文字をいれてください."

編集 削除
たかみちえ  URL  2004-02-25 22:10:51  No: 82278  IP: [192.*.*.*]

> SendKeys "ここに文字をいれてください."
  それでうまくいくでしょうか?
ShellでNotepadをひらかせるように指示してから、
実際にNotepadが入力できるようになるには、プログラム的にはかなりの差がありますから——。

  ファイル名が見えたりして、あんまりよろしくないような気もしますけど、
"ここに文字を入れてください"という文章の書かれたテキストファイルを作って、
Shell("notepad text.txt", vbNormalFocus)
みたいな感じにしてもいいかと。

編集 削除
punto  2004-02-25 23:34:32  No: 82279  IP: [192.*.*.*]

やってみたこともない code 例示したりはしないけど,必ず上手
くいくわけじゃないのは暗黙値ってことで….

な事のためにこんなんするか?わしゃあの2行で充分だとおもうけどぉ…


Private Declare Function OpenProcess Lib "kernel32" _
                            (ByVal dwDesiredAccess As Long, _
                             ByVal bInheritHandle As Long, _
                             ByVal dwProcessId As Long) As Long
Private Const PROCESS_QUERY_INFORMATION As Long = &H400&
Private Declare Function GetExitCodeProcess Lib "kernel32" _
                            (ByVal hProcess As Long, _
                             lpExitCode As Long) As Long
Private Const STILL_ACTIVE              As Long = &H103&
Private Declare Function CloseHandle Lib "kernel32" _
                            (ByVal hObject As Long) As Long


Private Function ProgramExists(lTask As Long) As Boolean
    Dim lProcHandle As Long
    Dim lExitCode   As Long
    Dim lState      As Long

    lProcHandle = OpenProcess(PROCESS_QUERY_INFORMATION, True, lTask)
    If lProcHandle = 0 Then
        ProgramExists = False
        Exit Function
    End If
    lState = GetExitCodeProcess(lProcHandle, lExitCode)
    If lExitCode = STILL_ACTIVE Then
        ProgramExists = True
    Else
        ProgramExists = False
    End If
    lState = CloseHandle(lProcHandle)
End Function

Private Sub Command1_Click()
   Dim lTask As Long
  
   lTask = Shell("notepad.exe", vbNormalFocus)
   Do Until ProgramExists(lTask): Loop
   SendKeys "ここに文字をいれてください."
End Sub

ま,なんでもやってみてくれ.

編集 削除
punto  2004-02-26 00:17:15  No: 82280  IP: [192.*.*.*]

とかいって,
    Shell("notepad.exe", vbNormalFocus)
じゃだめじゃん.
    Shell "notepad.exe", vbNormalFocus
だよね…はは!

編集 削除
たかみちえ  URL  2004-02-26 02:51:50  No: 82281  IP: [192.*.*.*]

ええーと、SendKeysは"アクティブなフォームにキーを送る"だけの手続きだから、
起動直後に送ったのでは失敗する可能性の方が高いのでは?と思ったのです。
  質問者さんのことを考えると、なるべくかんたんにした方がいいかな…とおもっていながら、
結局説明足らずになってしまいましたね^^;ごめんなさいm(__)m

  たとえば、その前にSendMessageでWM_NULLを送って、ちょっと待ってみるとか、SetForegroundWindowを使うとか——ですね。

  ちなみに、わたしはこういう風にしました。Delphiのものです。
hNotePad, hDebugNoteという変数は、あらかじめHWND型(VBでならLongが適当でしょう)で宣言しておきます。
NotePadClassName, NotePadEditClass定数は、
前から"メモ帳のクラス名"、"メモ帳のエディタのクラス名"です。

// メモ帳を起動し、デバッグモニタ代わりに使う(Delphiの無償版には、デバッグログを表示する機能がないので)
WinExec(NotePadFileName,SW_SHOW);
hNotePad      := FindWindow(NotePadClassName,nil);
SendMessage(hNotePad,WM_NULL,0,0);
hDebugNote    := FindWindowEx(hNotePad,0,NotePadEditClass,nil);
SetWindowText(hNotePad,'DebugUnitレポート');
// デバッグモニタが下がっちゃ意味ないので、最前面表示
SetWindowPos(hNotePad,HWND_TOPMOST,0,0,0,0,
  SWP_NOSIZE or SWP_NOMOVE or SWP_NOACTIVATE);

  こうしておいて、送信するときは
SendMessage(hDebugNote, EM_REPLACESEL, 0, lParam(PChar(Str)));
とやります。ただし、この場合メモ帳がすでに起動してるときにはどうなるかわかりません。
メモ帳でキャレット位置をいじっちゃった場合も、挿入場所がおかしくなります。
デバッグモニタなら、もう少ししっかりするべきでしょうね。

  WinExecなんかより、ShellExecuteとかを使いなよという意見もあるでしょうが、とりあえずそのつっこみはなしで(^^ゞ

編集 削除
punto  2004-02-26 08:40:32  No: 82282  IP: [192.*.*.*]

確かに、SendKeys だと active じゃないといかんなぁ・・・
いる、って判っても、どんな状態でいるかは解んないしなぁ・・・
ちょっと待つってのは大事だろうなぁ・・・
VB で SendKeys の debug はきっとやりにくいよなぁ・・・

そうかぁ Window Handle がとれりゃ SetWindowText できるから、
そっちの方がずっと smart だね!

“すでに開かれてたら” ってのも NotPad ならよさそうだけどなぁ・・・

あとは みおくん にがんばってもらおぅっと.

編集 削除
みお  2004-02-26 09:44:51  No: 82283  IP: [192.*.*.*]

punto様、たかみちえ様、ご教授ありがとうございますm(__)m
とてもわかりやすい文章で、初心者の私でも理解ができました。
ありがとうございます。

いろいろ調べながら、全て実行してみました。
初心者なので、至らない点などあると思うのですが、私が実行してみた
結果です。

まず、punto様の最初の
    Shell("notepad.exe", vbNormalFocus)
    SendKeys "ここに文字をいれてください."
の2行での実行はノートパッドは開き、文字は表示されるのですが、
表示される文字長がバラバラで、「ここに」までの時や、「ここに文字」までの
時など、全部文字が表示されませんでした。(TT)

たかみちえ様の最初のすでに文字を入力しておき、そのノードパッドを起動させる
方法は、文字が固定なら可能なのですが、ノードパッドのほかに、エクセルや、
コマンドプロンプトでも入力文字は可変長で実行したい箇所があるので、必ず
固定で実行するボタンが1つありますので、そこで実行するとうまくでました!

固定でのノートパッドの問題は解決したのですが、可変長での
ファイル起動後の文字入力(ノートパッドとコマンドプロンプトとエクセル)が
できず、まだ困ってまして、
次にpunto様のFunctionでのプログラムを実行してみました。
ブレークポイントを設定せずにプログラムを起動すると、うまくノートパッドに全て文字が
はいりました!!  なのですが、動作を確認したくて、ブレークポイントを2,3点設定して、
プログラムの動きを1行ずつみていくと、文字が1文字も出なくなってしまいました(TT)

ブレークポイントを追いかけて、ノートパッドがアクティブでないからなのでしょうか?
また、こちらの方法は、エクセル、コマンドプロンプトでも起動させるEXEを変更し、
ブレークポイントを設置しなければ、動作可能になるのでしょうか?(今からやってみます)


たかみちえ様のDelphiでの起動もやってみたのですが、
punto様のSENDKEYSと同じで、結果がまばらなものになってしまいました(TT)

もうすこしいろいろ試してみます。

それは私の実行方法がおかしいから、動かないのでは?という点がありましたら、
ご指摘お願いします。

他に方法などありましたら、ご教授お願い致します m(__)m

編集 削除
ねろ  2004-02-26 10:25:11  No: 82284  IP: [192.*.*.*]

Delphiが出てきたりして少し錯綜ぎみですが
>コマンドボタンを押したら、メモ長がひらかれるようにしたいんです。
なんで
Dim AppId
Private Sub Command1_Click()
    AppId = Shell("notepad.exe", vbNormalFocus) 'メモ帳起動
    AppActivate (AppId)
    SendKeys ("ここに文字をいれてください")     '書き込む
End Sub
これで失敗することは無いと思いますが。

ちなみに
For n = 1 To 20
    AppId = Shell("notepad.exe", vbNormalFocus) 'メモ帳起動
    AppActivate (AppId)
    SendKeys ("ここに文字をいれてください")             '書き込む
Next
なんてやっても失敗はしません。かなり過酷だと思いますが。

編集 削除
たかみちえ  URL  2004-02-26 10:47:56  No: 82285  IP: [192.*.*.*]

> プログラムの動きを1行ずつみていくと
  ええと、まずわたしが書いたとおり、SendKeysというのは、"アクティブなフォーム上で、キーボード操作をシミュレートする"ものですから、
アクティブなフォームがVBのウィンドウだと、当然結果がおかしくなります。
これはヘルプにもすこし載っていたと思いますから、確認してみてください。
  たとえば、SendKeys "ABC"とすると、
今その場で、キーボードで" A B C "とタイプしたのと同じこと"ができます。
つまりSendKeysが、キータイプ操作をシミュレートしてる途中で、アクティブなフォームを切り替えても、もちろん結果がおかしくなりますね。


> 可変長でのファイル起動後の文字入力
  全て共通の手段で、確実に…というのは難しいですね。
ExcelはまだActiveXコントロールとか(ExcelのActiveXについては過去ログにあったかも)があるでしょうからいいですが、
コマンドプロンプトみたいに、そもそもアプリケーションのタイプからしてそもそも別のものをどうにかしよう…となると、難しいです。
まあ、バッチファイルを自動生成してやれば、幾分かシミュレートできるかもしれませんが。
  そもそも実行されるものがなんなのかわからない状況で、文字を表示するのはたやすいことではないです。そういった意味では、
puntoさんのおっしゃった、"SendKeys"が一番簡単かつ汎用的ではあります。
(じゃあテキスト入力ウィンドウのないソフトの場合は?起動処理が長いソフトの場合は?とか言うと、きりがないですが(^^ゞ)


  そういえばSendKeysより確実にキーを送る方法としては、
SendMessageでWM_KEYUP、WM_KEYDOWN  などのメッセージを送る方法もあります。
これはSendKeysと違ってウィンドウハンドルを指定して送りますから、SendKeysよりかなり確実です。
  ただしSendKeysのように文字列で書くことができない(キーを押して、放して、押して——といった動作を記述していくことになる)ので、難しくなります。
汎用性を求めようとするなら、なおのこと。

編集 削除
たかみちえ  URL  2004-02-26 10:54:23  No: 82286  IP: [192.*.*.*]

VBのコードに、この場で正確に書き直せる自信がないですから、Delphiのコードをそのまま転載…ということになってしまいました(^^ゞ

>AppActivate (AppId)
  そういえばShellの戻り値IDを使えばそんなこともできるんでしたっけ…。
これでアクティブにしたあと、GetForegroundWindowでウィンドウハンドルを得て、SendMessageでWM_KEYDOWN——とやると、
SendKeys中のトラブル(キーシミュレート中にユーザーがフォーカスを切り替えてしまうなど)も防げるかも。

編集 削除
ねろ  2004-02-26 13:23:16  No: 82287  IP: [192.*.*.*]

>そういえばShellの戻り値IDを使えばそんなこともできるんでしたっけ…。
はい、MSDNのShell関数の使用例に。

>AppActivate (AppId)
>SendKeys ("ここに文字をいれてください")             '書き込む
DoEventsでも入れない限りこの間にキーイベントが割り込む可能性は少ないと思いますが。
どうしても心配で夜眠れない様な場合は、
SetClipboardDtat , SendMessage , WM_PASTE
を使ってクリップボード経由で貼り付ける方法もあるかと。
『何もそこまでしなくても』と言う感じですが。

編集 削除
魔界の仮面弁士  2004-02-26 14:09:03  No: 82288  IP: [192.*.*.*]

BASP21の Debugメソッドを使うのが簡単かも。

http://www.hi-ho.ne.jp/babaq/basp21.html#0021

編集 削除
簡単  2004-02-27 10:00:42  No: 82289  IP: [192.*.*.*]

Shell("○○.exe    ここに文字をいれて",VbNormal)
って入れると、この場合だと、ノートパッドが起動されて、しかも、
スペースの後の文字が入力されるんじゃないですか?

編集 削除
punto  2004-02-28 00:14:44  No: 82290  IP: [192.*.*.*]

おぃおぃ・・・

編集 削除