以编程方式“将我的 Windows 桌面扩展到这台显示器上”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/275063/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 05:41:11  来源:igfitidea点击:

"Extend my Windows desktop onto this monitor" programmatically

windowspowershellwmimultiple-monitors

提问by Zi Makki

I would like to be able to set "Extend my Windows desktop onto this monitor" via code. A PowerShell script would be ideal. WMI seems the way forward but I have zero knowledge in WMI.

我希望能够通过代码设置“将我的 Windows 桌面扩展到这台显示器上”。PowerShell 脚本将是理想的选择。WMI 似乎是前进的方向,但我对 WMI 的了解为零。

回答by Communicative Algebra

Windows 7, 8 and 10 are supposed to come with a small program that does exactly this: displayswitch.exe. This pagelists the following parameters:

Windows 7、8 和 10 应该带有一个可以执行此操作的小程序:displayswitch.exe。 此页面列出了以下参数:

displayswitch.exe/internal  Disconnect projector
displayswitch.exe/clone     Duplicate screen
displayswitch.exe/extend    Extend screen
displayswitch.exe/external  Projector only (disconnect local)

For a one-click solution to the problem posed, simply create a *.bat-file containing the single line

对于所提出问题的一键式解决方案,只需创建一个包含单行的 *.bat 文件

call displayswitch.exe/extend

and save it to your desktop.

并将其保存到您的桌面。

[I tested this on Windows 8.1, and it has been confirmed to work on Windows 10.]

[我在 Windows 8.1 上对此进行了测试,并已确认它可以在 Windows 10 上运行。]

回答by loraderon

I've made a cleaner version that does not use sendkeys.

我制作了一个不使用发送键的更干净的版本。

public class DisplayHelper
{
    [DllImport("user32.dll")]
    static extern DISP_CHANGE ChangeDisplaySettings(uint lpDevMode, uint dwflags);
    [DllImport("user32.dll")]
    static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);

    enum DISP_CHANGE : int
    {
        Successful = 0,
        Restart = 1,
        Failed = -1,
        BadMode = -2,
        NotUpdated = -3,
        BadFlags = -4,
        BadParam = -5,
        BadDualView = -1
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct DISPLAY_DEVICE
    {
        [MarshalAs(UnmanagedType.U4)]
        public int cb;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DeviceName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceString;
        [MarshalAs(UnmanagedType.U4)]
        public DisplayDeviceStateFlags StateFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceKey;
    }

    [Flags()]
    enum DisplayDeviceStateFlags : int
    {
        /// <summary>The device is part of the desktop.</summary>
        AttachedToDesktop = 0x1,
        MultiDriver = 0x2,
        /// <summary>The device is part of the desktop.</summary>
        PrimaryDevice = 0x4,
        /// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
        MirroringDriver = 0x8,
        /// <summary>The device is VGA compatible.</summary>
        VGACompatible = 0x16,
        /// <summary>The device is removable; it cannot be the primary display.</summary>
        Removable = 0x20,
        /// <summary>The device has more display modes than its output devices support.</summary>
        ModesPruned = 0x8000000,
        Remote = 0x4000000,
        Disconnect = 0x2000000
    }

    public static void EnableSecondaryDisplay()
    {
        var secondaryIndex = 1;
        var secondary = GetDisplayDevice(secondaryIndex);
        var id = secondary.DeviceKey.Split('\')[7];

        using (var key = Registry.CurrentConfig.OpenSubKey(string.Format(@"System\CurrentControlSet\Control\VIDEO\{0}", id), true))
        {
            using (var subkey = key.CreateSubKey("000" + secondaryIndex))
            {
                subkey.SetValue("Attach.ToDesktop", 1, RegistryValueKind.DWord);
                subkey.SetValue("Attach.RelativeX", 1024, RegistryValueKind.DWord);
                subkey.SetValue("DefaultSettings.XResolution", 1024, RegistryValueKind.DWord);
                subkey.SetValue("DefaultSettings.YResolution", 768, RegistryValueKind.DWord);
                subkey.SetValue("DefaultSettings.BitsPerPel", 32, RegistryValueKind.DWord);
            }
        }

        ChangeDisplaySettings(0, 0);
    }

    private static DISPLAY_DEVICE GetDisplayDevice(int id)
    {
        var d = new DISPLAY_DEVICE();
        d.cb = Marshal.SizeOf(d);
        if (!EnumDisplayDevices(null, (uint)id, ref d, 0))
            throw new NotSupportedException("Could not find a monitor with id " + id);
        return d;
    }
}

I have only tested this on a newly installed computer.

我只在新安装的计算机上对此进行了测试。

回答by halr9000

This sort of operation is not directly accessible from PowerShell in the sense that there is not a .NET interface to these settings. A lot of core OS stuff is unmanaged code which can only be manipulated via win32 API calls. While you may be on to something with WMI, I searched for a while and wasn't able to find a satisfactory WMI class which is able to manipulate this setting.

此类操作无法从 PowerShell 直接访问,因为这些设置没有 .NET 接口。许多核心操作系统的东西是非托管代码,只能通过 win32 API 调用来操作。虽然您可能对 WMI 有所了解,但我搜索了一段时间,但未能找到一个令人满意的 WMI 类,它能够操作此设置。

The next step would be to modify the registry directly. It looks like the setting lies under HKLM:\system\CurrentControlSet\control\video--somewhere. I believe it's the one called "Attach.ToDesktop".

下一步是直接修改注册表。看起来该设置位于 HKLM:\system\CurrentControlSet\control\video--某处。我相信它是一个叫做“Attach.ToDesktop”的。

This is a partial solution, so I'm marking as community wiki answer.

这是部分解决方案,因此我将其标记为社区 wiki 答案。

I'm not certain this is the right registry key, and I don't have a system on which I can test multi-monitor at the moment. The purpose of this is to determine which is the primary controller, and then it outputs the value of the Attach.ToDesktop key.

我不确定这是正确的注册表项,而且我目前没有可以测试多显示器的系统。这样做的目的是确定哪个是主控制器,然后输出 Attach.ToDesktop 键的值。

param ( 
    $ControllerName = "$( throw 'ControllerName is a mandatory parameter' )"
)
$regPath = "HKLM:\system\CurrentControlSet\control\video"
$devDescStr = "Device Description"

Set-Location -path $regPath
$regSubKey = Get-ChildItem -recurse -include 0000
$devDescProperty = $regSubKey | Get-ItemProperty -name $devDescStr -erroraction SilentlyContinue 
$priDescProperty = $devDescProperty | Where-Object { $_.$devDescStr -match $ControllerName }
Set-Location -path $priDescProperty.PSPath
Get-ItemProperty -path . -name "Attach.ToDesktop"

回答by VonC

One first possible solution is... through the GUI (but without user interaction)

第一个可能的解决方案是......通过GUI(但没有用户交互)

VB script(also described herebut in Autoitlanguage):

VB 脚本(也在此处描述,但使用Autoit语言):

Option Explicit
Dim WshShell, Dummy, Splash

On Error Resume Next

Set WshShell = WScript.CreateObject("WScript.Shell")

'Main
Call DoIt
WScript.Quit

Sub DoIt
wshshell.Run("%systemroot%\system32\control.exe desk.cpl,@0,3")

' Give Display Properties time to load
WScript.Sleep 1000
WshShell.SendKeys "2"
WScript.Sleep 10
WshShell.SendKeys "%E"
WScript.Sleep 500
WshShell.SendKeys "%A"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{ENTER}"
End Sub 'DoIt

In Autoit, that would be:

在 Autoit 中,这将是:

;
; — toggle-screen.au3
;

; exec cpanel app `display settings`
Run(”C:\WINDOWS\system32\control.exe desk.cpl,@0,3?”)

; wait for window to be active
WinWaitActive(”Display Settings”)

; select 2nd display
Send(”{TAB}”)
Send(”{DOWN}”)

; work back to the ‘extend desktop' control
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)
Send(”+{TAB}”)

; toggle ‘extend desktop' control and apply
Send(”{SPACE}”)
Send(”{ENTER}”)

; wait for window to be active
WinWaitActive(”Display Settings”)

; accept
Send(”{TAB}”)
Send(”{ENTER}”)

;
; — E.O.F.
; 

回答by MemphiZ

Here is my AutoIt-Script for switching monitors as my ATI graphics card doesn't allow me to have 3 monitors active at the same time. I have 2 monitors attached and a TV. This script is doing what VonC's script does but in a more effective and faster way.

这是我用于切换显示器的 AutoIt 脚本,因为我的 ATI 显卡不允许我同时使用 3 个显示器。我连接了 2 台显示器和一台电视。此脚本正在执行 VonC 的脚本,但以更有效和更快的方式执行。

Run("C:\WINDOWS\system32\control.exe desk.cpl", "C:\Windows\system32\")
WinWait("Screen Resolution")
ControlCommand("Screen Resolution", "", "ComboBox1", "SetCurrentSelection", "SAMSUNG")

if (ControlCommand("Screen Resolution", "", "ComboBox3", "GetCurrentSelection", "") = "Disconnect this display") Then
    ControlCommand("Screen Resolution", "", "ComboBox1", "SetCurrentSelection", "2")
    ControlCommand("Screen Resolution", "", "ComboBox3", "SetCurrentSelection", "3")
    ControlCommand("Screen Resolution", "", "ComboBox1", "SetCurrentSelection", "0")
    ControlCommand("Screen Resolution", "", "ComboBox3", "SetCurrentSelection", "1")
    ControlClick("Screen Resolution", "", "Button4")
    WinWait("Display Settings")
    ControlClick("Display Settings", "", "Button1")
Else
    ControlCommand("Screen Resolution", "", "ComboBox3", "SetCurrentSelection", "3")
    ControlCommand("Screen Resolution", "", "ComboBox1", "SetCurrentSelection", "2")
    ControlCommand("Screen Resolution", "", "ComboBox3", "SetCurrentSelection", "1")
    ControlClick("Screen Resolution", "", "Button4")
    WinWait("Display Settings")
    ControlClick("Display Settings", "", "Button1")
EndIf

Just replace "SAMSUNG" with your third monitors/tvs name and you're all set! As you surely know you can convert it to an executable which runs on any machine even without AutoIt installed.

只需将“SAMSUNG”替换为您的第三台显示器/电视名称即可!您肯定知道,即使没有安装 AutoIt,您也可以将其转换为可在任何机器上运行的可执行文件。

回答by Bong Gutz

2 lines in autohotkey

自动热键中的 2 行

2nd display on:

第二次显示:

RunWait C:\Windows\System32\DisplaySwitch.exe /extend

2nd display off:

2 显示关闭:

RunWait C:\Windows\System32\DisplaySwitch.exe /internal

-

——

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
#Persistent 

Any1stKeyUWantToTurnOn::RunWait C:\Windows\System32\DisplaySwitch.exe /extend
Any2stKeyUWantToTurnOff::RunWait C:\Windows\System32\DisplaySwitch.exe /internal

or

或者

You can check and try out my tool on github / BNK3R-Boy / DisplaySwitch. I published it right now.

您可以在github / BNK3R-Boy / DisplaySwitch上查看并试用我的工具。我马上发布了。

回答by David Resnick

I had to made some small modifications to get VonC's script to work on my machine. It is now a little more generic.

我必须做一些小的修改才能让 VonC 的脚本在我的机器上工作。它现在更通用一些。

;
; — toggle-screen2.au3
;

#include <WinAPI.au3>
; exec cpanel app `display settings`
Run(_WinAPI_ExpandEnvironmentStrings("%windir%") & "\system32\control.exe desk.cpl,@0,3?")

; wait for window to be active
WinWaitActive("Display Properties")

; select 2nd display
Send("!d")
Send("{DOWN}")

; toggle the ‘extend desktop' checkbox
Send("!e")

; close the dialog
Send("{ENTER}")

回答by user3237231

windows key + P button will do the same thing

Windows 键 + P 按钮会做同样的事情