質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.97%

ExcelVBA 外部アプリ→起動から入力可能になるまで待機する処理

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 4,946

dewdtm

score 6

いつもお世話になっております。

ExcelVBAにおきまして、外部アプリケーション(Oracle系データベースソフト)の起動からログインまでを自動で処理させたいと思っております。

この文の最後に記述しているコードでは、
アイドル状態になるまで待機するという処理を組んでいるのですが、
起動後、入力フォームが表示されるまで読み込みによるラグがあり、
入力可能になる前にデータが送信されてしまいます。

現在、Sleepにより数秒間の待機時間を設けていますが、
PCのスペックによって読み込み時間が上下するため、ほぼ使い物になりません。

IEなどの場合は、入力可能まで待機させるようなAPIが用意されているようですが、
他の外部アプリでは、そういったAPIは存在しないのでしょうか?
また、存在しない場合、何か別の方法で実現させることは可能でしょうか?
ご教示の程、どうか宜しくお願いいたします。

Option Explicit

Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
' OpenProcess 関数
Private Declare Function OpenProcess Lib "KERNEL32.DLL" ( _
ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long _
) As Long

' CloseHandle 関数
Private Declare Function CloseHandle Lib "KERNEL32.DLL" ( _
ByVal hObject As Long _
) As Long

' WaitForInputIdle 関数
Private Declare Function WaitForInputIdle Lib "USER32.DLL" ( _
ByVal hProcess As Long, _
ByVal dwMilliseconds As Long _
) As Long

' 定数の定義
Private Const SYNCHRONIZE As Long = &H100000
Private Const INFINITE    As Long = &HFFFF

' -------------------------------------------------------------------------------
'       プロセスがアイドル状態になるまで指定したミリ秒数待機します。
'
' @Param    lProcessId      アイドル状態になるまで待機するプロセスの ID。
' @Param    [lMilliseconds] アイドル状態になるまで待機する時間。(省略時は無限)
' -------------------------------------------------------------------------------
Public Sub WaitForInputIdleProcess(ByVal lProcessId As Long, Optional ByVal lMilliseconds As Long = INFINITE)
Dim hProcessHandle As Long

lProcessId = CLng(Shell("C:\SCV8CL\ClientPack\ScMen.exe", vbNormalFocus))

' Process を開いてハンドルを取得する
hProcessHandle = OpenProcess(SYNCHRONIZE, 0&, lProcessId)

If hProcessHandle <> 0 Then
' アイドル状態になるまで、指定したミリ秒数待機する
Call WaitForInputIdle(hProcessHandle, lMilliseconds)
' アイドル状態
Sleep 250
SendKeys "LoginID", True
SendKeys "{Enter}"
SendKeys "LoginPass", True
SendKeys "{Enter}"
SendKeys "{Enter}"
' Process のハンドルを閉じる
Call CloseHandle(hProcessHandle)
End If
End Sub

Sub test()
Dim lProcessId As Long
Call WaitForInputIdleProcess(lProcessId)
End Sub

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

アプリケーション自体の起動待ちは問題ないけど、そこから入力可能になるまでのラグがあるから待ちたいということですよね。
おそらく前回の質問からの継続ですよね。
送り先のテキストボックスのハンドルは取得できているはずですから、IsWindowEnabledでテキストボックスの状態を判定してみてはいかがでしょうか。
https://msdn.microsoft.com/ja-jp/library/cc410870.aspx

  1. 親ウィンドウのハンドル取得
  2. 子ウィンドウ(テキストボックス)のハンドル取得
  3. IsWindowEnabledで子ウィンドウの状態取得
  4. 戻り値が0の間3を繰り返す

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/02 16:52

    すみません、今更なのですが、
    いただいたコードのタイムアウトイベントが作動しておりませんでした。

    Do
    hwndParent = FindWindow("WindowsForms10.Window.8.app.0.141b42a_r12_ad1", "Login")
    If hwndParent <> 0 Then
    Call ノード展開_Click
    For idx = 1 To TreeView2.Nodes.Count
    If CLng(TreeView2.Nodes(idx)) = hwndParent Then
    hWndChild1 = TreeView2.Nodes(idx + 9)
    hWndChild2 = TreeView2.Nodes(idx + 10)
    hWndChild3 = TreeView2.Nodes(idx + 11)
    Exit Do
    End If
    Next
    End If
    DoEvents
    Loop

    st = Now
    Do
    DoEvents
    ret = IsWindowEnabled(hWndChild1)
    If DateDiff("s", st, Now) >= 10 Then Exit Do
    Loop While ret = 0

    If ret = 0 Then
    Debug.Print "time out"
    Exit Sub
    End If


    どうもDo Loop処理の配置が間違っているような気がするのですが、
    お分かりになりますでしょうか。
    後追いで申し訳ございません。

    キャンセル

  • 2018/08/02 17:00

    どうも、お久しぶりです。
    問題はなさそうですが、もしかしたら最初のループの方で無限ループしているとかいう話でしょうか。
    だとすればタイムアウトの処理は入っていないので当然そうなります。
    こちらにも必要ですね、みたいな話をしたようなしていないような・・・。

    キャンセル

  • 2018/08/02 17:06

    ttyp03様

    お久しぶりです、ttyp03様のおかげで
    日々、プログラムの質が向上しております。

    >こちらにも必要ですね、みたいな話をしたようなしていないような
    あ”っ! そうでした;;
    すみません! 以前にご指摘していただいたことを
    すっかり忘れておりました。大変失礼いたしました。
    早速、処理を追加してみたいと思います。
    ありがとうございました。

    キャンセル

+1

プロセスの一覧でターゲットのアプリケーションが起動しているかどうかを確認する方法があります。
以下参考。
実行中のタスク一覧(非API)
※上記はwordを利用しています。APIでプロセス一覧取得するほうが筋だとは思いますが。

外部アプリケーション(Oracle系データベースソフト)の起動は誰がおこなっているのでしょうか?
もし、そのアプリケーションがパラメータとしてログイン情報を受け付けるのなら、VBAで起動して完了したら次の処理というような事も可能かと思います。

追記

VBAで他のアプリケーションを同期起動する(WshShell)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/11 16:33

    sazi様

    ご回答ありがとうございます。

    >外部アプリケーション(Oracle系データベースソフト)の起動は誰がおこなっているのでしょうか?
    >もし、そのアプリケーションがパラメータとしてログイン情報を受け付けるのなら、
    >VBAで起動して完了したら次の処理というような事も可能かと思います。

    せっかくご回答いただいたのに私の力不足で内容の趣旨が理解できていないのですが、
    プロセスの起動まで待機したとしても、そこからログインフォームの表示が完了するまでに
    数秒のラグがあるため、どちらにしても早めにデータを送信してしまいます。

    的外れの回答でしたら、申し訳ございません。

    キャンセル

  • 2018/07/11 17:36

    追記しました。

    キャンセル

  • 2018/07/12 09:07

    sazi様

    追記情報、ありがとうございました。
    私もURL先に書かれているようなことは以前に試しましたが、
    やはり起動する・しない以外の判定が必要でした。
    他の方がお答えしていただいている子ハンドルの状態取得で対応したいと思います。
    お忙しい中、ご回答いただきまして感謝申し上げます。

    キャンセル

+1

プロセスの起動まで待機したとしても、そこからログインフォームの表示が完了するまでに
数秒のラグがあるため、どちらにしても早めにデータを送信してしまいます。

まず、事前に、ログインフォームのクラス名とウィンドウ名をツールを使って調べておきます。
APIのFindWindow関数でクラス名、ウィンドウ名を引数に、ウィンドウハンドルを取得する。開いてないと失敗するので、成功するまで、ループする。
ウィンドウハンドルが取得できたら、開いているということになります。

自作あぷりからAPIで他のあぷりをいじるときのめも。(1/4) - ×××Diary

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/12 09:11

    hatena19様

    お忙しい中ご回答いただきまして、感謝申し上げます。
    ハンドルの状態で入力可否を判断する方法は頭に浮かびませんでした。
    とても有用な情報をくださり、ありがとうございます。
    早速、試してみまして、このページ内に結果をご報告できればと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.97%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる