Reuse open Explorer window when selecting files
This commit is contained in:
@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -33,6 +34,19 @@ namespace DrillTools
|
||||
private bool _isStartupDrillTapeFile;
|
||||
private bool _canGeneratePpDrillTape;
|
||||
|
||||
private const int SwRestore = 9;
|
||||
private const int SvsiSelect = 0x1;
|
||||
private const int SvsiDeselectOthers = 0x4;
|
||||
private const int SvsiEnsureVisible = 0x8;
|
||||
private const int SvsiFocused = 0x10;
|
||||
private const int SelectFileFlags = SvsiSelect | SvsiDeselectOthers | SvsiEnsureVisible | SvsiFocused;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
/// <summary>
|
||||
/// 刀具列表
|
||||
/// </summary>
|
||||
@@ -1223,15 +1237,11 @@ M30";
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 explorer.exe 的 /select 参数来打开文件资源管理器并选中文件
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "explorer.exe",
|
||||
Arguments = $"/select,\"{filePath}\"",
|
||||
UseShellExecute = true
|
||||
};
|
||||
string fullFilePath = Path.GetFullPath(filePath);
|
||||
if (TrySelectFileInOpenExplorerWindow(fullFilePath))
|
||||
return;
|
||||
|
||||
Process.Start(startInfo);
|
||||
OpenNewExplorerWindowAndSelectFile(fullFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1239,6 +1249,138 @@ M30";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TrySelectFileInOpenExplorerWindow(string filePath)
|
||||
{
|
||||
string? directory = Path.GetDirectoryName(filePath);
|
||||
if (string.IsNullOrWhiteSpace(directory))
|
||||
return false;
|
||||
|
||||
string normalizedDirectory = NormalizeDirectoryPath(directory);
|
||||
Type? shellType = Type.GetTypeFromProgID("Shell.Application");
|
||||
if (shellType == null)
|
||||
return false;
|
||||
|
||||
object? shell = null;
|
||||
try
|
||||
{
|
||||
shell = Activator.CreateInstance(shellType);
|
||||
if (shell == null)
|
||||
return false;
|
||||
|
||||
dynamic shellApplication = shell;
|
||||
foreach (dynamic window in shellApplication.Windows())
|
||||
{
|
||||
if (!IsExplorerWindow(window))
|
||||
continue;
|
||||
|
||||
string? windowDirectory = GetExplorerWindowDirectory(window);
|
||||
if (string.IsNullOrWhiteSpace(windowDirectory)
|
||||
|| !string.Equals(NormalizeDirectoryPath(windowDirectory), normalizedDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SelectFileInExplorerWindow(window, filePath))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (shell != null && Marshal.IsComObject(shell))
|
||||
Marshal.FinalReleaseComObject(shell);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsExplorerWindow(dynamic window)
|
||||
{
|
||||
try
|
||||
{
|
||||
string fullName = Convert.ToString(window.FullName) ?? string.Empty;
|
||||
return string.Equals(Path.GetFileName(fullName), "explorer.exe", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string? GetExplorerWindowDirectory(dynamic window)
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = Convert.ToString(window.Document.Folder.Self.Path) ?? string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
return path;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 回退到 LocationURL,兼容部分 Explorer 窗口取不到 Folder.Self.Path 的情况。
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string locationUrl = Convert.ToString(window.LocationURL) ?? string.Empty;
|
||||
if (Uri.TryCreate(locationUrl, UriKind.Absolute, out Uri? uri) && uri.IsFile)
|
||||
return uri.LocalPath;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool SelectFileInExplorerWindow(dynamic window, string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
dynamic document = window.Document;
|
||||
dynamic fileItem = document.Folder.ParseName(Path.GetFileName(filePath));
|
||||
if (fileItem == null)
|
||||
{
|
||||
document.Refresh();
|
||||
fileItem = document.Folder.ParseName(Path.GetFileName(filePath));
|
||||
}
|
||||
|
||||
if (fileItem == null)
|
||||
return false;
|
||||
|
||||
document.SelectItem(fileItem, SelectFileFlags);
|
||||
IntPtr handle = new IntPtr(Convert.ToInt64(window.HWND));
|
||||
ShowWindow(handle, SwRestore);
|
||||
SetForegroundWindow(handle);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeDirectoryPath(string path)
|
||||
{
|
||||
return Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
}
|
||||
|
||||
private static void OpenNewExplorerWindowAndSelectFile(string filePath)
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "explorer.exe",
|
||||
Arguments = $"/select,\"{filePath}\"",
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成刀具直径列表文件
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user