diff --git a/ExplorerTabUtility/App.config b/ExplorerTabUtility/App.config index c5108bd..b42d465 100644 --- a/ExplorerTabUtility/App.config +++ b/ExplorerTabUtility/App.config @@ -11,7 +11,7 @@ True - False + True diff --git a/ExplorerTabUtility/ExplorerTabUtility.csproj b/ExplorerTabUtility/ExplorerTabUtility.csproj index c595aeb..7466ea0 100644 --- a/ExplorerTabUtility/ExplorerTabUtility.csproj +++ b/ExplorerTabUtility/ExplorerTabUtility.csproj @@ -7,6 +7,8 @@ true True Icon.ico + 1.1.0 + latest Explorer Tab Utility w4po $(AssemblyName) diff --git a/ExplorerTabUtility/Forms/TrayIcon.cs b/ExplorerTabUtility/Forms/TrayIcon.cs index 17912b2..17eafbb 100644 --- a/ExplorerTabUtility/Forms/TrayIcon.cs +++ b/ExplorerTabUtility/Forms/TrayIcon.cs @@ -145,9 +145,8 @@ private static async Task OnNewWindow(Window window) UiAutomation.AddNewTab(windowElement); - // If it is just a new (This PC) window, return. + // If it is just a new (This PC | Home), return. if (string.IsNullOrWhiteSpace(window.Path)) return; - if (!Uri.TryCreate(window.Path, UriKind.Absolute, out var uri)) return; var newTabHandle = WinApi.ListenForNewExplorerTab(oldTabs); if (newTabHandle == default) return; @@ -155,7 +154,7 @@ private static async Task OnNewWindow(Window window) var newTabElement = UiAutomation.FromHandle(newTabHandle); if (newTabElement == default) return; - UiAutomation.GoToLocation(uri.LocalPath, windowElement); + UiAutomation.GoToLocation(window.Path, windowElement); if (window.SelectedItems is not { } selectedItems) return; diff --git a/ExplorerTabUtility/Helpers/Helper.cs b/ExplorerTabUtility/Helpers/Helper.cs index f793895..f48af9f 100644 --- a/ExplorerTabUtility/Helpers/Helper.cs +++ b/ExplorerTabUtility/Helpers/Helper.cs @@ -2,13 +2,25 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; -using System.IO; using System.Threading; namespace ExplorerTabUtility.Helpers; public static class Helper { + public static T DoUntilCondition(Func action, Predicate predicate, int timeMs = 500, CancellationToken cancellationToken = default) + { + var startTicks = Stopwatch.GetTimestamp(); + + while (!cancellationToken.IsCancellationRequested && !IsTimeUp(startTicks, timeMs)) + { + var result = action.Invoke(); + if (predicate(result)) + return result; + } + + return action.Invoke(); + } public static T DoUntilNotDefault(Func action, int timeMs = 500, CancellationToken cancellationToken = default) { var startTicks = Stopwatch.GetTimestamp(); @@ -59,21 +71,4 @@ public static TimeSpan GetElapsedTime(long startTicks) return Icon.ExtractAssociatedIcon(location); } - - public static string GetFullPath(string path) - { - // Check if the path contains environment variables - if (path.StartsWith("%") && path.EndsWith("%")) - { - // Replace environment variables with their values - path = Environment.ExpandEnvironmentVariables(path); - } - - // If it has : or \, assume it's a regular path - if (path.Contains(":") || path.Contains("\\")) return path; - - // Check if the path is a special folder - var fullPath = $"{Environment.GetEnvironmentVariable("USERPROFILE")}\\{path}"; - return Directory.Exists(fullPath) ? fullPath : path; - } } \ No newline at end of file diff --git a/ExplorerTabUtility/Hooks/UiAutomation.cs b/ExplorerTabUtility/Hooks/UiAutomation.cs index 1943e2f..94514d5 100644 --- a/ExplorerTabUtility/Hooks/UiAutomation.cs +++ b/ExplorerTabUtility/Hooks/UiAutomation.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; using FlaUI.Core.AutomationElements; @@ -49,18 +48,15 @@ private void OnWindowOpened(AutomationElement element, EventId _) if (_cache.Contains(hWnd)) return; _cache.Add(hWnd); - WinApi.GetWindowRect(hWnd, out var originalRect); - // Move the window outside the screen (Hide) - WinApi.SetWindowPos(hWnd, IntPtr.Zero, -1000, -1000, 0, 0, WinApi.SWP_NOSIZE | WinApi.SWP_NOZORDER); + var originalRect = HideWindow(hWnd); var showAgain = true; try { - // a new "This PC" window (unless the opened folder is called "This PC"?) + // A new "This PC" window (unless the opened folder is called "This PC"?) if (string.Equals(element.Name, "This PC")) { - // the window suppose to have only one tab, so we should be okay. - CloseRandomTabOfAWindow(element); - _onNewWindow.Invoke(new Window(string.Empty)); + CloseAndNotifyNewWindow(element, new Window(string.Empty, oldWindowHandle: hWnd)); + showAgain = false; return; } @@ -69,38 +65,47 @@ private void OnWindowOpened(AutomationElement element, EventId _) // We have to invoke the suggestBox to populate the address bar :( suggestBox.Patterns.Invoke.Pattern.Invoke(); - Thread.Sleep(50); // Invoke searchBox to hide the suggestPopup window. searchBox.Patterns.Invoke.Pattern.Invoke(); - Thread.Sleep(50); - var location = addressBar.Patterns.Value.Pattern.Value.Value; + var location = Helper.DoUntilCondition( + action: () => addressBar.Patterns.Value.Pattern.Value.Value, + predicate: l => !string.IsNullOrWhiteSpace(l)); if (string.IsNullOrWhiteSpace(location)) + return; + + // ("Home") For English version. + if (string.Equals(location, "Home")) { - // Hmm... Let's try one more time. - Thread.Sleep(300); - location = addressBar.Patterns.Value.Pattern.Value.Value; - if (string.IsNullOrWhiteSpace(location)) return; + CloseAndNotifyNewWindow(element, new Window(string.Empty, oldWindowHandle: hWnd)); + showAgain = false; + return; } - location = Helper.GetFullPath(location); var tab = element.FindFirstChild(c => c.ByClassName("ShellTabWindowClass")); var folderView = tab?.FindFirstDescendant(c => c.ByClassName("UIItemsView")); - if (folderView == null) return; + if (folderView == default) + { + // ("Home") For non English versions. + var home = tab?.FindFirstDescendant(c => c.ByClassName("HomeListView")); + if (home == default) return; - var selectedNames = folderView.Patterns.Selection.Pattern.Selection.Value? - .Select(s => s.Name).ToList(); + CloseAndNotifyNewWindow(element, new Window(string.Empty, oldWindowHandle: hWnd)); + showAgain = false; + return; + } + + var selectedNames = folderView.Patterns.Selection.Pattern.Selection.Value + ?.Select(s => s.Name) + .ToList(); var tabHWnd = tab!.Properties.NativeWindowHandle.Value; - // the window suppose to have only one tab, so we should be okay. - CloseRandomTabOfAWindow(element); + CloseAndNotifyNewWindow(element, new Window(location, selectedNames, hWnd, tabHWnd)); showAgain = false; - - _onNewWindow.Invoke(new Window(location, selectedNames, hWnd, tabHWnd)); } finally { @@ -108,7 +113,6 @@ private void OnWindowOpened(AutomationElement element, EventId _) if (showAgain) WinApi.SetWindowPos(hWnd, IntPtr.Zero, originalRect.Left, originalRect.Top, 0, 0, WinApi.SWP_NOSIZE | WinApi.SWP_NOZORDER); } - } public static AutomationElement? FromHandle(IntPtr hWnd) => Automation.FromHandle(hWnd); public static void AddNewTab(AutomationElement windowElement) @@ -143,35 +147,18 @@ public static void GoToLocation(string location, AutomationElement windowElement GetHeaderElements(windowElement, out var suggestBox, out var searchBox, out var addressBar); if (suggestBox == default || searchBox == default || addressBar == default) return; - // Set location + suggestBox.Patterns.Invoke.Pattern.Invoke(); addressBar.Patterns.Value.Pattern.SetValue(location); // We have to invoke the suggestBox to Navigate :( suggestBox.Patterns.Invoke.Pattern.Invoke(); - Thread.Sleep(50); // Invoke searchBox to hide the suggestPopup window. searchBox.Patterns.Invoke.Pattern.Invoke(); - var suggestList = Helper.DoUntilNotDefault(() => suggestBox.FindFirstChild("SuggestionsList")); - if (suggestList != default) - searchBox.Patterns.Invoke.Pattern.Invoke(); - - //var startTime = Stopwatch.GetTimestamp(); - //while (Stopwatch.GetElapsedTime(startTime).TotalMilliseconds < 700) - //{ - // var suggestList = suggestBox.FindFirstChild("SuggestionsList")?.FindAllChildren(); - // if (suggestList == default) continue; - - // foreach (var suggestItem in suggestList) - // { - // if (string.Equals(suggestItem.Name, location, StringComparison.OrdinalIgnoreCase)) - // { - // suggestItem.Patterns.Invoke.Pattern.Invoke(); - // return; - // } - // } - //} + //var suggestList = Helper.DoUntilNotDefault(() => suggestBox.FindFirstDescendant("SuggestionsList")); + //if (suggestList != default) + // searchBox.Patterns.Invoke.Pattern.Invoke(); } public static void SelectItems(AutomationElement tabElement, ICollection names) { @@ -224,7 +211,20 @@ private static void GetHeaderElements(AutomationElement window, out AutomationEl suggestBox = headerBar.FindFirstChild("PART_AutoSuggestBox"); addressBar = suggestBox?.FindFirstChild(c => c.ByName("Address Bar")); } + private static RECT HideWindow(IntPtr hWnd) + { + WinApi.GetWindowRect(hWnd, out var originalRect); + // Move the window outside the screen (Hide) + WinApi.SetWindowPos(hWnd, IntPtr.Zero, -1000, -1000, 0, 0, WinApi.SWP_NOSIZE | WinApi.SWP_NOZORDER); + return originalRect; + } + private void CloseAndNotifyNewWindow(AutomationElement element, Window window) + { + // the window suppose to have only one tab, so we should be okay. + CloseRandomTabOfAWindow(element); + _onNewWindow.Invoke(window); + } public void Dispose() { diff --git a/ExplorerTabUtility/Properties/Settings.Designer.cs b/ExplorerTabUtility/Properties/Settings.Designer.cs index aa07df4..80852ad 100644 --- a/ExplorerTabUtility/Properties/Settings.Designer.cs +++ b/ExplorerTabUtility/Properties/Settings.Designer.cs @@ -37,7 +37,7 @@ public bool KeyboardHook { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] + [global::System.Configuration.DefaultSettingValueAttribute("True")] public bool WindowHook { get { return ((bool)(this["WindowHook"])); diff --git a/ExplorerTabUtility/Properties/Settings.settings b/ExplorerTabUtility/Properties/Settings.settings index f3b9128..e73ce8e 100644 --- a/ExplorerTabUtility/Properties/Settings.settings +++ b/ExplorerTabUtility/Properties/Settings.settings @@ -6,7 +6,7 @@ True - False + True \ No newline at end of file