Functions/GenXdev.Helpers/WindowHelper.cs

// ################################################################################
// Part of PowerShell module : GenXdev.Helpers
// Original cmdlet filename : WindowHelper.cs
// Original author : René Vaessen / GenXdev
// Version : 3.29.2026
// ################################################################################
// Copyright (c) 2026 René Vaessen / GenXdev
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ################################################################################



using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using Point = System.Windows.Point;
using Size = System.Windows.Size;

namespace GenXdev.Helpers
{
    public enum DeviceCap
    {
        VERTRES = 10,
        DESKTOPVERTRES = 117,

        // http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html
    }

    /// <summary>
    /// Static class providing desktop and monitor information utilities.
    /// Includes scaling factor calculations and window positioning constants.
    /// </summary>
    public static class DesktopInfo
    {
        // Place these at the top of WindowObj, near your other constants and P/Invokes
        public const int SWP_NOMOVE = 0x0002;
        public const int SWP_NOSIZE = 0x0001;
        public const int SWP_NOZORDER = 0x0004;
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        [DllImport("gdi32.dll")]
        public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

        [PreserveSig()]
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateDC(
            string lpszDriver,
            string lpszDevice,
            IntPtr lpszOutput,
            IntPtr lpInitData
        );

        /// <summary>
        /// Calculates the display scaling factor for a specific monitor.
        /// The scaling factor represents how much the display is scaled (e.g., 1.25 for 125% scaling).
        /// </summary>
        /// <param name="monitor">The monitor index to get scaling for</param>
        /// <returns>The scaling factor as a float (1.0 = 100% scaling)</returns>
        public static float getScalingFactor(int monitor)
        {
            //Graphics g = Graphics.FromHwnd(IntPtr.Zero);
            var AllScreens = (from q in WpfScreenHelper.Screen.AllScreens select q).ToArray();
            IntPtr desktop = CreateDC(
                AllScreens[monitor].DeviceName,
                AllScreens[monitor].DeviceName,
                IntPtr.Zero,
                IntPtr.Zero
            );
            int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
            int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);

            float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;

            return ScreenScalingFactor; // 1.25 = 125%
        }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SetWindowPos(
            IntPtr hWnd,
            IntPtr hWndInsertAfter,
            int X,
            int Y,
            int cx,
            int cy,
            uint uFlags
        );
    }

    public class WindowObj
    {
        /// <summary>
        /// Represents a Windows window with methods for manipulation and state management.
        /// Provides functionality to control window position, size, visibility, and various window states.
        /// </summary>
        private const int SWP_NOMOVE = 0x0002;
        private const int SWP_NOSIZE = 0x0001;
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SetWindowPos(
            IntPtr hWnd,
            IntPtr hWndInsertAfter,
            int X,
            int Y,
            int cx,
            int cy,
            uint uFlags
        );

        /// <summary>
        /// Windows handle to identify the current window. This is a unique identifier
        /// assigned by the Windows operating system to each window instance.
        /// </summary>
        public IntPtr Handle { get; private set; }
        /// <summary>
        /// Name/title of the window as displayed in the title bar.
        /// </summary>
        public string Title { get; private set; }
        /// <summary>
        /// Collection of child windows belonging to this window.
        /// Child windows are windows that are contained within this window's client area.
        /// </summary>
        public ICollection<WindowObj> Children { get; private set; }

        /// <summary>
        /// Get a custom representation of the window class base on https://docs.microsoft.com/en-us/windows/win32/winmsg/about-window-classes documentation
        /// </summary>
        public string WindowType { get; private set; }

        /// <summary>
        /// Get the name of the class that represents the windows
        /// </summary>
        public string WindowClassName { get; private set; }

        private const UInt32 WM_CLOSE = 0x0010;
        public static int Amount = 0;

        // Constants for window styling
        private const int GWL_STYLE = -16;
        private const int GWL_EXSTYLE = -20;
        private const uint WS_CAPTION = 0x00C00000;
        private const uint WS_THICKFRAME = 0x00040000;
        private const uint WS_MINIMIZEBOX = 0x00020000;
        private const uint WS_MAXIMIZEBOX = 0x00010000;
        private const uint WS_SYSMENU = 0x00080000;
        private const uint WS_EX_TOPMOST = 0x00000008;
        private const uint WS_EX_LAYERED = 0x00080000;
        private const uint LWA_ALPHA = 0x00000002;

        // For window transparency
        [DllImport("user32.dll")]
        private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);

        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);

        [DllImport("user32.dll")]
        private static extern uint GetWindowLong(IntPtr hWnd, int nIndex);

        /// <summary>
        /// Creates a window object with a handle and a window title.
        /// Initializes the window properties and enumerates child windows.
        /// </summary>
        /// <param name="handle">The Windows handle (HWND) of the window</param>
        /// <param name="title">The title/caption of the window</param>
        public WindowObj(IntPtr handle, string title)
        {
            Handle = handle;
            Title = title;

            // Initialize string builder for window class name retrieval
            StringBuilder stringBuilder = new StringBuilder(256);
            GetClassName(handle, stringBuilder, stringBuilder.Capacity);
            WindowType = GetWindowClass(stringBuilder.ToString());
            WindowClassName = stringBuilder.ToString();

            // Initialize children collection and enumerate child windows
            Children = new List<WindowObj>();
            ArrayList handles = new ArrayList();
            EnumedWindow childProc = GetWindowHandle;

            EnumChildWindows(handle, childProc, handles);
            foreach (IntPtr item in handles)
            {
                // Get capacity for child window text
                int capacityChild = GetWindowTextLength(handle) * 2;

                StringBuilder stringBuilderChild = new StringBuilder(capacityChild);
                GetWindowText(handle, stringBuilder, stringBuilderChild.Capacity);

                // Get class name for child window
                StringBuilder stringBuilderChild2 = new StringBuilder(256);
                GetClassName(handle, stringBuilderChild2, stringBuilderChild2.Capacity);

                // Create child window object
                WindowObj win = new WindowObj(item, stringBuilder.ToString());
                win.WindowClassName = stringBuilderChild2.ToString();
                win.WindowType = GetWindowClass(stringBuilderChild2.ToString());
                Children.Add(win);
            }
        }

        [DllImport("user32.dll")]
        public static extern void mouse_event(Int32 dwFlags, Int32 dx, Int32 dy, Int32 dwData, UIntPtr dwExtraInfo);

        private const int MOUSEEVENTF_MOVE = 0x0001;
        private const int WM_SYSCOMMAND = 0x0112;
        private const int SC_MONITORPOWER = 0xF170;
        private const int MonitorTurnOn = -1;
        private const int MonitorShutoff = 2;


        /// <summary>
        /// Sends a message to wake the monitor/display from sleep mode.
        /// Uses Windows API to simulate monitor power on command.
        /// </summary>
        public static void WakeMonitor()
        {
            // Send message to turn monitors on
            PostMessage((IntPtr)0xffff, WM_SYSCOMMAND, SC_MONITORPOWER, MonitorTurnOn);
            System.Threading.Thread.Sleep(150);

            // Simulate mouse movement to ensure monitor stays awake
            mouse_event(MOUSEEVENTF_MOVE, 0, 1, 0, UIntPtr.Zero);
            System.Threading.Thread.Sleep(40);
            mouse_event(MOUSEEVENTF_MOVE, 0, -1, 0, UIntPtr.Zero);
        }

        /// <summary>
        /// Sends a message to put the monitor/display into sleep mode.
        /// Uses Windows API to simulate monitor power off command.
        /// </summary>
        public static void SleepMonitor()
        {
            // Send message to turn monitors off
            PostMessage((IntPtr)0xffff, WM_SYSCOMMAND, SC_MONITORPOWER, MonitorShutoff);
        }
        [DllImport("user32.dll")]
        public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetConsoleWindow();


        /// <summary>
        /// Callback method for enumerating child windows.
        /// Adds each window handle to the provided collection.
        /// </summary>
        /// <param name="windowHandle">Handle of the enumerated window</param>
        /// <param name="windowHandles">Collection to add the handle to</param>
        /// <returns>Always returns true to continue enumeration</returns>
        public static bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles)
        {
            windowHandles.Add(windowHandle);
            return true;
        }

        ///<summary>A class to have better manipulation of windows sizes</summary>
        public struct RectStruct
        {
            public int Left { get; set; }
            public int Top { get; set; }
            public int Right { get; set; }
            public int Bottom { get; set; }
            public int Width { get; set; }
            public int Height { get; set; }

        }

        /// <summary>
        /// Opens a new process with the given file path and returns a window object
        /// if the process has a user interface, otherwise returns null.
        /// </summary>
        /// <param name="filePath">Path of the file to execute</param>
        /// <param name="timeToWait">Time in seconds to wait for the process to start
        /// (default -1 means wait for input idle)</param>
        /// <returns>WindowObj if process has a main window, null otherwise</returns>
        public static WindowObj Open(string filePath, int timeToWait = -1)
        {
            // Validate file exists
            if (!File.Exists(filePath))
                throw new Exception(string.Format("The filePath {0} is not valid", filePath));

            // Start the process
            Process newProcess = Process.Start(filePath);

            // Wait for process to be ready for input
            if (timeToWait == -1)
                newProcess.WaitForInputIdle();
            else
                newProcess.WaitForInputIdle(timeToWait * 1000);

            // Return window object if process has a main window
            if (newProcess != null && newProcess.MainWindowHandle != IntPtr.Zero)
                return new WindowObj(newProcess.MainWindowHandle, newProcess.MainWindowTitle);

            return null;
        }

        /// <summary>
        /// Searches for the existence of a process with the given name and returns
        /// the first occurrence as a WindowObj. Performs multiple attempts if needed.
        /// </summary>
        /// <param name="name">Exact name of the process to find</param>
        /// <param name="attempts">Number of attempts to find the window (default 1)</param>
        /// <param name="waitInterval">Milliseconds to wait between attempts (default 1000)</param>
        /// <returns>WindowObj if found, null otherwise</returns>
        public static WindowObj GetWindow(string name, int attempts = 1, int waitInterval = 1000)
        {
            // Get current processes
            IEnumerable<Process> currentProcesses = Process.GetProcesses();
            int counter = 0;

            // Try multiple times if needed
            while (counter < attempts)
            {
                // Search for process with matching main window title
                foreach (Process p in currentProcesses)
                    if (p.MainWindowHandle != IntPtr.Zero && p.MainWindowTitle == name)
                        return new WindowObj(p.MainWindowHandle, p.MainWindowTitle);

                // Wait and refresh process list
                System.Threading.Thread.Sleep(waitInterval);
                currentProcesses = Process.GetProcesses();
                counter++;
            }
            return null;
        }

        /// <summary>
        /// Gets the currently focused window.
        /// Returns a WindowObj representing the window that currently has keyboard focus.
        /// </summary>
        /// <returns>WindowObj of the focused window, or null if none</returns>
        public static WindowObj GetFocusedWindow()
        {
            // Get handle of foreground window
            var sb = new StringBuilder();
            var handle = GetForegroundWindow();

            // Return window object with title
            return new WindowObj(handle, GetWindowTitle(handle));
        }

        /// <summary>
        /// Searches for processes with the given name and returns all occurrences
        /// as WindowObj instances. Case-sensitive search by process name.
        /// </summary>
        /// <param name="name">Process name to search for</param>
        /// <param name="attempts">Number of attempts to find windows (default 1)</param>
        /// <param name="waitInterval">Milliseconds to wait between attempts (default 1000)</param>
        /// <returns>Collection of WindowObj instances for matching processes</returns>
        public static IEnumerable<WindowObj> GetWindows(string name, int attempts = 1, int waitInterval = 1000)
        {
            // Get current processes
            IEnumerable<Process> currentProcesses = Process.GetProcesses();
            ICollection<WindowObj> windows = new List<WindowObj>();
            int counter = 0;

            // Try multiple times if needed
            while (counter < attempts)
            {
                // Find processes with matching name and main window
                foreach (Process p in currentProcesses)
                    if (p.MainWindowHandle != IntPtr.Zero && p.ProcessName == name)
                        windows.Add(new WindowObj(p.MainWindowHandle, p.MainWindowTitle));

                // Stop if we found windows
                if (windows.Count > 0)
                    break;

                // Wait and refresh process list
                System.Threading.Thread.Sleep(waitInterval);
                currentProcesses = Process.GetProcesses();
                counter++;
            }
            return windows;
        }

        /// <summary>
        /// Gets the main window of a specific process.
        /// Searches for the main window handle of the given process.
        /// </summary>
        /// <param name="p">The process to get the main window for</param>
        /// <param name="attempts">Number of attempts to find the window (default 1)</param>
        /// <param name="waitInterval">Milliseconds to wait between attempts (default 1000)</param>
        /// <returns>Collection containing the main window if found</returns>
        public static IEnumerable<WindowObj> GetMainWindow(Process p, int attempts = 1, int waitInterval = 1000)
        {
            // Get current processes
            IEnumerable<Process> currentProcesses = Process.GetProcesses();
            ICollection<WindowObj> windows = new List<WindowObj>();
            int counter = 0;

            // Try multiple times if needed
            while (counter < attempts)
            {
                // Check if process has a main window
                if (p.MainWindowHandle != IntPtr.Zero)
                {
                    try
                    {
                        // Create window object for main window
                        windows.Add(new WindowObj(p.MainWindowHandle, p.MainWindowTitle));
                    }
                    catch
                    {
                        // Break on error
                        break;
                    }
                }

                // Stop if we found windows
                if (windows.Count > 0)
                    break;

                // Wait and refresh process list
                System.Threading.Thread.Sleep(waitInterval);
                currentProcesses = Process.GetProcesses();
                counter++;
            }

            return windows;
        }

        /// <summary>
        /// Gets the main window for a given window handle.
        /// Creates a WindowObj for the specified handle.
        /// </summary>
        /// <param name="windowHandle">The window handle to create object for</param>
        /// <param name="attempts">Number of attempts (not used in this overload)</param>
        /// <param name="waitInterval">Wait interval (not used in this overload)</param>
        /// <returns>Collection containing the window object</returns>
        public static IEnumerable<WindowObj> GetMainWindow(IntPtr windowHandle, int attempts = 1, int waitInterval = 1000)
        {
            // Get current processes
            IEnumerable<Process> currentProcesses = Process.GetProcesses();
            ICollection<WindowObj> windows = new List<WindowObj>();

            // Create window object if handle is valid
            if (windowHandle != IntPtr.Zero)
            {
                try
                {
                    // Create window object with empty title (will be populated by constructor)
                    windows.Add(new WindowObj(windowHandle, ""));
                }
                catch { }
            }

            return windows;
        }
        /// <summary>
        /// Searches for a process whose window title contains the given name (partial match).
        /// Returns the first matching window. Case-insensitive search.
        /// </summary>
        /// <param name="name">Partial name to search for in window titles</param>
        /// <param name="attempts">Number of attempts to find the window (default 1)</param>
        /// <param name="waitInterval">Milliseconds to wait between attempts (default 1000)</param>
        /// <returns>WindowObj if found, null otherwise</returns>
        public static WindowObj GetWindowWithPartialName(string name, int attempts = 1, int waitInterval = 1000)
        {
            // Get current processes
            IEnumerable<Process> currentProcesses = Process.GetProcesses();
            int counter = 0;

            // Try multiple times if needed
            while (counter < attempts)
            {
                // Search for process with partial title match
                foreach (Process p in currentProcesses)
                    if (p.MainWindowHandle != IntPtr.Zero && p.MainWindowTitle.ToLower().Contains(name.ToLower()))
                        return new WindowObj(p.MainWindowHandle, p.MainWindowTitle);

                // Wait and refresh process list
                System.Threading.Thread.Sleep(waitInterval);
                currentProcesses = Process.GetProcesses();
                counter++;
            }
            return null;
        }

        /// <summary>
        /// Searches for processes whose window titles contain the given name (partial match).
        /// Returns all matching windows. Case-insensitive search.
        /// </summary>
        /// <param name="name">Partial name to search for in window titles</param>
        /// <param name="attempts">Number of attempts to find windows (default 1)</param>
        /// <param name="waitInterval">Milliseconds to wait between attempts (default 1000)</param>
        /// <returns>Collection of WindowObj instances for matching processes</returns>
        public static IEnumerable<WindowObj> GetWindowsWithPartialName(string name, int attempts = 1, int waitInterval = 1000)
        {
            // Get current processes
            IEnumerable<Process> currentProcesses = Process.GetProcesses();
            ICollection<WindowObj> windows = new List<WindowObj>();
            int counter = 0;

            // Try multiple times if needed
            while (counter < attempts)
            {
                // Find processes with partial title match
                foreach (Process p in currentProcesses)
                    if (p.MainWindowHandle != IntPtr.Zero && p.MainWindowTitle.ToLower().Contains(name.ToLower()))
                        windows.Add(new WindowObj(p.MainWindowHandle, p.MainWindowTitle));

                // Stop if we found windows
                if (windows.Count > 0)
                    break;

                // Wait and refresh process list
                System.Threading.Thread.Sleep(waitInterval);
                currentProcesses = Process.GetProcesses();
                counter++;
            }
            return windows;
        }

        /// <summary>
        /// Gets the currently active window (the one with keyboard focus).
        /// Searches through all processes to find the one matching the active window handle.
        /// </summary>
        /// <returns>WindowObj of the active window, or null if not found</returns>
        public static WindowObj GetActive()
        {
            // Get handle of active window
            IntPtr handle = GetActiveWindow();

            // Find matching process
            if (handle != IntPtr.Zero)
            {
                foreach (Process p in Process.GetProcesses())
                    if (p.MainWindowHandle == handle)
                        return new WindowObj(p.MainWindowHandle, p.MainWindowTitle);
            }
            return null;
        }

        /// <summary>
        /// Brings focus to the current window by setting it as foreground.
        /// Temporarily allows the process to set foreground window if needed.
        /// </summary>
        public void Focus()
        {
            // Allow this process to set foreground window
            AllowSetForegroundWindow(-1);

            // Set this window as foreground and focus
            SetForegroundWindow(this.Handle);
            SetFocus(Handle);
        }

        /// <summary>
        /// Sets the current window as the foreground window with enhanced reliability.
        /// Handles minimized/maximized states and ensures proper activation.
        /// </summary>
        public void SetForeground()
        {
            // Temporarily set as topmost if not already

            // Show the window (SW_SHOW = 5)
            if (IsMinimized())
            {
                ShowWindowAsync(Handle, (int)ShowWindowCommands.Restore);
                System.Threading.Thread.Sleep(50);
            }

            // Ensure window is visible but don't activate it
            if (!IsVisible())
            {
                ShowWindowAsync(Handle, (int)ShowWindowCommands.ShowNoActivate);
            }

            SetForegroundWindow(Handle);
            BringWindowToTop(Handle);

            // Ensure the window is the active window and has keyboard focus
            SetActiveWindow(Handle);
            SetFocus(Handle);
        }

        /// <summary>Maximize the current window</summary>
        public bool Maximize()
        {
            return ShowWindowAsync(this.Handle, (int)ShowWindowCommands.Maximize);
        }

        /// <summary>Minimize the current window</summary>
        public bool Minimize()
        {
            return ShowWindowAsync(this.Handle, (int)ShowWindowCommands.Minimize);
        }

        /// <summary>Return the current windows at its first state when the windows was created</summary>
        public bool Restore()
        {
            return ShowWindowAsync(this.Handle, (int)ShowWindowCommands.Restore);
        }

        /// <summary>Return the current windows at its default state</summary>
        public bool DefaultState()
        {
            return ShowWindowAsync(this.Handle, (int)ShowWindowCommands.ShowDefault);
        }

        /// <summary>
        /// Gets the title/caption text of a window given its handle.
        /// </summary>
        /// <param name="hWnd">The window handle to get the title for</param>
        /// <returns>The window title as a string</returns>
        public static string GetWindowTitle(IntPtr hWnd)
        {
            var length = GetWindowTextLength(hWnd) + 1;
            var title = new StringBuilder(length);
            GetWindowText(hWnd, title, length);
            return title.ToString();
        }

        /// <summary>Hide the current window
        /// *If the application close with a hide process, this will not be close unless close method
        /// calls or manually kill the process*</summary>
        public bool Hide()
        {
            return ShowWindowAsync(this.Handle, (int)ShowWindowCommands.Hide);
        }

        /// <summary>Shows the current windows if it was hide</summary>
        public bool Show()
        {
            return ShowWindowAsync(this.Handle, (int)ShowWindowCommands.Show);
        }

        /// <summary>Close the current windows</summary>
        public bool Close()
        {
            return SendMessage(this.Handle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero) == IntPtr.Zero;
        }
        public bool SendMessage(UInt32 Msg, IntPtr wParam, IntPtr lParam)
        {
            return SendMessage(this.Handle, Msg, wParam, lParam) == IntPtr.Zero;
        }

        /// <summary>Resize the current window with the given params</summary>
        /// <param name="width">New width of the current windows</param>
        /// <param name="height">New height of the current windows</param>
        public bool Resize(int width, int height)
        {
            return MoveWindow(this.Handle, 0, 0, width, height, true);
        }

        /// <summary>Resize the current window with the given params</summary>
        /// <param name="pixels">this will use as new width and new height of the windows</param>
        public bool Resize(int pixels)
        {
            return MoveWindow(this.Handle, 0, 0, pixels, pixels, true);
        }

        /// <summary>Move the current window with the given params</summary>
        /// <param name="X">New X coordinate of the current windows</param>
        /// <param name="Y">New Y coordinate of the current windows</param>
        public bool Move(int X, int Y)
        {
            RectStruct rect = new RectStruct();
            GetWindowRect(this.Handle, ref rect);

            rect.Width = rect.Right - rect.Left + Amount;
            rect.Height = rect.Bottom - rect.Top + Amount;
            return MoveWindow(this.Handle, X, Y, rect.Width, rect.Height, true);
        }
        public bool Move(int X, int Y, int W, int H)
        {
            return MoveWindow(this.Handle, X, Y, W, H, true);
        }

        /// <summary>Return the position of the windows as X, Y coordinates</summary>
        public Point Position()
        {
            RectStruct rect = new RectStruct();
            GetWindowRect(this.Handle, ref rect);

            return new Point(rect.Left, rect.Top);
        }

        public int Left
        {

            get
            {
                return (int) Position().X;
            }

            set
            {
                Move(value, (int)Position().Y);
            }
        }
        public int Top
        {

            get
            {
                return (int)Position().Y;
            }

            set
            {
                Move((int)Position().X, value);
            }
        }

        public int Width
        {

            get
            {
                return (int)Size().Width;
            }

            set
            {
                Resize(value, (int)Size().Height);
            }
        }
        public int Height
        {

            get
            {
                return (int)Size().Height;
            }

            set
            {
                Resize((int)Size().Width, value);
            }
        }

        /// <summary>Return the Size of the windows as width, height coordinates</summary>
        public Size Size()
        {
            RectStruct rect = new RectStruct();
            GetWindowRect(this.Handle, ref rect);

            rect.Width = rect.Right - rect.Left + Amount;
            rect.Height = rect.Bottom - rect.Top + Amount;
            return new Size(rect.Width, rect.Height);
        }

        /// <summary>Return the position and size of the windows as X, Y, with, height coordinates</summary>
        /*
        public Rect Area()
        {
            RectStruct rect = new RectStruct();
            GetWindowRect(this.Handle, ref rect);

            rect.Width = rect.Right - rect.Left + Amount;
            rect.Height = rect.Bottom - rect.Top + Amount;
            return new System.Windows.(rect.Left, rect.Top, rect.Width, rect.Height);
        }
        */

        /// <summary>Check if the current windows is visible</summary>
        public bool IsVisible()
        {
            return IsWindowVisible(this.Handle);
        }
        [DllImport("user32.dll")]
        public static extern bool AllowSetForegroundWindow(int dwProcessId);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("user32.dll")]
        public static extern IntPtr GetActiveWindow();

        [DllImport("user32.dll")]
        public static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

        [DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

        [DllImport("user32.dll")]
        public static extern bool GetWindowRect(IntPtr hWnd, ref RectStruct rectangle);

        [DllImport("kernel32.dll")]
        public static extern int GetProcessId(IntPtr handle);

        [DllImport("user32.dll")]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);

        [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SetActiveWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetFocus(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

        [DllImport("user32.dll")]
        public static extern IntPtr GetFocus();

        [DllImport("user32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int GetWindowTextLength(IntPtr hWnd);

        private enum ShowWindowCommands
        {
            /// <summary>
            /// Hides the window and activates another window.
            /// </summary>
            Hide = 0,
            /// <summary>
            /// Activates and displays a window. If the window is minimized or
            /// maximized, the system restores it to its original size and position.
            /// An application should specify this flag when displaying the window
            /// for the first time.
            /// </summary>
            Normal = 1,
            /// <summary>
            /// Activates the window and displays it as a minimized window.
            /// </summary>
            ShowMinimized = 2,
            /// <summary>
            /// Maximizes the specified window.
            /// </summary>
            Maximize = 3, // is this the right value?
            /// <summary>
            /// Activates the window and displays it as a maximized window.
            /// </summary>
            ShowMaximized = 3,
            /// <summary>
            /// Displays a window in its most recent size and position. This value
            /// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except
            /// the window is not activated.
            /// </summary>
            ShowNoActivate = 4,
            /// <summary>
            /// Activates the window and displays it in its current size and position.
            /// </summary>
            Show = 5,
            /// <summary>
            /// Minimizes the specified window and activates the next top-level
            /// window in the Z order.
            /// </summary>
            Minimize = 6,
            /// <summary>
            /// Displays the window as a minimized window. This value is similar to
            /// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the
            /// window is not activated.
            /// </summary>
            ShowMinNoActive = 7,
            /// <summary>
            /// Displays the window in its current size and position. This value is
            /// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the
            /// window is not activated.
            /// </summary>
            ShowNA = 8,
            /// <summary>
            /// Activates and displays the window. If the window is minimized or
            /// maximized, the system restores it to its original size and position.
            /// An application should specify this flag when restoring a minimized window.
            /// </summary>
            Restore = 9,
            /// <summary>
            /// Sets the show state based on the SW_* value specified in the
            /// STARTUPINFO structure passed to the CreateProcess function by the
            /// program that started the application.
            /// </summary>
            ShowDefault = 10,
            /// <summary>
            /// <b>Windows 2000/XP:</b> Minimizes a window, even if the thread
            /// that owns the window is not responding. This flag should only be
            /// used when minimizing windows from a different thread.
            /// </summary>
            ForceMinimize = 11
        }
        private string GetWindowClass(string windowClass)
        {
            List<string> values = new List<string>(){
                "ComboLBox","DDEMLEvent","Message","#32768",
                "#32769","#32770","#32771","#32772","Button","Edit","ListBox","MDIClient",
                "ScrollBar","Static",""
            };

            if (windowClass == "#32768")
                return "Menu";
            else if (windowClass == "#32769")
                return "DektopWindow";
            else if (windowClass == "#32770")
                return "DialogBox";
            else if (windowClass == "#32771")
                return "TaskSwitchWindowClass";
            else if (windowClass == "#32772")
                return "IconTitlesClass";
            return values.SingleOrDefault(s => s == windowClass);
        }

        /// <summary>
        /// Sets or removes the "Always On Top" state of the window
        /// </summary>
        /// <param name="alwaysOnTop">True to set the window always on top, false otherwise</param>
        /// <returns>True if successful</returns>
        public bool SetAlwaysOnTop(bool alwaysOnTop)
        {
            uint exStyle = GetWindowLong(Handle, GWL_EXSTYLE);

            if (alwaysOnTop)
                exStyle |= WS_EX_TOPMOST;
            else
                exStyle &= ~WS_EX_TOPMOST;

            return SetWindowLong(Handle, GWL_EXSTYLE, exStyle) != 0;
        }

        /// <summary>
        /// Sets the opacity/transparency level of the window
        /// </summary>
        /// <param name="opacity">Opacity level from 0 (transparent) to 255 (opaque)</param>
        /// <returns>True if successful</returns>
        public bool SetOpacity(byte opacity)
        {
            uint exStyle = GetWindowLong(Handle, GWL_EXSTYLE);
            exStyle |= WS_EX_LAYERED;
            SetWindowLong(Handle, GWL_EXSTYLE, exStyle);
            return SetLayeredWindowAttributes(Handle, 0, opacity, LWA_ALPHA);
        }

        // Struct to save window position and state
        /// <summary>
        /// Serializable class that stores the complete state of a window,
        /// including position, size, and various window states.
        /// </summary>
        [Serializable]
        public class WindowState
        {
            public int X { get; set; }
            public int Y { get; set; }
            public int Width { get; set; }
            public int Height { get; set; }
            public bool IsMaximized { get; set; }
            public bool IsMinimized { get; set; }
            public bool IsAlwaysOnTop { get; set; }
            public byte Opacity { get; set; }
            public string Title { get; set; }
        }

        /// <summary>
        /// Captures the current state of the window
        /// </summary>
        /// <returns>A WindowState object containing the window's current state</returns>
        public WindowState CaptureState()
        {
            RectStruct rect = new RectStruct();
            GetWindowRect(Handle, ref rect);

            return new WindowState
            {
                X = rect.Left,
                Y = rect.Top,
                Width = rect.Right - rect.Left,
                Height = rect.Bottom - rect.Top,
                IsMaximized = IsMaximized(),
                IsMinimized = IsMinimized(),
                IsAlwaysOnTop = IsAlwaysOnTop(),
                Opacity = GetOpacity(),
                Title = Title
            };
        }

        /// <summary>
        /// Restores a previously captured window state
        /// </summary>
        /// <param name="state">The WindowState to restore</param>
        public void RestoreState(WindowState state)
        {
            if (state.IsMaximized)
                Maximize();
            else if (state.IsMinimized)
                Minimize();
            else
                Move(state.X, state.Y, state.Width, state.Height);

            SetAlwaysOnTop(state.IsAlwaysOnTop);
            SetOpacity(state.Opacity);
        }

        /// <summary>
        /// Checks if the window is currently maximized
        /// </summary>
        public bool IsMaximized()
        {
            return IsZoomed(Handle);
        }

        /// <summary>
        /// Checks if the window is currently minimized
        /// </summary>
        public bool IsMinimized()
        {
            return IsIconic(Handle);
        }

        /// <summary>
        /// Checks if the window is set to always be on top
        /// </summary>
        public bool IsAlwaysOnTop()
        {
            uint exStyle = GetWindowLong(Handle, GWL_EXSTYLE);
            return (exStyle & WS_EX_TOPMOST) != 0;
        }

        /// <summary>
        /// Gets the current opacity of the window
        /// </summary>
        public byte GetOpacity()
        {
            uint exStyle = GetWindowLong(Handle, GWL_EXSTYLE);
            if ((exStyle & WS_EX_LAYERED) == 0)
                return 255;

            byte opacity = 255;
            uint flags = 0;
            uint key = 0;
            GetLayeredWindowAttributes(Handle, ref key, ref opacity, ref flags);
            return opacity;
        }

        /// <summary>
        /// Fades the window in or out with animation
        /// </summary>
        /// <param name="fadeIn">True to fade in, false to fade out</param>
        /// <param name="duration">Duration of the animation in milliseconds</param>
        public void FadeWindow(bool fadeIn, int duration = 200)
        {
            byte startOpacity = fadeIn ? (byte)0 : (byte)255;
            byte endOpacity = fadeIn ? (byte)255 : (byte)0;
            int steps = 10;
            int delay = duration / steps;

            SetOpacity(startOpacity);
            if (fadeIn) Show();

            for (int i = 1; i <= steps; i++)
            {
                byte currentOpacity = (byte)(startOpacity + ((endOpacity - startOpacity) * i / steps));
                SetOpacity(currentOpacity);
                System.Threading.Thread.Sleep(delay);
            }

            if (!fadeIn) Hide();
        }

        /// <summary>
        /// Snaps the window to the nearest screen edge
        /// </summary>
        public void SnapToEdge()
        {
            var position = Position();
            var size = Size();
            var screen = ScreenFromPoint(new Point((int)position.X, (int)position.Y));

            int snapDistance = 20; // pixels
            var workArea = screen.WorkingArea;

            int x = (int)position.X;
            int y = (int)position.Y;

            // Snap to left edge
            if (System.Math.Abs(position.X - workArea.Left) < snapDistance)
                x = (int)workArea.Left;

            // Snap to right edge
            if (Math.Abs((position.X + size.Width) - workArea.Right) < snapDistance)
                x = (int)(workArea.Right - size.Width);

            // Snap to top edge
            if (System.Math.Abs(position.Y - workArea.Top) < snapDistance)
                y = (int)workArea.Top;

            // Snap to bottom edge
            if (System.Math.Abs((position.Y + size.Height) - workArea.Bottom) < snapDistance)
                y = (int)(workArea.Bottom - size.Height);

            Move(x, y);
        }

        private WpfScreenHelper.Screen ScreenFromPoint(Point point)
        {
            var p = new System.Windows.Point();
            p.X = point.X;
            p.Y = point.Y;

            return WpfScreenHelper.Screen.FromPoint(p);
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool IsZoomed(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool IsIconic(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetLayeredWindowAttributes(IntPtr hwnd, ref uint crKey, ref byte bAlpha, ref uint dwFlags);

        /// <summary>
        /// Removes the window border and title bar by modifying window styles.
        /// This creates a borderless window appearance.
        /// </summary>
        public void RemoveBorder()
        {
            uint style = GetWindowLong(this.Handle, GWL_STYLE);
            style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
            SetWindowLong(this.Handle, GWL_STYLE, style);
        }

        /// <summary>
        /// Positions the window on the left half of the screen
        /// </summary>
        public bool PositionLeft()
        {
            try
            {
                // First restore the window if it's maximized or minimized
                if (IsMaximized() || IsMinimized())
                {
                    Restore();
                    System.Threading.Thread.Sleep(50); // Give OS time to complete operation
                }

                var screen = ScreenFromHandle(Handle);
                int width = (int) screen.WorkingArea.Width / 2;
                int height = (int)screen.WorkingArea.Height;

                // Special handling for known problematic window classes or Electron apps
                bool isElectronApp = IsElectronApp();
                if (isElectronApp)
                {
                    // For Electron apps like VS Code, do an extra step to ensure it works
                    // First set a different size to force a refresh
                    MoveWindow(Handle, (int)screen.WorkingArea.X, (int)screen.WorkingArea.Y, width - 1, height, true);
                    System.Threading.Thread.Sleep(10);
                }

                return MoveWindow(Handle, (int)screen.WorkingArea.X, (int)screen.WorkingArea.Y, width, height, true);
            }
            catch
            {
                // Fallback to basic implementation if anything goes wrong
                try
                {
                    var screen = ScreenFromHandle(Handle);
                    int width = (int)screen.WorkingArea.Width / 2;
                    int height = (int)screen.WorkingArea.Height;
                    return MoveWindow(Handle, (int)screen.WorkingArea.X, (int)screen.WorkingArea.Y, width, height, true);
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Positions the window on the right half of the screen
        /// </summary>
        public bool PositionRight()
        {
            try
            {
                // First restore the window if it's maximized or minimized
                if (IsMaximized() || IsMinimized())
                {
                    Restore();
                    System.Threading.Thread.Sleep(50); // Give OS time to complete operation
                }

                var screen = ScreenFromHandle(Handle);
                int width = (int)screen.WorkingArea.Width / 2;
                int height = (int)screen.WorkingArea.Height;
                int x = (int)(screen.WorkingArea.X + screen.WorkingArea.Width - width);

                // Special handling for known problematic window classes or Electron apps
                bool isElectronApp = IsElectronApp();
                if (isElectronApp)
                {
                    // For Electron apps like VS Code, do an extra step to ensure it works
                    // First set a different size to force a refresh
                    MoveWindow(Handle, x, (int)screen.WorkingArea.Y, width - 1, height, true);
                    System.Threading.Thread.Sleep(10);
                }

                return MoveWindow(Handle, x, (int)screen.WorkingArea.Y, width, height, true);
            }
            catch
            {
                // Fallback to basic implementation if anything goes wrong
                try
                {
                    var screen = ScreenFromHandle(Handle);
                    int width = (int)screen.WorkingArea.Width / 2;
                    int height = (int)screen.WorkingArea.Height;
                    int x = (int)(screen.WorkingArea.X + screen.WorkingArea.Width - width);
                    return MoveWindow(Handle, x, (int)screen.WorkingArea.Y, width, height, true);
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Positions the window on the top half of the screen
        /// </summary>
        public bool PositionTop()
        {
            var screen = ScreenFromHandle(Handle);
            int width = (int)screen.WorkingArea.Width;
            int height = (int)screen.WorkingArea.Height / 2;
            return MoveWindow(Handle, (int)screen.WorkingArea.X, (int)screen.WorkingArea.Y, width, height, true);
        }

        /// <summary>
        /// Positions the window on the bottom half of the screen
        /// </summary>
        public bool PositionBottom()
        {
            var screen = ScreenFromHandle(Handle);
            int width = (int)screen.WorkingArea.Width;
            int height = (int)screen.WorkingArea.Height / 2;
            int y = (int) (screen.WorkingArea.Y + screen.WorkingArea.Height - height);
            return MoveWindow(Handle, (int)screen.WorkingArea.X, y, width, height, true);
        }

        /// <summary>
        /// Positions the window in the top-left corner of the screen (quarter screen)
        /// </summary>
        public bool PositionTopLeft()
        {
            var screen = ScreenFromHandle(Handle);
            int width = (int)screen.WorkingArea.Width / 2;
            int height = (int)screen.WorkingArea.Height / 2;
            return MoveWindow(Handle, (int)screen.WorkingArea.X, (int)screen.WorkingArea.Y, width, height, true);
        }

        /// <summary>
        /// Positions the window in the top-right corner of the screen (quarter screen)
        /// </summary>
        public bool PositionTopRight()
        {
            var screen = ScreenFromHandle(Handle);
            int width = (int)screen.WorkingArea.Width / 2;
            int height = (int)screen.WorkingArea.Height / 2;
            int x = (int) (screen.WorkingArea.X + screen.WorkingArea.Width - width);
            return MoveWindow(Handle, x, (int)screen.WorkingArea.Y, width, height, true);
        }

        /// <summary>
        /// Positions the window in the bottom-left corner of the screen (quarter screen)
        /// </summary>
        public bool PositionBottomLeft()
        {
            var screen = ScreenFromHandle(Handle);
            int width = (int)screen.WorkingArea.Width / 2;
            int height = (int)screen.WorkingArea.Height / 2;
            int y = (int) (screen.WorkingArea.Y + screen.WorkingArea.Height - height);
            return MoveWindow(Handle, (int)screen.WorkingArea.X, y, width, height, true);
        }

        /// <summary>
        /// Positions the window in the bottom-right corner of the screen (quarter screen)
        /// </summary>
        public bool PositionBottomRight()
        {
            var screen = ScreenFromHandle(Handle);
            int width = (int)screen.WorkingArea.Width / 2;
            int height = (int)screen.WorkingArea.Height / 2;
            int x = (int) (screen.WorkingArea.X + screen.WorkingArea.Width - width);
            int y = (int) (screen.WorkingArea.Y + screen.WorkingArea.Height - height);
            return MoveWindow(Handle, x, y, width, height, true);
        }

        /// <summary>
        /// Positions the window in the center of the screen
        /// </summary>
        public bool PositionCentered()
        {
            var screen = ScreenFromHandle(Handle);
            int width = (int)(screen.WorkingArea.Width * 0.8);
            int height = (int)(screen.WorkingArea.Height * 0.8);
            int x = (int) (screen.WorkingArea.X + (screen.WorkingArea.Width - width) / 2);
            int y = (int) (screen.WorkingArea.Y + (screen.WorkingArea.Height - height) / 2);
            return MoveWindow(Handle, x, y, width, height, true);
        }

        private WpfScreenHelper.Screen ScreenFromHandle(IntPtr handle)
        {
            return WpfScreenHelper.Screen.FromHandle(handle);
        }

        /// <summary>
        /// Positions the window to fill the entire screen (without maximizing)
        /// </summary>
        public bool PositionFullscreen()
        {
            var screen = ScreenFromHandle(Handle);
            return MoveWindow(Handle, (int)screen.WorkingArea.X, (int)screen.WorkingArea.Y,
                             (int)screen.WorkingArea.Width, (int)(int)screen.WorkingArea.Height, true);
        }

        /// <summary>
        /// Moves the window to a specified monitor
        /// </summary>
        /// <param name="monitorIndex">Monitor index: 0=primary, 1+=specific monitor (1-based index), -2=secondary monitor</param>
        /// <param name="preserveState">If true, preserves maximized/minimized state after moving</param>
        /// <returns>True if successful</returns>
        public bool MoveToMonitor(int monitorIndex, bool preserveState = true)
        {
            // Save current window state
            bool wasMaximized = IsMaximized();
            bool wasMinimized = IsMinimized();

            // Need to restore window before moving if it's maximized/minimized
            if (wasMaximized || wasMinimized)
            {
                Restore();
                System.Threading.Thread.Sleep(100); // Give OS time to complete the operation
            }

            // Get current size
            var size = Size();

            // Get all available screens
            var allScreens = WpfScreenHelper.Screen.AllScreens.ToList();
            WpfScreenHelper.Screen targetScreen = null;

            // Determine target monitor
            if (monitorIndex == 0)
            {
                // Primary monitor
                targetScreen = WpfScreenHelper.Screen.PrimaryScreen;
            }
            else if (monitorIndex == -2)
            {
                // Try to get from global variable in PowerShell
                // Default to secondary monitor if available, or current monitor
                try
                {
                    // We can't access PowerShell variables directly, so we'll rely on the caller
                    // to translate the -2 value appropriately
                    if (allScreens.Count > 1)
                    {
                        // Use second monitor as default secondary
                        targetScreen = allScreens[1];
                    }
                    else
                    {
                        // Fall back to primary if only one monitor
                        targetScreen = WpfScreenHelper.Screen.PrimaryScreen;
                    }
                }
                catch
                {
                    // Default to primary if anything goes wrong
                    targetScreen = WpfScreenHelper.Screen.PrimaryScreen;
                }
            }
            else if (monitorIndex > 0 && monitorIndex <= allScreens.Count)
            {
                // Specific monitor (1-based index)
                targetScreen = allScreens[monitorIndex - 1];
            }
            else
            {
                // Invalid monitor index, use current
                targetScreen = WpfScreenHelper.Screen.FromHandle(Handle);
            }

            // Center window on target monitor's working area
            int newX = (int)targetScreen.WorkingArea.X + (int)((targetScreen.WorkingArea.Width - size.Width) / 2.0);
            int newY = (int)targetScreen.WorkingArea.Y + (int)((targetScreen.WorkingArea.Height - size.Height) / 2.0);

            // Move window to new position
            bool result = Move(newX, newY);

            // Restore previous state if requested
            if (preserveState)
            {
                if (wasMaximized)
                {
                    Maximize();
                }
                else if (wasMinimized)
                {
                    Minimize();
                }
            }

            return result;
        }

        /// <summary>
        /// Gets the index of the monitor that the window is currently on
        /// </summary>
        /// <returns>
        /// Monitor index where the window is located:
        /// 0 = primary monitor
        /// 1+ = specific monitor (1-based index)
        /// -1 = unable to determine
        /// </returns>
        public int GetCurrentMonitor()
        {
            try
            {
                // Get window position (center point of the window)
                var windowRect = new RectStruct();
                GetWindowRect(Handle, ref windowRect);
                int centerX = windowRect.Left + ((windowRect.Right - windowRect.Left) / 2);
                int centerY = windowRect.Top + ((windowRect.Bottom - windowRect.Top) / 2);

                // Get all screens
                var allScreens = WpfScreenHelper.Screen.AllScreens.ToList();

                // Find which screen contains this point
                for (int i = 0; i < allScreens.Count; i++)
                {
                    var screen = allScreens[i];
                    if (centerX >= screen.Bounds.Left && centerX <= screen.Bounds.Right &&
                        centerY >= screen.Bounds.Top && centerY <= screen.Bounds.Bottom)
                    {
                        return i;
                    }
                }

                // If we reach here, try a different approach using FromHandle
                return 0;
            }
            catch
            {
                return 0;
            }
        }

        // Add helper method to detect Electron apps like VS Code
        /// <summary>
        /// Determines if the window belongs to an Electron-based application.
        /// Electron apps have specific window class names and process names.
        /// </summary>
        /// <returns>True if the window is from an Electron app</returns>
        private bool IsElectronApp()
        {
            // Check window class name first
            if (WindowClassName.Contains("Chrome_WidgetWin") ||
                WindowClassName.Contains("Electron") ||
                Title.Contains("Visual Studio Code"))
            {
                return true;
            }

            // Try to get process name as additional check
            try
            {
                uint processId = 0;
                GetWindowThreadProcessId(Handle, out processId);
                if (processId != 0)
                {
                    var process = System.Diagnostics.Process.GetProcessById((int)processId);
                    string procName = process?.ProcessName?.ToLowerInvariant() ?? "";
                    return procName.Contains("code") || procName.Contains("electron");
                }
            }
            catch
            {
                // Ignore errors in process identification
            }

            return false;
        }

        // Helper struct for docked state
        /// <summary>
        /// Structure that tracks which edges and corners of the screen
        /// the window is docked to.
        /// </summary>
        private struct DockedState
        {
            public bool Left;
            public bool Top;
            public bool Right;
            public bool Bottom;
            public bool LeftTop;
            public bool TopRight;
            public bool BottomLeft;
            public bool BottomRight;
        }

        // Helper method to calculate docked state
        /// <summary>
        /// Calculates the current docking state of the window based on its position
        /// and size relative to the monitor's work area.
        /// </summary>
        /// <returns>A DockedState struct indicating which edges/corners are docked</returns>
        private DockedState GetDockedState()
        {
            var state = new DockedState();

            if (IsMaximized()) return state; // Maximized windows are not considered docked

            // Get current screen
            var allScreens = WpfScreenHelper.Screen.AllScreens.ToList();
            var monitorIndex = GetCurrentMonitor();
            if (monitorIndex < 0 || monitorIndex >= allScreens.Count)
                return state;

            var currentScreen = allScreens[monitorIndex];

            // Get window position and size
            var position = Position();
            var size = Size();

            // Calculate relative position within monitor's work area
            var workArea = currentScreen.WorkingArea;
            var relativeX = (position.X - workArea.X) / (double)workArea.Width;
            var relativeY = (position.Y - workArea.Y) / (double)workArea.Height;
            var relativeWidth = size.Width / (double)workArea.Width;
            var relativeHeight = size.Height / (double)workArea.Height;

            double tolerance = 0.02; // 2% tolerance for position detection
            double sizeTolerance = 0.4; // 40% minimum size to consider positioned

            // Detect docking based on relative position and size
            if (relativeWidth >= sizeTolerance)
            {
                if (relativeX <= tolerance)
                {
                    state.Left = true;
                }
                else if ((relativeX + relativeWidth) >= (1.0 - tolerance))
                {
                    state.Right = true;
                }
            }
            if (relativeHeight >= sizeTolerance)
            {
                if (relativeY <= tolerance)
                {
                    state.Top = true;
                }
                else if ((relativeY + relativeHeight) >= (1.0 - tolerance))
                {
                    state.Bottom = true;
                }
            }

            // Detect corner docking
            if (state.Left && state.Top)
            {
                state.LeftTop = true;
            }
            if (state.Top && state.Right)
            {
                state.TopRight = true;
            }
            if (state.Bottom && state.Left)
            {
                state.BottomLeft = true;
            }
            if (state.Bottom && state.Right)
            {
                state.BottomRight = true;
            }

            return state;
        }

        // Properties for Get-WindowPosition hashtable compatibility
        public int MonitorIndex
        {
            get { return GetCurrentMonitor(); }
            set
            {
                // Get current docked state before moving
                var dockedState = GetDockedState();
                bool wasDocked = dockedState.Left || dockedState.Top || dockedState.Right || dockedState.Bottom;

                // Move to new monitor
                MoveToMonitor(value);

                // If was docked, restore docking on new monitor
                if (wasDocked)
                {
                    if (dockedState.LeftTop)
                        PositionTopLeft();
                    else if (dockedState.TopRight)
                        PositionTopRight();
                    else if (dockedState.BottomLeft)
                        PositionBottomLeft();
                    else if (dockedState.BottomRight)
                        PositionBottomRight();
                    else if (dockedState.Left)
                        PositionLeft();
                    else if (dockedState.Right)
                        PositionRight();
                    else if (dockedState.Top)
                        PositionTop();
                    else if (dockedState.Bottom)
                        PositionBottom();
                }
                else
                {
                    int currentMonitor = GetCurrentMonitor();
                    if (value == currentMonitor) return;
                    var allScreens = WpfScreenHelper.Screen.AllScreens.ToList();
                    if (value < 0 || value >= allScreens.Count) return;
                    var currentScreen = allScreens[currentMonitor];
                    var newScreen = allScreens[value];
                    // Get current position and size
                    var position = Position();
                    var size = Size();
                    // Current work area
                    var currentWorkArea = currentScreen.WorkingArea;
                    // Calculate relative
                    double relativeX = (position.X - currentWorkArea.X) / (double)currentWorkArea.Width;
                    double relativeY = (position.Y - currentWorkArea.Y) / (double)currentWorkArea.Height;
                    double relativeWidth = size.Width / (double)currentWorkArea.Width;
                    double relativeHeight = size.Height / (double)currentWorkArea.Height;
                    // New work area
                    var newWorkArea = newScreen.WorkingArea;
                    // New position and size
                    int newX = (int)Math.Round(newWorkArea.X + relativeX * newWorkArea.Width);
                    int newY = (int)Math.Round(newWorkArea.Y + relativeY * newWorkArea.Height);
                    int newWidth = (int)Math.Round(relativeWidth * newWorkArea.Width);
                    int newHeight = (int)Math.Round(relativeHeight * newWorkArea.Height);
                    // Set position and size
                    SetWindowPos(Handle, IntPtr.Zero, newX, newY, newWidth, newHeight, DesktopInfo.SWP_NOZORDER);
                }
            }
        }

        public int Monitor
        {
            get { return MonitorIndex + 1; }
            set { MonitorIndex = value - 1; }
        }

        public bool DockedLeft
        {
            get { return GetDockedState().Left; }
            set
            {
                if (value)
                {
                    PositionLeft();
                }
                else
                {
                    var ds = GetDockedState();
                    if (ds.LeftTop)
                    {
                        PositionTop();

                    }
                    else if (ds.BottomLeft)
                    {
                        PositionBottom();
                    }
                    else if (ds.Left)
                    { // Move to center if only left docked
                        PositionCentered();
                    }
                }
            }
        }

        public bool DockedTop
        {
            get { return GetDockedState().Top; }
            set
            {
                if (value)
                {
                    PositionTop();
                }
                else
                {
                    var ds = GetDockedState();
                    if (ds.LeftTop)
                    {
                        PositionLeft();
                    }
                    else if (ds.TopRight)
                    {
                        PositionRight();
                    }
                    else if (ds.Top)
                    { // Move to center if only top docked
                        PositionCentered();
                    }
                }
            }
        }

        public bool DockedRight
        {
            get { return GetDockedState().Right; }
            set
            {
                if (value)
                {
                    PositionRight();
                }
                else
                {
                    var ds = GetDockedState();
                    if (ds.TopRight)
                    {
                        PositionTop();
                    }
                    else if (ds.BottomRight)
                    {
                        PositionBottom();
                    }
                    else if (ds.Right)
                    { // Move to center if only right docked
                        PositionCentered();
                    }
                }
            }
        }

        public bool DockedBottom
        {
            get { return GetDockedState().Bottom; }
            set
            {
                if (value)
                {
                    PositionBottom();
                }
                else
                {
                    var ds = GetDockedState();
                    if (ds.BottomLeft)
                    {
                        PositionLeft();
                    }
                    else if (ds.BottomRight)
                    {
                        PositionRight();
                    }
                    else if (ds.Bottom)
                    { // Move to center if only bottom docked
                        PositionCentered();
                    }
                }
            }
        }

        public bool DockedLeftTop
        {
            get { return GetDockedState().LeftTop; }
            set
            {
                if (value)
                {
                    PositionTopLeft();
                }
                else
                {
                    var ds = GetDockedState();
                    if (ds.LeftTop)
                    {
                        PositionCentered();
                    }
                }
            }
        }

        public bool DockedTopRight
        {
            get { return GetDockedState().TopRight; }
            set
            {
                if (value)
                {
                    PositionTopRight();
                }
                else
                {
                    var ds = GetDockedState();
                    if (ds.TopRight)
                    {
                        PositionCentered();
                    }
                }
            }
        }

        public bool DockedBottomLeft
        {
            get { return GetDockedState().BottomLeft; }
            set
            {
                if (value)
                {
                    PositionBottomLeft();
                }
                else
                {
                    var ds = GetDockedState();
                    if (ds.BottomLeft)
                    {
                        PositionCentered();
                    }
                }

            }
        }

        public bool DockedBottomRight
        {
            get { return GetDockedState().BottomRight; }
            set
            {
                if (value)
                {
                    PositionBottomRight();
                }
                else
                {
                    var ds = GetDockedState();
                    if (ds.BottomRight)
                    {
                        PositionCentered();
                    }
                }
            }
        }

        public bool IsRestored
        {
            get { return !IsMinimized() && !IsMaximized(); }
            set
            {
                if (value)
                {
                    Restore();
                }
                else
                {
                    Minimize();
                }
            }
        }

        public bool IsHidden
        {
            get { return !IsVisible(); }
            set
            {
                if (value)
                {
                    Hide();
                }
                else
                {
                    Show();
                }
            }
        }

        public Process Process
        {
            get
            {
                try
                {
                    uint processId = 0;
                    GetWindowThreadProcessId(Handle, out processId);
                    return Process.GetProcessById((int)processId);
                }
                catch
                {
                    return null;
                }
            }
        }

        public bool HasFocus
        {
            get
            {
                var focused = GetFocusedWindow();
                return focused != null && focused.Handle == Handle;
            }
            set
            {
                if (value)
                {
                    Focus();
                }
            }
        }

        public bool IsInForeground
        {
            get { return HasFocus; }
            set
            {
                if (value)
                {
                    SetForeground();
                }
                else
                {
                    SetBackground();
                }
            }
        }

        /// <summary>
        /// Sends the window to the background by positioning it at the bottom of the Z-order.
        /// </summary>
        private void SetBackground()
        {
            // Send window to back using SetWindowPos with HWND_BOTTOM
            SetWindowPos(this.Handle, new IntPtr(1), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
        }

        [DllImport("user32.dll")]
        public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [DllImport("user32.dll")]
        public static extern bool BringWindowToTop(IntPtr hWnd);

        /// <summary>
        /// Gets the parent process of the specified process
        /// </summary>
        /// <param name="process">The process to find the parent for</param>
        /// <returns>The parent process, or null if not found</returns>
        private static Process GetParentProcess(Process process)
        {
            try
            {
                var parentId = GetParentProcessId(process.Id);
                return parentId > 0 ? Process.GetProcessById(parentId) : null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the parent process ID using WMI
        /// </summary>
        /// <param name="processId">The process ID to find the parent for</param>
        /// <returns>The parent process ID, or 0 if not found</returns>
        private static int GetParentProcessId(int processId)
        {
            try
            {
                using (var searcher = new System.Management.ManagementObjectSearcher(
                    $"SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {processId}"))
                {
                    using (var results = searcher.Get())
                    {
                        var result = results.Cast<System.Management.ManagementObject>().FirstOrDefault();
                        return result != null ? Convert.ToInt32(result["ParentProcessId"]) : 0;
                    }
                }
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// Determines if a process name suggests it's likely a terminal or host application
        /// </summary>
        /// <param name="processName">The process name to check</param>
        /// <returns>True if the process name indicates a terminal/host app</returns>
        private static bool IsLikelyHostProcess(string processName)
        {
            if (string.IsNullOrEmpty(processName))
                return false;

            var name = processName.ToLowerInvariant();

            // Common terminal/host process names
            return name.Contains("terminal") ||
                   name.Contains("console") ||
                   name.Contains("cmd") ||
                   name.Contains("conhost") ||
                   name.Contains("wsl") ||
                   name.Contains("bash") ||
                   name.Contains("ssh") ||
                   name.Contains("cursor") ||
                   name.Contains("putty") ||
                   name.Contains("code") || // VS Code
                   name.Contains("code - insiders") || // VS Code
                   name.Contains("idea") || // IntelliJ
                   name.Contains("eclipse") || // Eclipse
                   name.Contains("cmder") || // Cmder
                   name.Contains("conemu") || // ConEmu
                   name.Contains("hyper") || // Hyper terminal
                   name.Contains("alacritty") || // Alacritty
                   name.Contains("kitty") || // Kitty terminal
                   name.Contains("iterm") || // iTerm (if running on Windows via compatibility)
                   name.Contains("mintty"); // MinTTY (Git Bash, etc.)
        }
    }
}