I'll give you an answer in two parts...
Firstly, if the GetFiles()
call takes to long and freezes the form (which doesn't seem to be the current problem), you should do the following:
Use EnumerateFiles()
instead because in this case, FirstOrDefault()
will return immediately after finding a matching file, unlike GetFiles()
which will get all the files first before calling FirstOrDefault()
.
Wrap the call to EnumerateFiles()
in a Task.Run()
to execute it on a worker thread in case the search takes a little too long:
' Or:
' Private Async Sub SomeEventHandler()
Private Async Function ParentMethod() As Task
Dim filePath As String = Await Task.Run(
Function()
Return Directory.EnumerateFiles(dirPath, FileName, SearchOption.AllDirectories).
FirstOrDefault()
End Function)
' TODO: Use `filePath` to open the folder and select the file.
End Function
Secondly, do not use Process.Start("explorer.exe", "/select")
because a) it starts a new Explorer.exe process rather than opening the directory in the current one, b) it seems to be causing you some issues, and c) it has some limitations.
Instead, use the approach demonstrated in the answer linked in point (c) above. The code is in C# but it can be easily converted to VB. Here's the VB version of the code (with an additional overload).
Add the following class to your project:
Imports System.IO
Imports System.Runtime.InteropServices
Public Class NativeMethods
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Function SHOpenFolderAndSelectItems(
pidlFolder As IntPtr, cidl As UInteger,
<[In], MarshalAs(UnmanagedType.LPArray)> apidl As IntPtr(),
dwFlags As UInteger) As Integer
End Function
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Sub SHParseDisplayName(
<MarshalAs(UnmanagedType.LPWStr)> name As String,
bindingContext As IntPtr, <Out> ByRef pidl As IntPtr,
sfgaoIn As UInteger, <Out> ByRef psfgaoOut As UInteger)
End Sub
Public Shared Sub OpenFolderAndSelectFile(filePath As String)
Dim dirPath As String = Path.GetDirectoryName(filePath)
Dim fileName As String = Path.GetFileName(filePath)
OpenFolderAndSelectFile(dirPath, fileName)
End Sub
Public Shared Sub OpenFolderAndSelectFile(dirPath As String, fileName As String)
Dim nativeFolder As IntPtr
Dim psfgaoOut As UInteger
SHParseDisplayName(dirPath, IntPtr.Zero, nativeFolder, 0, psfgaoOut)
If nativeFolder = IntPtr.Zero Then
' Log error, can't find folder
Return
End If
Dim nativeFile As IntPtr
SHParseDisplayName(Path.Combine(dirPath, fileName),
IntPtr.Zero, nativeFile, 0, psfgaoOut)
Dim fileArray As IntPtr()
If nativeFile = IntPtr.Zero Then
' Open the folder without the file selected if we can't find the file
fileArray = New IntPtr(-1) {}
Else
fileArray = New IntPtr() {nativeFile}
End If
SHOpenFolderAndSelectItems(nativeFolder, CUInt(fileArray.Length), fileArray, 0)
Marshal.FreeCoTaskMem(nativeFolder)
If nativeFile <> IntPtr.Zero Then
Marshal.FreeCoTaskMem(nativeFile)
End If
End Sub
End Class
Then, you can easily call it like this:
NativeMethods.OpenFolderAndSelectFile(filePath)
Some additional notes:
You should choose meaningful variable names. filePath
should refer to the path of a file. If you want to refer to a folder/directory path, use something like dirPath
or folderPath
instead.
You don't need to call .ToString()
on a variable that's already of a String
type.