Coming to the party late.
I found RandomEngy's solution useful, but modified it slightly to allow the selection of many files at once. Hope someone finds it useful.
[DllImport("shell32.dll", SetLastError = true)]
public static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags);
[DllImport("shell32.dll", SetLastError = true)]
public static extern void SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, [Out] out IntPtr pidl, uint sfgaoIn, [Out] out uint psfgaoOut);
public static void OpenFolderAndSelectItem(string folderPath, List<string> files)
{
IntPtr nativeFolder;
uint psfgaoOut;
SHParseDisplayName(folderPath, IntPtr.Zero, out nativeFolder, 0, out psfgaoOut);
if (nativeFolder == IntPtr.Zero)
{
// Log error, can't find folder
return;
}
List<IntPtr> nativeFiles = new();
foreach (string file in files)
{
IntPtr nativeFile;
SHParseDisplayName(Path.Combine(folderPath, file), IntPtr.Zero, out nativeFile, 0, out psfgaoOut);
if (nativeFile != IntPtr.Zero)
{
// Open the folder without the file selected if we can't find the file
nativeFiles.Add(nativeFile);
}
}
if (nativeFiles.Count == 0)
{
nativeFiles.Add(IntPtr.Zero);
}
IntPtr[] fileArray = nativeFiles.ToArray();
SHOpenFolderAndSelectItems(nativeFolder, (uint)fileArray.Length, fileArray, 0);
Marshal.FreeCoTaskMem(nativeFolder);
nativeFiles.ForEach((x) =>
{
if (x != IntPtr.Zero) Marshal.FreeCoTaskMem(x);
});
}