1

I need to open the specific folder for a file and I am doing it with:

file = Directory.GetFiles(filepath,Filename,
                          SearchOption.AllDirectories).FirstOrDefault()

   Process.Start("explorer.exe", "/select," & file.ToString)

This code is immediately opening the folder which is already fully loaded, but it doesnt seem enabled, endeed I cant do any action in it. The form is not freezing. Thanks

Mattia
  • 258
  • 7
  • 25
  • 1
    Why did you delete [the original question](https://stackoverflow.com/q/64420375/8967612) and reposted it? The title of _this_ question says that the form freezes but the body says "the form is not freezing", so which one is it? What is the actual problem here? – 41686d6564 stands w. Palestine Oct 19 '20 at 04:13
  • I've tested basically the same code and it works fine for me. The Explorer window opens quickly (`EnumerateFiles` was quicker than `GetFiles` in my test case), the correct file was selected and the window was fully functional. I thought that maybe quotes were required if the path contained spaces but apparently not. There's something different about what you're doing compared to what I'm doing that is not discernible from the information that you've provided. You need to provide a [`minimal, reproducible example`](https://stackoverflow.com/help/minimal-reproducible-example). – jmcilhinney Oct 19 '20 at 04:45
  • @jmcilhinney Based on the OP's [comment](https://stackoverflow.com/questions/64420375/freezing-form-while-opening-a-windows-explorer-to-a-specific-file#comment113911994_64420375) in the other post, it appears that Explorer is what freezes and not the form. I couldn't reproduce this either but it's plausible considering the fact that `Process.Start()` will start a new process of Explorer. It's better to avoid that anyway as I explained in my answer. – 41686d6564 stands w. Palestine Oct 19 '20 at 04:52
  • 1
    Actually the other question has been closed because there were 2 questions eventhough I updated it before.. Anyway, sorry it was probably the late time, but the form doesn't freezes, just the explorer. – Mattia Oct 19 '20 at 12:11
  • 1
    @jmcilhinney I created a new empty project and I used my code. The explorer is still taking 4 seconds to be fully functional.. – Mattia Oct 19 '20 at 12:12

2 Answers2

2

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.

  • This is working great! Please have a look to [link](https://stackoverflow.com/questions/64427333/how-can-i-check-how-many-ms-take-for-a-downloadstring) – Mattia Oct 19 '20 at 12:52
  • Sorry but how can I check if the file already exist? With the example in my question I could do if file is nothign then file doesn't exist. How can I do something similiar here?= Thanks – Mattia Oct 19 '20 at 15:57
  • @Mattia You can (and should) do the same thing: `If Not String.IsNullOrEmpty(filePath) Then NativeMethods.OpenFolderAndSelectFile(filePath)`. Note that `filePath` refers to the path of the file, which is returned by `EnumerateFiles().FirstOrDefault()` and _not_ the original `filepath` variable you had in the question (which was actually a directory path, not a file path). – 41686d6564 stands w. Palestine Oct 19 '20 at 16:07
  • This is the solution but filepath needs to be declared at form scope. – Mattia Oct 21 '20 at 04:26
0

I would change the variable name file to something else. Maybe foundFile. After all File is the name of a class in System.IO and vb.net is not case sensitive. Your code works fine for me with the variable name change. Also got rid of the .ToString. I used .EnumerateFiles as commented by @jmcilhinney in the question you deleted. I purposely chose a path with all sorts of strange characters and it still worked.

Private Sub OPCode()
    Dim filepath = "C:\Users\xxxx\Documents\TextNotes\Dogs & Cats (Pets)"
    Dim Filename = "Specialty Vets.txt"
    Dim foundFile = Directory.EnumerateFiles(filepath, Filename,
                      IO.SearchOption.AllDirectories).FirstOrDefault()
    Process.Start("explorer.exe", "/select," & foundFile)
End Sub
Mary
  • 14,926
  • 3
  • 18
  • 27
  • To me, `folderPath`, `fileName` and `filePath` seem the logical variable names. – jmcilhinney Oct 19 '20 at 04:56
  • @jmcilhinney Yes, I left those names alone. It was `file = Directory.GetFiles` that I objected to. `file` as a variable name could conflict with the class, `File`. – Mary Oct 19 '20 at 05:01
  • 1
    I think that would only conflict, per se, if you tried to access a member of the `File` class, in which case you'd have to qualify the class name. It could certainly cause confusion though and I agree that, regardless of anything else, such names should be avoided for that reason. – jmcilhinney Oct 19 '20 at 05:10
  • 2
    Hi mary, to me it seems this code is basically identical to mine, and I still get the explorer freezing. The other question was closed by a moderator since I asked 2 question in one. – Mattia Oct 19 '20 at 12:15