Closed eltos closed 2 years ago
An alternative approach would be to manually iterate the open instances and find a matching one.
This works very well, except that it takes a moment until the newly created file appears in the Folder.Items()
list.
Therefore, if calling ExplorerUtil.RequestFilenameEdit(file)
too early, it can not select the newly created file and the method falls back to SHOpenFolderAndSelectItems
.
One could overcome this by introducing an artificial delay. Since SHOpenFolderAndSelectItems
also takes a moment to select the file, this is no general disadvantage. However, the question remains how long the delay has to be, and if there is a way to use a callback or something in order not to introduce a hardcoded Thread.Sleep(1000);
.
In ExplorerUtils.cs
:
/// <summary>
/// Searches the file with given path in the given shell window and selects it if found
/// </summary>
/// <param name="window">The shell window</param>
/// <param name="path">The path of the file to select</param>
/// <param name="edit">Select in edit mode if true, otherwise just select</param>
/// <returns>True if the file was found and selected, false otherwise</returns>
private static bool SelectPathInWindow(SHDocVw.InternetExplorer window, string path, bool edit=true) {
IShellFolderViewDual view = window?.Document as IShellFolderViewDual;
if (view != null) {
// TODO: some time needed for the file to appears in view.Folder.Items()
foreach (FolderItem folderItem in view.Folder.Items()) {
if (folderItem.Path == path){
SetForegroundWindow((IntPtr)window.HWND);
// https://docs.microsoft.com/en-us/windows/win32/shell/shellfolderview-selectitem
view.SelectItem(folderItem, 16 /* focus it, */ + 8 /* ensure it's visible, */
+ 4 /* deselect all other and */
+ (edit ? 3 : 1) /* select or edit */);
return true;
}
}
}
return false;
}
/// <summary>
/// Request file name edit by user in active explorer path
/// </summary>
/// <param name="filePath">Path of file to select/edit</param>
/// <param name="edit">can be set to false to select only (without entering edit mode)</param>
public static void RequestFilenameEdit(string filePath, bool edit = true) {
filePath = Path.GetFullPath(filePath);
var dirPath = Path.GetDirectoryName(filePath);
// check current shell window first
var focussedWindow = GetActiveExplorer();
if (GetExplorerPath(focussedWindow) == dirPath) {
if (SelectPathInWindow(focussedWindow, filePath, edit))
return;
}
// then check other open shell windows
var shellWindows = new SHDocVw.ShellWindows();
foreach (SHDocVw.InternetExplorer window in shellWindows) {
if (GetExplorerPath(window) == dirPath) {
if (SelectPathInWindow(window, filePath, edit))
return;
}
}
// or open a new shell window
IntPtr file;
SHParseDisplayName(filePath, IntPtr.Zero, out file, 0, out _);
try {
SHOpenFolderAndSelectItems(file, 0, null, edit ? 1 : 0);
} finally {
ILFree(file);
}
}
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
[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);
However, the question remains how long the delay has to be, and if there is a way to use a callback or something in order not to introduce a hardcoded
Thread.Sleep(1000);
.
This was solved with window.Refresh()
and window.DocumentComplete
. See ddfb8a4d907f2bcc63283a197321ccec3c58ebff.
To Reproduce
::{374DE290-123F-4565-9164-39C4925E467B}
and notC:\Users\User\Downloads
)C:\Users\User\Downloads
) will open with the new file selected for rename.Expected behaviour
The file is selected for rename in the existing file explorer. Furthermore, when multiple windows with the same location are open, the one where the right-click action was performed is used.
Cause
This is caused by the use of
SHOpenFolderAndSelectItems
which does not re-use an existing file explorer instance where the path is a GUID (like::{374DE290-123F-4565-9164-39C4925E467B}
instead ofC:\Users\User\Downloads
) and also does not know which instance to re-use if there are multiple.Related: