|
|
2014/9/2(Tue) 20:24:26|NO.64638
現在、サイドバー・ランチャーを作成中です。
このランチャーの機能の1つでソフトを起動後、
ある一定の間隔でメッセージを出す仕組みを実装しようとしてます。
最初は、ランチャーで選択したソフトをタイマー処理などで
ある一定の間隔で報告することを考えてました。
この方法だとソフトを終了してもタイマー処理の中断は行われません。
これを実現するには、ランチャーで起動したソフトの監視が必要です。
そうなるとウインドウ・ハンドルを監視するのが一般だと思ういますが、
問題は、ランチャー(ShellExecute)で起動したソフトのウインドウ・ハンドルを
取得する方法が分かりません。
↑
この部分が知りたいと思います。
このウインドウ・ハンドルの監視以外のも
ソフトの終了を検出する方法を教えてほしいと思います。
よろしくお願いします。
|
|
2014/9/2(Tue) 20:25:33|NO.64639
> このウインドウ・ハンドルの監視以外にも
> ソフトの終了を検出する方法を教えてほしいと思います。
訂正。
|
|
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とか。
|
|
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
参考になれば幸いです。
| |
|
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
|
|
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
| |
|