Open Windows Explorer directory, select a specific file (in Delphi)
Asked Answered
T

2

21

I have a procedure to open a folder in Windows Explorer that gets passed a directory path:

procedure TfrmAbout.ShowFolder(strFolder: string);
begin
   ShellExecute(Application.Handle,PChar('explore'),PChar(strFolder),nil,nil,SW_SHOWNORMAL);
end;

Is there a way to also pass this a file name (either the full file name path or just the name + extension) and have the folder open in Windows Explorer but also be highlighted/selected? The location I am going to has many files and I need to then manipulate that file in Windows.

Townsend answered 8/3, 2013 at 18:39 Comment(3)
Using the method as answered by Andreas Rejbrand below, I am hitting dead ends with strange activity, I'm on Win7 Pro 32bit and sometimes when using this, it either opens the directory but doesn't highlight the file, or it opens some out of the ordinary directory and highlights something completely irrelevant. For example, just now I used it to select a file, but it opened my personal user folder and highlighted "My Documents" folder, when the file I was directing it to isn't even on the same hard drive.Leora
I just noticed a trend with this issue, it happens when there are certain characters in the filename, for example a comma, which throws off the commandLeora
NOTE: My above issue was resolved with an update by Andreas including another more specific and reliable solution.Leora
U
55

Yes, you can use the /select flag when you call explorer.exe:

ShellExecute(0, nil, 'explorer.exe', '/select,C:\WINDOWS\explorer.exe', nil,
  SW_SHOWNORMAL)

A somewhat more fancy (and perhaps also more reliable) approach (uses ShellAPI, ShlObj):

const
  OFASI_EDIT = $0001;
  OFASI_OPENDESKTOP = $0002;

{$IFDEF UNICODE}
function ILCreateFromPath(pszPath: PChar): PItemIDList stdcall; external shell32
  name 'ILCreateFromPathW';
{$ELSE}
function ILCreateFromPath(pszPath: PChar): PItemIDList stdcall; external shell32
  name 'ILCreateFromPathA';
{$ENDIF}
procedure ILFree(pidl: PItemIDList) stdcall; external shell32;
function SHOpenFolderAndSelectItems(pidlFolder: PItemIDList; cidl: Cardinal;
  apidl: pointer; dwFlags: DWORD): HRESULT; stdcall; external shell32;

function OpenFolderAndSelectFile(const FileName: string): boolean;
var
  IIDL: PItemIDList;
begin
  result := false;
  IIDL := ILCreateFromPath(PChar(FileName));
  if IIDL <> nil then
    try
      result := SHOpenFolderAndSelectItems(IIDL, 0, nil, 0) = S_OK;
    finally
      ILFree(IIDL);
    end;
end;
Urology answered 8/3, 2013 at 18:41 Comment(12)
string passed to the parameter must be null terminated is it?Compression
Yes, the compiler makes sure that there are null terminators.Urology
Please note: #4292293 This doesn't always work, and I in fact just witnessed it not working myself.Leora
I wish this wasn't closed as a duplicate because as I say in my comment in the question above, the other accepted answer is in C++ and uses a different mechanism than this. I'd like to see if someone can answer this question using that code converted to Delphi, because I cannot figure out how to re-write it.Leora
@JerryDodge: I implemented that for you.Urology
Thanks Andreas, the "fancy approach" is much, much, much more reliable.Leora
@Jerry: Yeah, I suppose this API function was added for a reason.Urology
For those that are using Delphi XE and up: you don't have to declare ILCreateFromPath & co.Bil
This is VERY SLOW. Can anyone confirm this?Lapsus
@user1580348: Shouldn't be slow. Should take less than a second. (So compared to multiplying two floating-point numbers, of course, is it extremely slow, but it involves the shell and shell extensions might be involved. That's quite heavy. But it shouldn't be slower than opening a new Explorer window with Win+E or opening an Open dialog box in Notepad.) If it takes several seconds, maybe you have some troublesome shell extension?Urology
I wrote a program where a user could open/select dozens of different files. I do not want explorer.exe to be run dozens of times. This code does not instantiate a new instance of explorer.exe. Bravo!Extensometer
Would be nice if this could be extended to select multiple filesChristenechristening
P
2

The answers at delphi.lonzu.net and swissdelphicenter offer a simpler solution to this. I've only tested it on Windows 10, 1909,, but the gist of it is:

uses ShellApi, ...
...
var
   FileName : TFileName;
begin
  FileName := 'c:\temp\somefile.html';

  ShellExecute(Handle, 'OPEN', 
    pchar('explorer.exe'), 
    pchar('/select, "' + FileName + '"'), 
    nil, 
    SW_NORMAL);
end;

This is simple and easy to use. Whether it works with older versions of Windows, I don't know.

Pongee answered 5/4, 2020 at 22:46 Comment(2)
It works even back in Windows XP, so that's probably fine for most.Krick
I created an empty unit, and need more uses like Winapi.Windows, Winapi.Messages, Forms,Flaming

© 2022 - 2024 — McMap. All rights reserved.