3

Проблема: у нас есть большой телевизор, который мы используем для презентаций, который не совсем 1920 × 1080 пикселей. Это выключено, и я не могу это контролировать. Что я могу контролировать, так это мой ноутбук с Windows, к которому я подключаюсь. Я искал способы получить все, что мне нужно, чтобы перейти к проектору, установить положение, которое будет смещено для хорошего просмотра на телевизоре, и установить ширину / высоту для того же.

Что было сделано: я могу использовать скрипт Boe Prox Get-Window (https://blogs.technet.microsoft.com/heyscriptingguy/2015/12/26/weekend-scripter-manage-window-placement-by-using-pinvoke/), и это работало нормально, чтобы получить размеры и смещение, которые мне нужны. Однако его ошибки Set-Window на мне как в WinPosh 5, так и в Posh 6 с правами администратора и без них. Другие потенциальные решения приводили к подобным ошибкам, поэтому я решил остаться со скриптом Прокса, так как он по моим стандартам эксперт.

Я оцениваю решения, упомянутые здесь, https://stackoverflow.com/questions/10392620/how-can-a-batch-file-run-a-program-and-set-the-position-and-size-of-the -окно / как возможные обходные пути. Однако решение PowerShell без каких-либо зависимостей от сторонних разработчиков было бы идеальным решением.

Вопрос в том, кто-нибудь решил, как заставить Set-Window мистера Прокса или что-либо еще установить положение и размер окна в PowerShell v5 или v6 прямо?

Сообщение об ошибке в Posh 5:

Cannot convert argument "hWnd", with value: "System.Object[]", for "GetWindowRect" to type "System.IntPtr": "Cannot
convert the "System.Object[]" value of type "System.Object[]" to type "System.IntPtr"."
At Z:\scripts\Set-Window.ps1:90 char:9
+         $Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Сообщения об ошибках в Posh 6:

PS C:\> Set-Window -ProcessName notepad -X 1911 -Y "-369" -Width 266 -Height 113
Method invocation failed because [Window] does not contain a method named 'MoveWindow'.
At Z:\scripts\Set-Window.ps1:98 char:13
+             $Return = [Window]::MoveWindow($Handle, $x, $y, $Width, $ ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound


PS C:\> Set-Window -ProcessName firefox -X "-9" -Y "-9" -Width "1938" -Height "1050"
Cannot convert argument "hWnd", with value: "System.Object[]", for "GetWindowRect" to type "System.IntPtr": "Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.IntPtr"."
At Z:\scripts\Set-Window.ps1:90 char:9
+         $Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Примечание. Эти ошибки возникают в Windows 7 (некоторое время нет доступа к Win10).

Обновление: я заметил размер / положение окна Set (Many) программ, но модуль UIAutomation не поддерживается (codeplex заархивирован, и последнее сообщение в блоге автора для него было февраль 2014 г.).

2 ответа2

4

По умолчанию командлет Get-Process возвращает System.Diagnostics.Process - или массив такого объекта, если есть больше совпадающих процессов. К сожалению, оригинальный скрипт Set-Window.ps1 не отражает последний сценарий.

Вывод с использованием улучшенного скрипта:

PS D:\PShell> Get-Process -ProcessName notepad

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    505      29    12104      41232       2,05   4208   1 notepad
    231      14     3068      13212       0,09   6732   1 notepad

PS D:\PShell> . D:\PShell\Downloaded\WindowManipulation\Set-Window.ps1

PS D:\PShell> Set-Window -ProcessName notepad -X 11 -Y 11 -Width 1200 -Height 900 -Passthru

ProcessName Size     TopLeft BottomRight
----------- ----     ------- -----------
notepad     1200,900 11,11   1211,911
notepad     1200,900 11,11   1211,911


PS D:\PShell>

Улучшенный скрипт (изменения прокомментированы):

Function Set-Window {
    <#
        .SYNOPSIS
            Sets the window size (height,width) and coordinates (x,y) of
            a process window.

        .DESCRIPTION
            Sets the window size (height,width) and coordinates (x,y) of
            a process window.

        .PARAMETER ProcessName
            Name of the process to determine the window characteristics

        .PARAMETER X
            Set the position of the window in pixels from the top.

        .PARAMETER Y
            Set the position of the window in pixels from the left.

        .PARAMETER Width
            Set the width of the window.

        .PARAMETER Height
            Set the height of the window.

        .PARAMETER Passthru
            Display the output object of the window.

        .NOTES
            Name: Set-Window
            Author: Boe Prox
            Version History
                1.0//Boe Prox - 11/24/2015
                    - Initial build
                1.1//JosefZ (https://superuser.com/users/376602/josefz) - 19.05.2018
                    - treats more process instances of supplied process name properly

        .OUTPUT
            System.Automation.WindowInfo

        .EXAMPLE
            Get-Process powershell | Set-Window -X 2040 -Y 142 -Passthru

            ProcessName Size     TopLeft  BottomRight
            ----------- ----     -------  -----------
            powershell  1262,642 2040,142 3302,784   

            Description
            -----------
            Set the coordinates on the window for the process PowerShell.exe

    #>
    [OutputType('System.Automation.WindowInfo')]
    [cmdletbinding()]
    Param (
        [parameter(ValueFromPipelineByPropertyName=$True)]
        $ProcessName,
        [int]$X,
        [int]$Y,
        [int]$Width,
        [int]$Height,
        [switch]$Passthru
    )
    Begin {
        Try{
            [void][Window]
        } Catch {
        Add-Type @"
              using System;
              using System.Runtime.InteropServices;
              public class Window {
                [DllImport("user32.dll")]
                [return: MarshalAs(UnmanagedType.Bool)]
                public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

                [DllImport("User32.dll")]
                public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
              }
              public struct RECT
              {
                public int Left;        // x position of upper-left corner
                public int Top;         // y position of upper-left corner
                public int Right;       // x position of lower-right corner
                public int Bottom;      // y position of lower-right corner
              }
"@
        }
    }
    Process {
        $Rectangle = New-Object RECT
        $Handles = (Get-Process -Name $ProcessName).MainWindowHandle   ### 1.1//JosefZ
        foreach ( $Handle in $Handles ) {                              ### 1.1//JosefZ
            if ( $Handle -eq [System.IntPtr]::Zero ) { Continue }      ### 1.1//JosefZ
            $Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)
            If (-NOT $PSBoundParameters.ContainsKey('Width')) {            
                $Width = $Rectangle.Right - $Rectangle.Left            
            }
            If (-NOT $PSBoundParameters.ContainsKey('Height')) {
                $Height = $Rectangle.Bottom - $Rectangle.Top
            }
            If ($Return) {
                $Return = [Window]::MoveWindow($Handle, $x, $y, $Width, $Height,$True)
            }
            If ($PSBoundParameters.ContainsKey('Passthru')) {
                $Rectangle = New-Object RECT
                $Return = [Window]::GetWindowRect($Handle,[ref]$Rectangle)
                If ($Return) {
                    $Height = $Rectangle.Bottom - $Rectangle.Top
                    $Width = $Rectangle.Right - $Rectangle.Left
                    $Size = New-Object System.Management.Automation.Host.Size -ArgumentList $Width, $Height
                    $TopLeft = New-Object System.Management.Automation.Host.Coordinates -ArgumentList $Rectangle.Left, $Rectangle.Top
                    $BottomRight = New-Object System.Management.Automation.Host.Coordinates -ArgumentList $Rectangle.Right, $Rectangle.Bottom
                    If ($Rectangle.Top -lt 0 -AND $Rectangle.LEft -lt 0) {
                        Write-Warning "Window is minimized! Coordinates will not be accurate."
                    }
                    $Object = [pscustomobject]@{
                        ProcessName = $ProcessName
                        Size = $Size
                        TopLeft = $TopLeft
                        BottomRight = $BottomRight
                    }
                    $Object.PSTypeNames.insert(0,'System.Automation.WindowInfo')
                    $Object            
                }
            }
        }
    }
}
0

Мне также пришлось изменить «User32.dll» на «user32.dll» в Windows 10 / PowerShell:

[DllImport("User32.dll")]
    public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
}

(обратите внимание на строчные буквы u)

[DllImport("user32.dll")]
    public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
}

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .