Развивание и каскадирование определенной программы для настольных пк windows

What you’re trying to do requires multiple, nontrivial P/Invoke calls to the WinAPI:

# Helper type for various window-related WinAPI functions.
Add-Type -Namespace Util -Name WinApi -MemberDefinition @' // The callback delegate, which receives: // - the hWnd being enumerated // - a custom LPARAM value passed on invocation to EnumWindows() // The delegate must return $true to keep enumerating; in other words: $false stops the enumeration. // Note the custom ArrayList parameter in lieu of an IntPtr (LPARAM) (using a generic list is NOT an option, because generic types cannot be marshalled). public delegate bool EnumWindowsProc(IntPtr hWnd, System.Collections.ArrayList lParam); // The EnumWindows() API function that enumerates *top-level windows*. // It too uses a custom ArrayList parameter instead of the IntPtr (LPARAM), which the callback receives. [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, System.Collections.ArrayList lParam); // Get the class name of a window by its handle. [DllImport("user32.dll")] public static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder classNameBuf, int nMaxCount); // Cascade the specified window(s). [DllImport("user32.dll")] public static extern ushort CascadeWindows(IntPtr hwndParent, uint wHow, IntPtr lpRect, uint cKids, IntPtr[] lpKids); // Show the specified window. [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); // Make the specified window the foreground window, if allowed (see below). [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); // Allow making the windows of other processes the foreground window // for the remainder of the session. [DllImport("user32.dll", EntryPoint="SystemParametersInfo")] static extern bool SystemParametersInfo_Set_UInt32(uint uiAction, uint uiParam, UInt32 pvParam, uint fWinIni); public static void AllowWindowActivation() { if (! SystemParametersInfo_Set_UInt32(0x2001 /* SPI_SETFOREGROUNDLOCKTIMEOUT */, 0, 0 /* timeout in secs */, 0 /* non-persistent change */)) { throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error(), "Unexpected failure calling SystemParametersInfo() with SPI_SETFOREGROUNDLOCKTIMEOUT"); } }
'@
# Get all Notepad windows, on the assumption that their window class name is 'Notepad'.
[System.Collections.ArrayList] $matchingHwnds = @()
$null = [Util.WinApi]::EnumWindows( { # delegate (callback function) param([intptr] $hWnd, [System.Collections.ArrayList] $param) $sb = [System.Text.StringBuilder]::new(1024) $null = [Util.WinApi]::GetClassName($hWnd, $sb, $sb.Capacity-1) if ($sb.ToString() -eq 'Notepad') { $param.Add($hWnd) } return $true # continue enumerating }, $matchingHwnds # the array list to pass as custom data
)
# Reverse the matching window handles (if any).
[IntPtr[]] $matchingHwndsInReverse = [Linq.Enumerable]::Reverse([IntPtr[]] $matchingHwnds)
# Make sure all windows are restored (non-minimized, non-maximized), in reverse order.
foreach ($hwnd in $matchingHwndsInReverse) { $null = [Util.WinAPI]::ShowWindow($hwnd, 1)
}
# Cascade them.
$null = [Util.WinAPI]::CascadeWindows([IntPtr]::Zero, 0, [IntPtr]::Zero, $matchingHwnds.Count, $matchingHwnds)
# Activate them in reverse order.
# First, enable cross-process window activation...
[Util.WinAPI]::AllowWindowActivation()
# ... then activate them.
foreach ($hwnd in $matchingHwndsInReverse) { $null = [Util.WinAPI]::SetForegroundWindow($hwnd)
}

As for what you tried:

  • Shell.Application.CascadeWindows() indeed by design invariably cascades all (presumably non-minimized only) windows – you cannot constrain it to only a given application’s windows.

    • Additionally, this method seemingly no longer works as of Windows 11; similarly, the cascading feature has been removed from the GUI (you used to be able to right-click on the taskbar and select Cascade windows).
    • What you presumably meant was this – but it wouldn’t work on Windows 11, with the new, UWP implementation of Notepad:

      (Get-Process | Where {$_.MainWindowTitle -and $_.Description -like '*note*' }).MainWindowTitle
      # Simpler alternative:
      @((Get-Process notepad).MainWindowTitle) -ne $null
    • On Windows 11, with the new UWP implementation of Notepad.exe, only the title of a single Notepad window – namely the currently or most recently active one – is reflected in the .MainWindowTitle property of the Notepad processes, so that there’s no one-to-one relationship between processes and visible windows.

:/>  Как объединить незанятое пространство на диске

Therefore, a WinAPI-based solution, as shown above, is required, because it:

  • allows you to find top-level windows by class name, independently of examining processes.

  • allows you to cascade only a given set of windows (though you must activate them separately).