9

I can easily get Explorer to open up and select a file using the following :

string argument = @"/select, " + filePath;
System.Diagnostics.Process.Start("explorer.exe", argument);

However when I come to opening up the next file, I will get a new instance of explorer. This could result in our users opening having hundreds of explorers open by the end of an intensive day.

How can I get it to reuse an already open explorer instance to select the file I want?

Visual Studio can do this when you right click on a tab and select Open containing folder... providing explorer is already opened at the same directory. How has it been able to do this?

Mongus Pong
  • 11,337
  • 9
  • 44
  • 72
  • 3
    Why do you have to use explorer in the first place? What are you trying to achieve? – ChrisBint Nov 18 '11 at 12:35
  • Why not use `System.Diagnostics.Process.Start(filePath)` ?That way you open the file with the assigned default program. – Alex Nov 18 '11 at 12:37
  • @ChrisBint Our app has clients. Each client has a folder where their various documents are stored. We want the option to open explorer at the clients folder so they can browse / edit / copy etc... the clients documents. – Mongus Pong Nov 18 '11 at 12:42
  • Nice, I never noticed that VS will try and reuse the same window. Also, there's nothing wrong with the question as is, opening explorer rather than the file has a lot of uses (hence why it's in VS). – Ray Nov 18 '11 at 12:43
  • @Alex, I want to open explorer so the user can browse rather than any particular file.. – Mongus Pong Nov 18 '11 at 12:45
  • This requires Automation support, hard to come by. There is some left over from the days when they tried to turn the shell into a browser. ShDocVw.ShellWindows returns a list of both IE and shell windows. No idea how far you'll get with this or how well this is still supported. – Hans Passant Nov 18 '11 at 14:05
  • 1
    The right answer is to use SHOpenFolderAndSelectItems http://stackoverflow.com/questions/3010305/programmatically-selecting-file-in-explorer/3010871#3010871 – rogerdpack Mar 24 '14 at 20:30

5 Answers5

4

How about embedding the Explorer window into your application by using the ExplorerBrowser object? That way, you can just keep reusing the window by calling IExplorerBrowser::BrowseToObject on a different folder when you want to show a different folder.

Trying to renavigate an existing Explorer window is problematic because you don't know what the user did with that window while you weren't looking. Maybe they used the Folder pane to go to some other folder, and then boom you just ripped that folder out from under them and sent it somewhere else. Or maybe they closed it! Opening a new Explorer window is a fire-and-forget type of thing. If you want to retain control of the window, then you need to exercise more explicit control (e.g. via ExplorerBrowser above).

Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
  • Windows itself uses this behaviour of reusing opened explorer windows. For example selecting a folder and typing Ctrl+Enter to open it in a new window. If you do it again, it does not open a new window instance. – Ivan Ferrer Villa Aug 23 '13 at 15:12
  • That's not the same as renavigating an existing window. – Raymond Chen Aug 23 '13 at 15:25
  • well, then using then contextual menu in a shortcut (Windows 7) and clicking 'find target' or similar (I have spanish version). If the target file is in an open window, Windows reuses it. – Ivan Ferrer Villa Aug 26 '13 at 16:18
  • Right, but that is not a renavigate. It's not like it took your My Computer window and changed it to a My Documents window. It just took an existing window that was already open to the correct location. – Raymond Chen Aug 26 '13 at 17:21
  • Yes, really it's not the same. What I meant is valid only for files in the same location. (btw, I'd like to do this using .NET!) – Ivan Ferrer Villa Aug 30 '13 at 14:24
3

A bit late to the party, but if you want to select one or more files in a folder that already opened in an explorer window, you can do so with the shell32 method SHOpenFolderAndSelectItems. That method re-uses an existing window if there's one, or opens a new one otherwise.

[DllImport("shell32.dll")]
private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags);

[DllImport("shell32.dll")]
private static extern void SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, out IntPtr pidl, uint sfgaoIn, out uint psfgaoOut);

public static void ShowFileInExplorer(string folderPath, string filePath)
{
    Shell32.SHParseDisplayName(Path.GetFullPath(folderPath), IntPtr.Zero, out IntPtr folder, 0, out uint psfgaoOut);

    if (folder == IntPtr.Zero)
    {
        return;
    }

    Shell32.SHParseDisplayName(Path.GetFullPath(filePath), IntPtr.Zero, out IntPtr file, 0, out psfgaoOut);

    if (file != IntPtr.Zero)
    {
        IntPtr[] files = { file };

        Shell32.SHOpenFolderAndSelectItems(folder, (uint)files.Length, files, 0);
        Marshal.FreeCoTaskMem(file);
    }

    Marshal.FreeCoTaskMem(folder);
}

This sample only selects one file, but can easily be extended to select multiple files.

Domi
  • 272
  • 1
  • 3
  • 9
0

For similar purpose I use launchkey.exe "{F4}^AC:\TEMP{ENTER}" to send F4 key message and direct current explorer instance to c:\TEMP folder.

I run it from right-click shell context menu item:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\85TEMP]
"Position"="Top"
@="JUMP TO TEMP"

[HKEY_CLASSES_ROOT\Directory\Background\shell\85TEMP\command]
@="c:\\launchkey\\launchkey.exe \"{F4}^AC:\\TEMP{ENTER}\""

"JUMP TO TEMP" is displayed item name
"85*" exists to let keep order among other "jumps" which I have several. Items are ordered in reverse by key name, hence number in front.

0

Have you tried getting an instance of the Process class and always using that to call the Start() method?

Process myProcess = new Process();
string argument = @"/select, " + filePath;

myProcess.StartInfo.FileName = argument;
myProcess.Start();
Widor
  • 13,003
  • 7
  • 42
  • 64
  • This works if its the same path as the previous call. Opens a new window on different paths thoug. – Alex Nov 18 '11 at 12:56
  • According to [the documentation](http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx), `Start()` will "start (or reuse) the process resource". So that implies that you can change the value of `myProcess.StartInfo.FileName` and call `Start()` again? – Widor Nov 18 '11 at 12:58
  • 1
    @Widor This is not an answer, rather a question. Without confirming if it works from the answerer, it is a pure speculation. – Marek May 26 '14 at 10:44
  • Won't work that way. You have to specify the command that has to be executed in `FileName` NOT the argument! The arguments are specified in the `Arguments`-Property. – DrCopyPaste Aug 06 '14 at 13:54
0

Another approach is the Win32 API way.

StringBuilder sb = new StringBuilder(@"C:\temp\");
 hWnd = (...) // get Handle of the Explorer's address field.
SendMessage(hWnd, WM_SETTEXT, new IntPtr(sb.ToString().Length), sb) 

// Set the focus to the address field

SendMessage(hWnd, WM_SETFOCUS, IntPtr.Zero, null)
// Simulate Enter key
SendMessage(hWnd, WM_KEYDOWN, new IntPtr(VK_RETURN), null)
Alex
  • 7,901
  • 1
  • 41
  • 56
  • 2
    There is no portable way of obtaining the address field, and you can't reliably simulate input by sending messages. – Raymond Chen Nov 18 '11 at 14:56
  • 1
    You can enumerate all child windows of the explorer process and by checking the CLASS, find out the address field. Have a look at Spy++. – Alex Nov 18 '11 at 15:31
  • 3
    Hunting for a specific CLASS is not portable. The class name of the address bar has changed over the years, and it may very well change again in the future. – Raymond Chen Nov 18 '11 at 15:48