ページ 1 / 1
ファイルの終了について
Posted: 2008年6月21日(土) 22:57
by jacoby
アプリケーションのヘルプ・メニューからヘルプ(テキストファイル)を
「ShellExecute」APIを使って表示させるようにプログラムを書きました。
それ自体はうまくいったのですが、
ヘルプファイルを開いたまま本体であるアプリケーションを終了させた時
そのヘルプが残ったままという現象に気づきました。
ShellExecuteの戻り値で開いたファイルのインスタンス・ハンドルが
分かるのでそれを使ってどうにかファイルを終了させられないかと
思うのですが、ファイルの終了の方法が分からず。
「アプリケーションを終了時にヘルプファイルが開いたままの場合
それを終了させる」ことの実装について、お知りの方がおられましたら
教えて下さい。
CloseHandleでは閉じてくれない?
Posted: 2008年6月23日(月) 04:33
by jacoby
ヘルプ・ファイルの「インスタンス・ハンドル」を入れるためのグローバル変数(hHelpFile)、を用意してヘルプの表示時に
コード: 全て選択
hHelpFile=ShellExecute(hMainWnd,"open",readMe_FullPathName,NULL,NULL,SW_SHOW)
として、
もしアプリ終了時にヘルプが開いたままならそれを閉じるために
メイン・ウインドウのデストロイ・イベントで
コード: 全て選択
CloseHandle(hHelpFile)
としてみたのですが、これではヘルプを閉じてくれないようです。
試しに
コード: 全て選択
DestroyWindow(hHelpFile)
としてもみたのですがこれもダメ。
ウインドウハンドルじゃなくて「インスタンス・ハンドル」ってのが
そもそもどうなんだろうかなと思っているのですが。。。
Posted: 2008年6月23日(月) 09:35
by ゲスト
ShellExecute/ShellExecuteExで得られるHINSTANCEはエラーを得るため以外には使えないらしいですが
ShellExecuteの変わりに
CreateProcessかShellExecuteEx使ってみては
Posted: 2008年6月23日(月) 11:16
by ゲスト
ヘルプファイルを開いたときに、GetActiveWindow 関数(API)でウインドウハンドルを取得するのはどうでしょうか。
レスありがとうございます。
Posted: 2008年6月23日(月) 22:33
by jacoby
ShellExecute/ShellExecuteExで得られるHINSTANCEはエラーを得るため以外には使えないらしいですが
ShellExecuteの変わりに
CreateProcessかShellExecuteEx使ってみては
レスありがとうございます。
自分の調べた限りですが、CreateProcessは「実行ファイル」を開くのに
使われる命令のようで、その他のファイルはShellExecuteが一般的との
印象です。
ShellExecuteExはShellExecuteの拡張版とも言えるもののようなので
やはり元のShellExecuteで出来る筈じゃないかなとも思っています。
それから、GetActiveWindow 関数ですが、もしもShellExecuteの設定で
ファイルをアクティブなウインドウとして開かなかった場合にはどうなるのかなと、、
まだ実際に自分で試してないので何とも言えないのですが。。。
Posted: 2008年6月24日(火) 00:16
by イグトランス
正攻法はWinHelp関数やHtmlHelp関数を使うことだと思いますが、自分も使ったことないので正直よく分かりません。
http://www.kumei.ne.jp/c_lang/sdk3/sdk_284.htm
CreateProcessやShellExecuteExが勧められたのは、共に(ShellExecuteと違って)プロセスハンドルを取得できるためでしょう。プロセスハンドルがあれば、TerminateProcess関数で強制終了が可能だからです(一般的に勧められませんが)。TerminateProcessを使わないにしても、プロセスハンドルやプロセスIDが分かれば、それを基に対象のウィンドウハンドルを探せます。そうすれば、そこへWM_CLOSEを送るなど穏便な手段で終了を依頼できます。
ShellExecuteがHINSTANCEを返す理由は、16ビットWindowsではHISNTANCEが今で言うところのプロセスハンドルに近い役割を果たしていたからです。
Posted: 2008年6月24日(火) 02:00
by jacoby
CreateProcessやShellExecuteExが勧められたのは、共に(ShellExecuteと違って)プロセスハンドルを取得できるためでしょう。プロセスハンドルがあれば、TerminateProcess関数で強制終了が可能だからです(一般的に勧められませんが)。TerminateProcessを使わないにしても、プロセスハンドルやプロセスIDが分かれば、それを基に対象のウィンドウハンドルを探せます。そうすれば、そこへWM_CLOSEを送るなど穏便な手段で終了を依頼できます。
レスありがとうございます。
まだ中々内容の理解が追いつかないのですが、
「プロセスハンドル」及び「プロセスID」というものが(二つとも)必要という
ことでしょうか。
それについてWeb上で他に調べていて、このような記事を見つけました。
http://okwave.jp/qa566523.html
この記事からはShellExecuteExでは「プロセスハンドル」は取得できるものの
「プロセスID」は取得出来なさそうな内容で、
「CreateProcessならプロセスIDを取得出来る」とあります。
ただし確か↓ページの説明では
http://detail.chiebukuro.yahoo.co.jp/qa ... 1310043483
CreateProcessで起動できるのは「実行ファイル」だけなので、
ShellExecuteのようにデータファイルを渡してそれに関連付けされている
アプリケーションを起動するということはできません。
ですので、メモ帳の実行ファイル名と開きたいテキストファイルの名前を
渡してやらなければなりません。
とあり、テキストファイルを「メモ帳」で開くことがあらかじめ前提となっていなくては
ならないような印象も受けます。
うーん、、あれこれ調べてはいるんですけど、何れも大変な処理になりそうな
感じで、何だかヘルプをShellExecute(Ex)やCreateProcess等で開くこと
自体がそもそも不自然なのかもと思えてきます。
そこでイグトランスさんの書かれていた、
正攻法はWinHelp関数やHtmlHelp関数を使うことだと思いますが、
ですが、これについては今、読ませてもらっています。これが一般的な方法
なのでしょうか。(ただHTML形式のヘルプを作らなければならないようですね。。)
でも、教えていただいて有難うございます。
それにしてもヘルプをShellExecuteで表示したときに比べて、後始末のほうは
何て複雑なんだろうと、、ABのプロジェクトエディターがしっかり開いたままの
ヘルプを閉じて終了する様を眺めつつ、思っています。
ゲストさん、イグトランスさん、レスありがとうございました。
またよろしくお願いします。
Posted: 2008年6月24日(火) 09:09
by イグトランス
そうです。ShellExecuteExではプロセスIDが取得できませんが、プロセスハンドルが分かれば、GetProcessId関数でプロセスIDは容易に判明します。言葉足らずでした、すみません。プロセスハンドルとプロセスIDは2つ必要というわけではなく、関数によってどちらを引数に取るかが異なるので、場合によってどちらを使うか変わってくるというだけです。
CreateProcessでも、自分で関連付けされているアプリケーションのパスを調べてくればいいはずなのですが、結構面倒そうなんですよね。
Posted: 2008年6月28日(土) 13:38
by ゲスト2
> プロセスハンドルやプロセスIDが分かれば、それを基に対象のウィンドウハンドルを探せます。
イグトランスさん、プロセスIDからウィンドウハンドルを探す方法を具体的に教えていただけるとうれしいのですが。
Posted: 2008年6月28日(土) 15:39
by konisi
EnumWindowsとGetWindowThreadProcessIdを上手く使えばいけたと思います。
Posted: 2008年7月01日(火) 08:41
by ゲスト2
> EnumWindowsとGetWindowThreadProcessIdを上手く使えばいけたと思います。
konisiさんありがとうございました。何とかなりそうです。
不具合を修正しコードを変更しました。
Posted: 2009年1月04日(日) 13:06
by 田中 宏
AB4.24で外部機器との通信プログラムでヘルプとしてpdfファイルを表示した時に作成したものです。
Dim pi As PROCESS_INFORMATION 'CreateProcess実行時に取得する構造体
Dim hApplWnd As HWND
Function EnumWindowsProc(hWnd As HWND, lParam As LPARAM) As LRESULT 'CreateProcessで起動したアプリのHWNDを取得する関数
Dim lpdwProcessId As DWord
GetWindowThreadProcessId(hWnd, VarPtr(lpdwProcessId)) 'EnumWindowsで得たhWndのProcessIdを取得し
If pi.dwProcessId = lpdwProcessId Then 'CreateProcessで取得したProcessIdと同じ
hApplWnd = GetWindow(hWnd, GW_OWNER) 'hWndの親hWndを取得する
If hApplWnd = 0 Then hApplWnd = hWnd
End If
EnumWindowsProc = TRUE '全てのhWndをチェック
End Function
'終了イベント
Sub MainWnd_IDM_EXIT_MenuClick()
If pi.hProcess > 0 Then
TypeDef WNDENUMPROC = *Function(hWnd As HWND, lp As LPARAM) As BOOL
Declare Function EnumWindows Lib "user32" (lpEnumFunc As WNDENUMPROC, lParam As LPARAM) As BOOL
Dim lParam As LPARAM
EnumWindows(AddressOf(EnumWindowsProc), lParam)
SendMessage(hApplWnd, WM_CLOSE, 0, 0)
CloseHandle(pi.hProcess)
CloseHandle(pi.hThread)
End If
SendMessage(hMainWnd,WM_CLOSE,0,0)
End Sub
2月21日追記 エラー報告
このコードではWindowが最小化されていると正常に機能しません。
タスクマネージャで終了しなければならない事になります。
手動(マウス)で元に戻すと正常に機能するのですが OpenIcon で元に戻したのでは
改善されません。
2月24日不具合を修正しコードを変更しました。
インスタンスハンドルからウインドウハンドルを取得する
Posted: 2009年1月18日(日) 15:49
by げすと5
日本語が怪しげなのと、VBらしきコードなので・・・あれですが
下記が参考になると思います。
http://support.microsoft.com/kb/242308/ja
ABに書き直しても、それほど手間ではないと思います。