ファイルの終了について

ActiveBasicでのプログラミングでわからないこと、困ったことなどがあったら、ここで質問してみましょう(質問を行う場合は、過去ログやWeb上であらかじめ問題を整理するようにしましょう☆)。
返信する
メッセージ
作成者
jacoby
記事: 106
登録日時: 2006年6月02日(金) 18:20

ファイルの終了について

#1 投稿記事 by jacoby »

アプリケーションのヘルプ・メニューからヘルプ(テキストファイル)を
「ShellExecute」APIを使って表示させるようにプログラムを書きました。
それ自体はうまくいったのですが、
ヘルプファイルを開いたまま本体であるアプリケーションを終了させた時
そのヘルプが残ったままという現象に気づきました。
ShellExecuteの戻り値で開いたファイルのインスタンス・ハンドルが
分かるのでそれを使ってどうにかファイルを終了させられないかと
思うのですが、ファイルの終了の方法が分からず。

「アプリケーションを終了時にヘルプファイルが開いたままの場合
それを終了させる」ことの実装について、お知りの方がおられましたら
教えて下さい。
jacoby
記事: 106
登録日時: 2006年6月02日(金) 18:20

CloseHandleでは閉じてくれない?

#2 投稿記事 by jacoby »

ヘルプ・ファイルの「インスタンス・ハンドル」を入れるためのグローバル変数(hHelpFile)、を用意してヘルプの表示時に

コード: 全て選択


hHelpFile=ShellExecute(hMainWnd,"open",readMe_FullPathName,NULL,NULL,SW_SHOW)
として、
もしアプリ終了時にヘルプが開いたままならそれを閉じるために
メイン・ウインドウのデストロイ・イベントで

コード: 全て選択


 CloseHandle(hHelpFile)
としてみたのですが、これではヘルプを閉じてくれないようです。
試しに

コード: 全て選択


DestroyWindow(hHelpFile)
としてもみたのですがこれもダメ。
ウインドウハンドルじゃなくて「インスタンス・ハンドル」ってのが
そもそもどうなんだろうかなと思っているのですが。。。
ゲスト

#3 投稿記事 by ゲスト »

ShellExecute/ShellExecuteExで得られるHINSTANCEはエラーを得るため以外には使えないらしいですが

ShellExecuteの変わりに
CreateProcessかShellExecuteEx使ってみては
ゲスト

#4 投稿記事 by ゲスト »

ヘルプファイルを開いたときに、GetActiveWindow 関数(API)でウインドウハンドルを取得するのはどうでしょうか。
jacoby
記事: 106
登録日時: 2006年6月02日(金) 18:20

レスありがとうございます。

#5 投稿記事 by jacoby »

ShellExecute/ShellExecuteExで得られるHINSTANCEはエラーを得るため以外には使えないらしいですが

ShellExecuteの変わりに
CreateProcessかShellExecuteEx使ってみては
レスありがとうございます。
自分の調べた限りですが、CreateProcessは「実行ファイル」を開くのに
使われる命令のようで、その他のファイルはShellExecuteが一般的との
印象です。
ShellExecuteExはShellExecuteの拡張版とも言えるもののようなので
やはり元のShellExecuteで出来る筈じゃないかなとも思っています。

それから、GetActiveWindow 関数ですが、もしもShellExecuteの設定で
ファイルをアクティブなウインドウとして開かなかった場合にはどうなるのかなと、、
まだ実際に自分で試してないので何とも言えないのですが。。。
最後に編集したユーザー jacoby [ 2008年6月24日(火) 02:29 ], 累計 1 回
イグトランス
記事: 899
登録日時: 2005年5月31日(火) 17:59
お住まい: 東京都
連絡する:

#6 投稿記事 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が今で言うところのプロセスハンドルに近い役割を果たしていたからです。
jacoby
記事: 106
登録日時: 2006年6月02日(金) 18:20

#7 投稿記事 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のプロジェクトエディターがしっかり開いたままの
ヘルプを閉じて終了する様を眺めつつ、思っています。

ゲストさん、イグトランスさん、レスありがとうございました。
またよろしくお願いします。
イグトランス
記事: 899
登録日時: 2005年5月31日(火) 17:59
お住まい: 東京都
連絡する:

#8 投稿記事 by イグトランス »

そうです。ShellExecuteExではプロセスIDが取得できませんが、プロセスハンドルが分かれば、GetProcessId関数でプロセスIDは容易に判明します。言葉足らずでした、すみません。プロセスハンドルとプロセスIDは2つ必要というわけではなく、関数によってどちらを引数に取るかが異なるので、場合によってどちらを使うか変わってくるというだけです。

CreateProcessでも、自分で関連付けされているアプリケーションのパスを調べてくればいいはずなのですが、結構面倒そうなんですよね。
ゲスト2

#9 投稿記事 by ゲスト2 »

> プロセスハンドルやプロセスIDが分かれば、それを基に対象のウィンドウハンドルを探せます。

イグトランスさん、プロセスIDからウィンドウハンドルを探す方法を具体的に教えていただけるとうれしいのですが。
konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

#10 投稿記事 by konisi »

EnumWindowsとGetWindowThreadProcessIdを上手く使えばいけたと思います。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
ゲスト2

#11 投稿記事 by ゲスト2 »

> EnumWindowsとGetWindowThreadProcessIdを上手く使えばいけたと思います。

konisiさんありがとうございました。何とかなりそうです。
田中 宏
記事: 11
登録日時: 2008年3月31日(月) 19:04
お住まい: 東京都

不具合を修正しコードを変更しました。

#12 投稿記事 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日不具合を修正しコードを変更しました。
最後に編集したユーザー 田中 宏 [ 2009年2月24日(火) 23:34 ], 累計 2 回
げすと5

インスタンスハンドルからウインドウハンドルを取得する

#13 投稿記事 by げすと5 »

日本語が怪しげなのと、VBらしきコードなので・・・あれですが
下記が参考になると思います。

http://support.microsoft.com/kb/242308/ja

ABに書き直しても、それほど手間ではないと思います。
返信する