HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2014
0902
ぴょぴょ(科学太郎)起動ソフトのウインドウ・ハンドルの取得方法は?7解決


ぴょぴょ(科学太郎)

リンク

2014/9/2(Tue) 20:24:26|NO.64638

現在、サイドバー・ランチャーを作成中です。

このランチャーの機能の1つでソフトを起動後、
ある一定の間隔でメッセージを出す仕組みを実装しようとしてます。

最初は、ランチャーで選択したソフトをタイマー処理などで
ある一定の間隔で報告することを考えてました。

この方法だとソフトを終了してもタイマー処理の中断は行われません。
これを実現するには、ランチャーで起動したソフトの監視が必要です。

そうなるとウインドウ・ハンドルを監視するのが一般だと思ういますが、
問題は、ランチャー(ShellExecute)で起動したソフトのウインドウ・ハンドルを
取得する方法が分かりません。

この部分が知りたいと思います。
このウインドウ・ハンドルの監視以外のも
ソフトの終了を検出する方法を教えてほしいと思います。

よろしくお願いします。



この記事に返信する


ぴょぴょ(科学太郎)

リンク

2014/9/2(Tue) 20:25:33|NO.64639

> このウインドウ・ハンドルの監視以外にも
> ソフトの終了を検出する方法を教えてほしいと思います。
訂正。



woodfields

リンク

2014/9/4(Thu) 01:29:27|NO.64676

hspはcomが使えるのでこんな感じでどうでしょう。

newcom oWshShell, "WScript.Shell" oExec=oWshShell("Exec", "calc.exe") repeat wait 1 if oExec("Status") == 0 { title "calc.exe実行中 PID:"+oExec("ProcessID") }else{ title "" } loop delcom oWshShell
起動したプログラムをアレコレしたいなら、ShellExecuteではなく、ShellExecuteExのようです。
http://chokuto.ifdef.jp/urawaza/api/ShellExecuteEx.html
プロセスハンドルからプロセスIDを取得するには、GetProcessIdとか。



osakana

リンク

2014/9/4(Thu) 03:47:21|NO.64678

詳しくはないですが個人的にも興味があったので
調べて API を使うスクリプトを書いてみました。
#Vista SP2 でしか確認していません

まずプロセスからウィンドウハンドルを取得するには
全ウィンドウから地道に1個ずつ一致するまで調べて行くしかないようです。

/* プロセスIDからウィンドウハンドルを得る */ #uselib "kernel32.dll" #func CloseHandle "CloseHandle" sptr #func GetProcessId "GetProcessId" sptr #uselib "user32.dll" #func GetDesktopWindow "GetDesktopWindow" #func GetTopWindow "GetTopWindow" sptr #func GetWindowThreadProcessId "GetWindowThreadProcessId" sptr,sptr ;#func GetNextWindow "GetNextWindow" sptr,sptr ; ないもよう #func GetWindow "GetWindow" sptr,sptr #func IsWindowVisible "IsWindowVisible" sptr #func GetWindowLong "GetWindowLongA" sptr,sptr #uselib "shell32.dll" #func ShellExecuteEx "ShellExecuteExA" sptr #define SW_SHOW $00000005 #define SEE_MASK_NOCLOSEPROCESS $00000040 #define GW_HWNDNEXT $00000002 #define GW_HWNDPREV $00000003 #define GWL_HWNDPARENT $FFFFFFF8 #define WM_GETTEXT $0000000D #define WM_GETTEXTLENGTH $0000000E #define WM_CLOSE $00000010 #define hProcess 14 // メモ帳起動 dim sei, 15 ;SHELLEXECUTEINFO 構造体 verb = "open" file = "notepad" sei = 60, SEE_MASK_NOCLOSEPROCESS, hwnd, varptr(verb), varptr(file), 0, 0, SW_SHOW ShellExecuteEx varptr(sei) if stat = 0 :mes "失敗" // プロセスハンドルからプロセスIDを得る GetProcessId sei.hProcess ; XP SP1 以降でないと使えないらしい pid_target = stat CloseHandle sei.hProcess ; GetDesktopWindow ; GetTopWindow stat GetTopWindow 0 ; これでも良いみたい hw = stat wait 50 sdim wndtxt, 256 repeat if hw = 0 :break IsWindowVisible hw if stat { ; 可視状態のウィンドウ GetWindowLong hw, GWL_HWNDPARENT if stat = 0 { ; 親ウィンドウを持たないウィンドウ GetWindowThreadProcessId hw, varptr(pid) if pid = pid_target { ; 探しているプロセスIDと一致 mes "プロセスID:" + pid mes "ウィンドウハンドル:" + hw sendmsg hw, WM_GETTEXT, 256, varptr(wndtxt) mes "ウィンドウタイトル:" + wndtxt break } } } GetWindow hw, GW_HWNDNEXT ; 次のウィンドウハンドル取得 hw = stat loop // 実行プロセスをウィンドウハンドルから終了 sendmsg hw, WM_CLOSE, 0, 0 mes "終了"
プロセスの終了を監視するには
CreateProcess で実行ファイルを起動し WaitForSingleObject で終了を待つやり方と
プロセスハンドルから GetExitCodeProcess で終了を監視する方法があるようです。
(他にも有るかもしれませんが分かりませんでした)
WaitForSingleObject で待つやり方はスレッドが停止してしまって
HSPでは他の処理ができなくなるためプロセスを分けるなど工夫が必要そうなので、
GetExitCodeProcess で待つ方法でスクリプトを作ってみました。

/* プロセスの終了を監視 */ #uselib "kernel32.dll" #cfunc OpenProcess "OpenProcess" sptr,sptr,sptr #func GetExitCodeProcess "GetExitCodeProcess" sptr,sptr #func CloseHandle "CloseHandle" sptr #cfunc GetProcessId "GetProcessId" sptr #uselib "shell32.dll" #cfunc ShellExecuteEx "ShellExecuteExA" sptr #define SW_SHOW $00000005 #define SEE_MASK_NOCLOSEPROCESS $00000040 #define PROCESS_QUERY_INFORMATION $00000400 #define hInstApp 8 #define hProcess 14 // メモ帳を起動 dim sei, 15 ;SHELLEXECUTEINFO 構造体 Verb = "open" File = "notepad" sei = 60, SEE_MASK_NOCLOSEPROCESS, hwnd, varptr(Verb), varptr(File), 0, 0, SW_SHOW if ShellExecuteEx(varptr(sei)) = 0 :mes "失敗" process_handle = sei.hProcess #if 0 ; GetExitCodeProcess へ渡すハンドルは PROCESS_QUERY_INFORMATION アクセス権が ; 割り当ててないといけないらしいけど、ShellExecuteEx で取得したプロセスハンドルでも ; 問題なく動くからこの部分は要らないかも? process_id = GetProcessId(process_handle) if process_id = 0 :mes "失敗" CloseHandle process_handle process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, 1, process_id) if process_handle = 0 :mes "失敗" #endif // 終了を監視 repeat GetExitCodeProcess process_handle, varptr(exitcode) if exitcode = 0 { CloseHandle process_handle break } wait 50 loop mes "終了"
おまけで実行ファイル名からプロセスIDを取得
これも全てのプロセスから一致する物を調べてる。

/* 実行ファイル名からプロセスIDを探す */ #uselib "kernel32.dll" #func CreateToolhelp32Snapshot "CreateToolhelp32Snapshot" sptr,sptr #func Process32First "Process32First" sptr,sptr #func Process32Next "Process32Next" sptr,sptr #func CloseHandle "CloseHandle" sptr #define SW_SHOW $00000005 #define SEE_MASK_NOCLOSEPROCESS $00000040 #define TH32CS_SNAPPROCESS $00000002 #define th32ProcessID 2 #define szExeFile 9 // メモ帳を起動 filename = "notepad.exe" exec filename wait 50 // プロセスのスナップショットをとる CreateToolhelp32Snapshot TH32CS_SNAPPROCESS, 0 if stat = -1 :stop hsnapshot = stat dim pe32, 74 ; PROCESSENTRY32 構造体 pe32 = 296 ; 構造体のサイズ // スナップショットから取得 Process32First hsnapshot, varptr(pe32) // 一致するファイル名を探す repeat if stat = 0 :break dupptr exefile, varptr(pe32.szExeFile), 260, 2 ; XP以降はファイル名のみでそれ以前はフルパスらしい。 if exefile = filename { ; 小文字に変換してから調べた方が良いかも。 mes "プロセスID:" + pe32.th32ProcessID + " : " + exefile } Process32Next hsnapshot, varptr(pe32) loop CloseHandle hsnapshot
参考になれば幸いです。



woodfields

リンク

2014/9/4(Thu) 20:40:53|NO.64685

ほぼ解決だと思いますが、念のために起動、終了をコントロールするならこんな感じでよいかも。

onexit *exit_routine button gosub "起動",*excute button gosub "強制終了",*terminate newcom oWshShell, "WScript.Shell" *mainloop wait 1 if flg_excute { if oExec("Status") == 0 { title "calc.exe実行中 PID:"+oExec("ProcessID") }else{ title "" flg_excute=0 } } goto *mainloop *excute if flg_excute == 0 { oExec=oWshShell("Exec", "calc.exe") if stat == 0 : flg_excute=1 } return *terminate if flg_excute : oExec->"Terminate" ; または mcall oExec,"Terminate" return *exit_routine onexit 0 delcom oWshShell end
あと、MS-Dosのtasklist、taskkillコマンドについて、次のようなページがあります。
http://tooljp.com/qa/D6F57AA3E7C9159549257BCF0055CB7C.html



osakana

リンク

2014/9/4(Thu) 22:42:45|NO.64687

すみませんスクリプトを一箇所訂正します。
プロセスの終了を監視する
の監視ループを抜ける条件文

if exitcode = 0 {...}


if exitcode ! STILL_ACTIVE {...} ;#define STILL_ACTIVE $00000103
に修正。
終了コードを決め打ちしてるのはさすがにおかしいですね、
再度訂正したスクリプト全体を貼っておきます。

#uselib "kernel32.dll" #cfunc OpenProcess "OpenProcess" sptr,sptr,sptr #func GetExitCodeProcess "GetExitCodeProcess" sptr,sptr #func CloseHandle "CloseHandle" sptr #cfunc GetProcessId "GetProcessId" sptr #uselib "shell32.dll" #cfunc ShellExecuteEx "ShellExecuteExA" sptr #define SW_SHOW $00000005 #define SEE_MASK_NOCLOSEPROCESS $00000040 #define PROCESS_QUERY_INFORMATION $00000400 #define STILL_ACTIVE $00000103 #define hInstApp 8 #define hProcess 14 // メモ帳を起動 dim sei, 15 ;SHELLEXECUTEINFO 構造体 Verb = "open" File = "notepad" sei = 60, SEE_MASK_NOCLOSEPROCESS, hwnd, varptr(Verb), varptr(File), 0, 0, SW_SHOW if ShellExecuteEx(varptr(sei)) = 0 :mes "失敗" process_handle = sei.hProcess #if 0 ; GetExitCodeProcess へ渡すハンドルは PROCESS_QUERY_INFORMATION アクセス権が ; 割り当ててないといけないらしいけど、ShellExecuteEx で取得したプロセスハンドルでも ; 問題なく動くからこの部分は要らないかも? process_id = GetProcessId(process_handle) if process_id = 0 :mes "失敗" CloseHandle process_handle process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, 1, process_id) if process_handle = 0 :mes "失敗" #endif // 終了を監視 repeat GetExitCodeProcess process_handle, varptr(exitcode) if exitcode ! STILL_ACTIVE { CloseHandle process_handle break } wait 50 loop mes "終了"
どうもお騒がせしました。

それにしてもCOMを使うと随分スッキリしますね
やりたい事にも依るでしょうがそっちを使っといたほうが良さそうかな。



ぴょぴょ(科学太郎)

リンク

2014/9/5(Fri) 05:46:43|NO.64690

osakanaさん
> プロセスの終了を監視するには
> CreateProcess で実行ファイルを起動し WaitForSingleObject で終了を待つやり方と
> プロセスハンドルから GetExitCodeProcess で終了を監視する方法があるようです。
色々調べると確かに CreateProcess と WaitForSingleObject の組み合わせで
終了を待つことが可能ですね。

> WaitForSingleObject で待つやり方はスレッドが停止してしまって
> HSPでは他の処理ができなくなるためプロセスを分けるなど工夫が必要そうなので、
> GetExitCodeProcess で待つ方法でスクリプトを作ってみました。
そうですよね。
スレッドが止まっても stop、wait、await 命令とは異なりHSPが処理されない。
普通に繰り返しで終了を待つと時計などの他の処理が実行されないので、
タイマー処理で GetExitCodeProcess の戻り値をチェックするようにします。

> おまけで実行ファイル名からプロセスIDを取得
> これも全てのプロセスから一致する物を調べてる。
この方法は、繰り返しなどで監視しなくても良い簡単な方法ですが、
同じメモ帳ソフトを複数起動してると正しく取得できそうにありませんね。
やはり、起動したプロセスのプロセスIDを直接取得する方が良さそうです。

> 参考になれば幸いです。
でも参考になりました。



woodfieldsさん
COMインターフェイスは良く知りませんが、
見るととてもシンプルに実現できそうですね。

> ほぼ解決だと思いますが、念のために起動、終了をコントロールするならこんな感じでよいかも。
はい。解決ですね。

今回は、プロセスの起動・終了のコントロールではなく
プロセスの起動経過時間を監視して「ソフトの実行時間」を報告する機能、そして、
プロセスが終了したら「ソフトの実行時間」の監視を解除することを考えてました。



ぴょぴょ(科学太郎)

リンク

2014/9/5(Fri) 05:47:25|NO.64691

問題は解決しましたので、サンプルを載せておきます。
;Win32API関数の命令登録
#uselib "Shell32.dll" #cfunc ShellExecuteEx "ShellExecuteExA" var #uselib "User32.dll" # func SetTimer "SetTimer" int,int,int,int # func KillTimer "KillTimer" int,int #uselib "Kernel32.dll" #cfunc GetTickCount "GetTickCount" # func GetExitCodeProcess "GetExitCodeProcess" sptr,var # func CloseHandle "CloseHandle" sptr ;Win32API関数の記号定数 #const NULL $0000 #const WM_TIMER $0113 #const STILL_ACTIVE $0103 #const SW_SHOWNORMAL $0001 #const SEE_MASK_NOCLOSEPROCESS $00000040 ;列挙定数(構造体メンバ) #enum sei_cbSize=0 #enum sei_fMask #enum sei_hWnd #enum sei_lpVerb #enum sei_lpFile #enum sei_lpParameters #enum sei_lpDirectory #enum sei_nShow #enum sei_hInstApp #enum sei_lpIDList #enum sei_lpClass #enum sei_hkeyClass #enum sei_dwHotKey #enum sei_hIcon ;共用体メンバ(DUMMYUNIONNAME) #enum sei_hMonitor=sei_hIcon ;共用体メンバ(DUMMYUNIONNAME) #enum sei_hProcess #enum SIZE_SHELLEXECUTEINFO ;列挙定数(タイマーID) #enum TID_CLOCK=0 #enum TID_ALARM #enum TID_CHIME #enum TID_TIMER #enum TID_WATCH #enum TID_SOFTTIME ;ソフトの起動経過時間を監視 ;プロセスの起動経過時間を報告 *Init ;ソフトのタイトル名 n=-1 n++:SoftTitle(n)="電卓" n++:SoftTitle(n)="メモ帳" n++:SoftTitle(n)="ペイント" n++:SoftTitle(n)="ワードパッド" n++:SoftTitle(n)="HSPアシスタント" n++:SoftTitle(n)="HSPスクリプト・エディタ" n++:SoftTitle(n)="ドキュメント・ブラウザ" ;ソフトのフルパス名 n=-1 n++:SoftFname(n)="C:\\Windows\\System32\\calc.exe" n++:SoftFname(n)="C:\\Windows\\System32\\notepad.exe" n++:SoftFname(n)="C:\\Windows\\System32\\mspaint.exe" n++:SoftFname(n)="C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe" n++:SoftFname(n)="D:\\hsp332\\hspat.exe" n++:SoftFname(n)="D:\\hsp332\\hsed3.exe" n++:SoftFname(n)="D:\\hsp332\\hdl.exe" ;ソフトのプロセス・ハンドル SoftStart(0)=0 ;プロセスの起動時間 SoftHandle(0)=0 ;プロセスのハンドル SoftNumber(0)=0 ;ソフトのボタン番号 *Main title "プロセスの起動経過時間サンプル" font MSGOTHIC,16 objsize 150,24 foreach SoftTitle button gosub SoftTitle(cnt), *SoftExecute loop ;割り込みの設定 oncmd gosub *OnTimer,WM_TIMER ;WM_TIMERの割り込み登録 onexit goto *OnQuit ;プログラム終了時の登録 stop ;登録ソフトの起動処理 *SoftExecute dim sei,SIZE_SHELLEXECUTEINFO sei.sei_cbSize = (SIZE_SHELLEXECUTEINFO*4) sei.sei_fMask = SEE_MASK_NOCLOSEPROCESS sei.sei_hWnd = hWnd sei.sei_lpVerb = NULL sei.sei_lpFile = varptr(SoftFname(stat)) sei.sei_lpParameters = NULL sei.sei_lpDirectory = NULL sei.sei_nShow = SW_SHOWNORMAL ;登録ソフトの実行 SoftNo=stat if(ShellExecuteEx(sei)==0){ dialog "登録ソフトが起動できませんでした。",1,"確認" return } ;プロセス・ハンドルの登録 NotFound=1 foreach SoftHandle if(SoftHandle(cnt)==NULL){ SoftStart(cnt)=GetTickCount() SoftHandle(cnt)=sei.sei_hProcess SoftNumber(cnt)=SoftNo NotFound=0 break } loop ;プロセス・ハンドルの追加登録 if(NotFound){ n=length(SoftHandle) SoftStart(n)=GetTickCount() SoftHandle(n)=sei.sei_hProcess SoftNumber(n)=SoftNo } SetTimer hWnd,TID_SOFTTIME,1000,NULL return ;WM_TIMERメッセージの処理 *OnTimer if(wParam==TID_SOFTTIME){ sdim msgbox redraw 0 color $FF,$FF,$FF:boxf color $00,$00,$00 CurrentTime=GetTickCount() foreach SoftHandle if SoftHandle(cnt){ GetExitCodeProcess SoftHandle(cnt),n if(n==STILL_ACTIVE){ if((CurrentTime-SoftStart(cnt))>10*1000){ SoftStart(cnt)=CurrentTime msgbox+=SoftTitle(SoftNumber(cnt))+"\n" } GetSoftTime (CurrentTime-SoftStart(cnt)),hour,min,sec,hsec sdim msg msg+=strf(" SoftHandle(%2d)",cnt) msg+=strf(" %03d:%02d.%02d",hour,min,sec) msg+=strf("⇒%s",SoftTitle(SoftNumber(cnt))) pos 150,cnt*16:mes msg } else{ CloseHandle SoftHandle(cnt) SoftHandle(cnt)=NULL } } loop redraw 1 ;報告 if peek(msgbox,0){ msgbox+="\n" msgbox+="以上のソフトが10秒を経過しました。\n" dialog msgbox,0,"お知らせ" } } return 0 ;ソフトの起動経過時間を取得 #deffunc GetSoftTime int _time_,var _hour_,var _min_,var _sec_,var _hsec_ n=_time_ _hsec_ = (n \ 1000):n/=1000 _sec_ = (n \ 60):n/=60 _min_ = (n \ 60):n/=60 _hour_ = (n \ 1000):n/=1000 return ;終了時の処理 *OnQuit KillTimer hWnd,TID_SOFTTIME ;タイマーの破棄 end ;EOF



ONION software Copyright 1997-2021(c) All rights reserved.