< Summary

Information
Class: SwitchBlade.Core.WindowFinder
Assembly: SwitchBlade
File(s): D:\a\switchblade\switchblade\Core\WindowFinder.cs
Tag: 203_23722840422
Line coverage
100%
Covered lines: 90
Uncovered lines: 0
Coverable lines: 90
Total lines: 166
Line coverage: 100%
Branch coverage
100%
Covered branches: 30
Total branches: 30
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%44100%
get_PluginName()100%11100%
get_HasSettings()100%11100%
get_IsUiaProvider()100%11100%
SetExclusions(...)100%11100%
Initialize(...)100%11100%
ReloadSettings()100%11100%
ScanWindowsCore()100%22100%
EnumCallback()100%2020100%
ActivateWindow(...)100%11100%
GetPid(...)100%22100%
GetProcessInfo(...)100%22100%
IsWindowValid(...)100%11100%

File(s)

D:\a\switchblade\switchblade\Core\WindowFinder.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Diagnostics;
 5using SwitchBlade.Contracts;
 6using SwitchBlade.Services;
 7
 8namespace SwitchBlade.Core
 9{
 10    public class WindowFinder : CachingWindowProviderBase
 11    {
 12        private readonly ISettingsService _settingsService;
 13        private readonly IWindowInterop _interop;
 3814        private IEnumerable<string> _dynamicExclusions = new List<string>();
 15
 2616        public override string PluginName => "WindowFinder";
 417        public override bool HasSettings => false;
 118        public override bool IsUiaProvider => false; // Uses EnumWindows, not UIA
 19
 3820        public WindowFinder(ISettingsService settingsService, IWindowInterop interop)
 3821        {
 3822            _settingsService = settingsService ?? throw new ArgumentNullException(nameof(settingsService));
 3723            _interop = interop ?? throw new ArgumentNullException(nameof(interop));
 3624        }
 25
 26        public override void SetExclusions(IEnumerable<string> exclusions)
 227        {
 228            _dynamicExclusions = exclusions;
 229        }
 30
 31        public override void Initialize(IPluginContext context)
 2432        {
 2433            base.Initialize(context);
 34            // Note: SettingsService is now injected via constructor, not through Initialize
 2435        }
 36
 37        public override void ReloadSettings()
 138        {
 39            // No plugin-specific settings to reload
 140        }
 41
 42
 43        protected override IEnumerable<WindowItem> ScanWindowsCore()
 1244        {
 1245            var results = new List<WindowItem>();
 1346            if (_settingsService == null) return results; // Add safety
 47
 1148            var excluded = new HashSet<string>(_settingsService.Settings.ExcludedProcesses, StringComparer.OrdinalIgnore
 49
 50            // Note: Browser processes are now managed by the ChromeTabFinder plugin.
 51            // To prevent duplicate windows, add browser process names to ExcludedProcesses in Settings.
 52
 53            unsafe bool EnumCallback(IntPtr hwnd, IntPtr lParam)
 1154            {
 1155                if (!_interop.IsWindowVisible(hwnd))
 156                    return true;
 57
 58                // Bleeding edge optimization: Use stackalloc for zero-allocation title retrieval
 59                // Max window title length is technically 256, but can be larger. 512 is safe.
 60                const int simplifyTitleBuffer = 512;
 1061                char* buffer = stackalloc char[simplifyTitleBuffer];
 62
 1063                int length = _interop.GetWindowTextUnsafe(hwnd, (IntPtr)buffer, simplifyTitleBuffer);
 1064                if (length == 0)
 165                    return true;
 66
 67                // Perform "Program Manager" check without allocating string
 68                // Check if starts with "Program Manager" (length 15)
 969                if (length == 15)
 570                {
 71                    // Fast manual check
 72                    // "Program Manager"
 573                    bool match = true;
 574                    string pm = "Program Manager";
 7075                    for (int i = 0; i < 15; i++)
 3476                    {
 4677                        if (buffer[i] != pm[i]) { match = false; break; }
 3078                    }
 679                    if (match) return true;
 480                }
 81
 82                // Get Process Name and Path (Optimized Interop handles caching and minimal allocations internally)
 883                string processName = "Window";
 884                string? executablePath = null;
 85                try
 886                {
 87                    uint pid;
 888                    _interop.GetWindowThreadProcessId(hwnd, out pid);
 789                    if (pid != 0)
 590                    {
 591                        (processName, executablePath) = _interop.GetProcessInfo(pid);
 592                    }
 793                }
 194                catch
 195                {
 96                    // Ignore access denied errors etc.
 197                }
 98
 99                // fast-path rejection before allocating title string
 8100                if (excluded.Contains(processName) || _dynamicExclusions.Contains(processName, StringComparer.OrdinalIgn
 2101                {
 2102                    return true;
 103                }
 104
 105                // Only allocate string if we are keeping the window
 6106                string title = new string(buffer, 0, length);
 107
 108                // Debug log
 6109                base.Logger?.Log($"Included Window: '{title}', Process: '{processName}' (Exclusions: {string.Join(",", _
 110
 6111                results.Add(new WindowItem
 6112                {
 6113                    Hwnd = hwnd,
 6114                    Title = title,
 6115                    ProcessName = processName,
 6116                    ExecutablePath = executablePath,
 6117                    Source = this
 6118                });
 119
 6120                return true;
 11121            }
 122
 11123            _interop.EnumWindows(EnumCallback, IntPtr.Zero);
 124
 11125            return results;
 12126        }
 127
 128        public override void ActivateWindow(WindowItem windowItem)
 1129        {
 130            // Robust window activation using the improved helper
 1131            _interop.ForceForegroundWindow(windowItem.Hwnd);
 1132        }
 133
 134        protected override int GetPid(IntPtr hwnd)
 8135        {
 136            try
 8137            {
 8138                _interop.GetWindowThreadProcessId(hwnd, out uint pid);
 5139                return (int)pid != 0 ? (int)pid : -1;
 140            }
 3141            catch
 3142            {
 3143                return -1;
 144            }
 8145        }
 146
 147        protected override (string ProcessName, string? ExecutablePath) GetProcessInfo(uint pid)
 4148        {
 6149             if ((int)pid == -1) return ("Window", null);
 150
 151             try
 2152             {
 2153                return _interop.GetProcessInfo(pid);
 154             }
 2155             catch
 2156             {
 2157                return ("Window", null);
 158             }
 4159        }
 160
 161        protected override bool IsWindowValid(IntPtr hwnd)
 1162        {
 1163            return _interop.IsWindowVisible(hwnd);
 1164        }
 165    }
 166}