Home > database >  Windows Explorer Opened by VBA Shell Command Not On Top
Windows Explorer Opened by VBA Shell Command Not On Top

Time:10-04

I'm working with an older MS Access application in which a "Show File in Folder" function has been implemented.

This function uses this basic strategy

vPID = Call Shell("explorer.exe /select," & FileFullPathName, vbNormalFocus)

AppActivate vPID

For the most part, this works fine. However, I have several users complaining that the window that opens is always behind other windows. All of the users with this complaint have had their machines patched to the latest and greatest Windows 10. I have been able to duplicated this on a similar machine. The problem is most prevalent when there are a number of Explorer windows open a the the time the user clicks the "Show File in Folder" button.

My various searches have revealed several Windows API functions that sound like they should work (BringWindowToTop, SetForegroundWindow, SwitchToThisWindow (deprecated as far as I can tell), SetWindowPos, ShowWindow). I think I understand the differences and the one I should be focusing on is BringWindowToTop.

I have done a number of test implementations of this, but they are best summed up by the content of this post: How to set a windows explorer window as the active window.

For the time being I'm simply ignoring window cleanliness and how many windows a user might have open, etc. If I construct the following:

'Declarations

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _ 
    (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function BringWindowToTop Lib "user32" _
    (ByVal hWnd As Long) As Long

'Then set the following up in a button
Dim strFile as String
dim strPath as String
dim strPathSplit() as String
dim lngWindow as Long

strFile = "C:\folder1\folder2\folder3\foo.txt"
strPath = "C:\folder1\folder2\folder3\"

strPathSplit = Split(strPath, "\")

Shell "explorer.exe /select," & strFile, vbNormalFocus

lngWindow = FindWindow("CabinetWClass", strPathSplit(UBound(strPathSplit) - 1))

BringWindowToTop lngWindow

AppActivate strPathSplit(UBound(strPathSplit) - 1)

I get a non-zero window handle back from FindWindow. But the explorer window that I opened with the Shell command is stubbornly in the background. It is flashing on the taskbar, but I still have to notice it and click it to get it on top. (It does open correctly when I do that.)

I have tried several variants using the other Windows API functions and gotten similar results.

I would muchly appreciate it if anyone could point out what I'm doing wrong, or point me at the right technique to accomplish this. I know it's very possible to do in other applications that have similar "Show File in Folder" functions, but I'm also aware that those were written in other languages and may have access to functions I do not.

Thanks in advance!

CodePudding user response:

You should use conditional compilation to check which version of Windows you're on and change your declarations appropriately. 32bit Windows handles Long types differently to 64bit:

#If Win64 Then
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" _
               (ByVal hWnd As LongPtr) As LongPtr
#Else
    Private Declare Function SetForegroundWindow Lib "user32" _
               (ByVal hWnd As Long) As Long
#End If

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _ 
        (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Then seeing as you already have the window handle you can just use:

SetForegroundWindow lngWindow

CodePudding user response:

You aren't checking the return value from BringWindowToTop. You should do that. If you check the return value from (some of the) function, it will tell you that the attempt failed.

The flashing taskbar indicates that the Window was correctly notified, but can't be brought do the top because some other window won't let it come up.

Nothing you do to the file dialog window will bring it to the top -- the problem is that it's not attached to the active process.

Conditions for success are described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow

If Office / Win10 has been re-engineered to have this behavior, a solution will be to find the handle of the process which has the foreground, and use that to create the new dialog, or use it to release focus and push it to the background, because two processes trying to be in the foreground at the same time doesn't work.

In the past this was often caused by the user clicking twice when only once was required -- grabbing focus. You should check that isn't happening.

  • Related