VBAで開いたフォルダをVBAで閉じるには
- [記事公開]2022.08.12[最終更新]2022.08.20
- 知恵袋
- OutlookVBA, Yahoo!知恵袋
Yahoo!知恵袋で次のような質問がありました。
前月に知恵袋で教えていただき、outlook vbaで添付ファイルを任意のフォルダへ保存するところまではできました。 その保存されたフォルダを開けて、保存されているかどうかを確認する為shellを使ってフォルダを表示しましたが、確認後にフォルダを閉じたいのですがどこにどう書けばいいのかわかりませんので、教えていただけますでしょうか。 ちなみにフォルダを閉じる前にmsgboxでOK?表示のうえ、OKボタンを押したらフォルダが閉じるという動きにしたいです。 よろしくお願いいたします。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12265854167
Sub openFolder(path As String)
Shell "C:\Windows\Explorer.exe " & path, vbNormalFocus
End Sub
'----------------
Sub test_openFolder()
Call openFolder("任意のフォルダ保存先")
End Sub
Shellで起動しているのに戻り値を用意していないから、閉じるときに指定できなくて困っているだけで、
lPtr = Shell("C:\Windows\Explorer.exe " & path, vbNormalFocus)
とすれば簡単じゃね?と思ったのですが、ことはそう簡単ではありました。
Shell戻値で閉じるやり方
最初に考えたのが、Shell戻値で閉じるやり方です。
こんな感じ。
Sub openFolder(path As String)
Dim lPtr As Long
Dim iMsg As Integer
lPtr = Shell("C:\Windows\Explorer.exe " & path, vbNormalFocus)
iMsg = MsgBox("終了させますか", vbYesNo + vbDefaultButton2 + vbExclamation)
If iMsg = vbYes Then
AppActivate lPtr
VBA.Interaction.SendKeys "%{F4}"
End If
End Sub
'----------------
Sub test_openFolder()
Call openFolder("C:\temp")
End Sub
あらかじめShellの戻り値を把握しておいて、AppActivateでアクティブにした後、SendKeyでAlt+F4で終了するやり方です。
このやり方だと、エラーになりました。
エラーになった個所はここ。
理由はよく分かりません。引数の指定が悪かったかとCstrでlPtrを文字列にしてみたけど、やはりエラーでした。
Outlookだからか?と思い、ExcelVBAでも同じコードを試してみたけど、結果は同じ、エラーになりました。
こんなご意見もあり、
AppActivateというのは確実性のないものだというだけは分かりました。
うまくいかない
いろいろ試しましたがうまくいきません。
SendMessageを使うやり方
こちら↑で紹介されていたSendMessageを使うやり方を試しました。
Option Explicit
Declare PtrSafe Function SendMessage Lib "user32.dll" _
Alias "SendMessageA" _
(ByVal hwnd&, _
ByVal wMsg&, _
ByVal wParam&, _
ByVal lParam&) As Long
Public Const SC_CLOSE = &HF060
Public Const WM_SYSCOMMAND = &H112
Sub openFolder(path As String)
Dim lPtr As Long
Dim iMsg As Integer
lPtr = Shell("C:\Windows\Explorer.exe " & path, vbNormalFocus)
iMsg = MsgBox("終了させますか", vbYesNo + vbDefaultButton2 + vbExclamation)
If iMsg = vbYes Then
Call SendMessage(lPtr, WM_SYSCOMMAND, SC_CLOSE, 0)
End If
End Sub
'--------------
Sub test_openFolder()
Call openFolder("C:\temp")
End Sub
これはエラーにはならないけど、フォルダは開いたままでした。orz
https://teratail.com/questions/36610
↑こちらの方法2のやり方も、結局はSendMessageでして、うまくいきませんでした。
Shell.Applicationを使うやり方
こちら↓で紹介されていたShell.Applicationを使うやり方も試しました。
Sub openFolder(path As String)
Dim iMsg As Integer
Dim w As Object
Dim Shl As Object
Set Shl = CreateObject("Shell.Application")
Shell "C:\Windows\Explorer.exe " & path, vbNormalFocus
iMsg = MsgBox("終了させますか", vbYesNo + vbDefaultButton2 + vbExclamation)
If iMsg = vbYes Then
For Each w In Shl.Windows
If (InStr(TypeName(w.document), "IShellFolderView") > 0) Then w.Quit
Next w
End If
End Sub
'---------
Sub test_openFolder()
Call openFolder("C:\temp")
End Sub
これを実行したら、やはりエラーにはならないけどフォルダは開いたままでした。
しかし、途中でローカルウィンドウで確認したら、w.documentのTypenameは確かにIShellFolderViewDual3となっていたので、w.Quitがうまくいっていないことが分かりました。
原因は不明です。
うまくいった方法
結局うまくいったのはプロセスからフォルダエクスプローラーを検索して強引にプロセスをkillしてしまう方法だけでした。
Excel VBA – Excel VBA:複数の開いているエクスプローラーウィンドウから特定のエクスプローラーウィンドウを閉じる
ExcelVBAとなっているけど、Outlookでもいけました。
Sub openFolder(path As String)
Dim objWMIcimv2 As Object, objProcess As Object, objList As Object
Dim intError As Integer
Dim iMsg As Integer
Shell "C:\Windows\Explorer.exe " & path, vbNormalFocus
iMsg = MsgBox("終了させますか", vbYesNo + vbDefaultButton2 + vbExclamation)
If iMsg = vbYes Then
Set objWMIcimv2 = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set objList = objWMIcimv2.ExecQuery("select * from win32_process where name='explorer.exe'")
For Each objProcess In objList
intError = objProcess.Terminate
If intError <> 0 Then Exit For
Next
End If
Set objWMIcimv2 = Nothing
Set objList = Nothing
Set objProcess = Nothing
End Sub
'-----
Sub test_openFolder()
Call openFolder("C:\temp")
End Sub
これで実行すると、指定のフォルダはもちろん、すでに開いておいたフォルダも全部閉じてしまいます。
開いているフォルダのタイトルとかWindowHandleをゲットすればさらに細かくフォルダを指定して閉じることができるでしょうけど、これって結局タスクマネージャーを立ち上げて強制終了しているのと同じな訳で、これを組み込むのはどうなのよ・・・・と思ったので知恵袋に回答するのはやめました。
まとめ
対象がOutlookだったのもありますが、今回は全然うまくいかなかったなという挫折感が大きいです。
しかし、フォルダを閉じるのをVBAでやる必要性って何だろうと考えてしまいました。
こんな↓意見もありました。
https://oshiete.goo.ne.jp/qa/4540635.html
5000ファイルを溜め込む処理(どういう間隔で? 1日で?)を見直すのがよいかもしれませんし、また、目視で5000ファイル全てを確認するのではないでしょうから、フォルダを開かずに目的のファイルだけを直接開けばよいかもしれません。
やはりフォルダを開かざるを得ないとしても、キャッシュを有効にするためにはフォルダを開いて閉じなくてもスクリプトでフォルダ内を空読みすればよさそうな気がします。
一般論として、OSへの要求(本件の場合フォルダを開いて閉じる)は避けられるものなら避けた方がシステム全体の性能のためには良いと思います。
https://oshiete.goo.ne.jp/qa/4540635.html
これは、
Win2kサーバーをファイルサーバーとして利用しているのですが、
https://oshiete.goo.ne.jp/qa/4540635.html
1つのフォルダ内に5000ファイルほどをためてしまう構造になっています。(サーバの他のフォルダからスクリプト(?)のようなものを使って、自動的にそのフォルダにデータがたまるようになっています。)
そのためなのかは、よくわからないのですが、そのフォルダをサーバー上で開くと、数十秒近く時計マークがでてなかなかフォルダ内のデータを確認できません。一度そのフォルダを開くと、その後は瞬時に開くようになります。(キャッシュの問題では?と同僚から言われております。)
他の処理にも何となくですがレスポンスが悪くなっているような気がします
構造的な問題があるかもしれないのですが、このレスポンスの悪さを解消するために、データを移動させるスクリプトにデータ移動後、フォルダを開いて、閉じるという内容を加えたいと思っております。
という目的で自動でフォルダを閉じる機能を実装したいという質問者に対するアドバイスなんですが、確かに自動でフォルダを閉じるより、フォルダを空読みする方がシステムに対する負荷は小さいですよね。
-
前の記事
国民年金保険料の告示期間全期間前納について 2022.08.11
-
次の記事
集合グラフの作り方【Excel】 2022.08.13