&引用;“将我的Windows桌面扩展到此显示器”;以编程方式

&引用;“将我的Windows桌面扩展到此显示器”;以编程方式,windows,powershell,wmi,multiple-monitors,Windows,Powershell,Wmi,Multiple Monitors,我希望能够通过代码设置“将我的Windows桌面扩展到此监视器”。PowerShell脚本将是理想的。WMI似乎是前进的方向,但我对WMI一无所知。第一个可能的解决方案是。。。通过GUI(但无用户交互) (也可使用语言): 在Autoit中,这将是: ; ; — toggle-screen.au3 ; ; exec cpanel app `display settings` Run(”C:\WINDOWS\system32\control.exe desk.cpl,@0,3?”) ; wai

我希望能够通过代码设置“将我的Windows桌面扩展到此监视器”。PowerShell脚本将是理想的。WMI似乎是前进的方向,但我对WMI一无所知。

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

(也可使用语言):

在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.
; 

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

下一步是直接修改注册表。看起来该设置位于HKLM:\system\CurrentControlSet\control\video——某个地方。我相信这是一个叫“附加。ToDesktop”的

这是一个局部解决方案,所以我将其标记为社区维基答案

我不确定这是否是正确的注册表项,而且我目前没有一个可以测试多监视器的系统。其目的是确定哪个是主控制器,然后输出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"

为了让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}")
;
; — 切换屏幕2.au3
;
#包括
; exec cpanel应用程序`显示设置`
运行(_WinAPI_ExpandEnvironmentStrings(“%windir%”)和“\system32\control.exe desk.cpl,@0,3?”)
; 等待窗口处于活动状态
WinWaitActive(“显示属性”)
; 选择第二个显示
发送(“!d”)
发送(“{DOWN}”)
; 切换“扩展桌面”复选框
发送(“!e”)
; 关闭对话框
发送(“{ENTER}”)

我制作了一个更干净的版本,不使用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;
    }
}
公共类DisplayHelper
{
[DllImport(“user32.dll”)]
静态外部显示更改显示设置(uint lpDevMode、uint dwflags);
[DllImport(“user32.dll”)]
静态外部布尔枚举显示设备(字符串lpDevice、uint iDevNum、ref DISPLAY\u设备lpDisplayDevice、uint dwFlags);
枚举显示更改:int
{
成功=0,
重新启动=1,
失败=-1,
BadMode=-2,
NotUpdated=-3,
坏信号=-4,
BadParam=-5,
BadDualView=-1
}
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
结构显示设备
{
[Marshallas(UnmanagedType.U4)]
公共交通局;
[Marshallas(UnmanagedType.ByValTStr,SizeConst=32)]
公共字符串DeviceName;
[Marshallas(UnmanagedType.ByValTStr,SizeConst=128)]
公共字符串设备字符串;
[Marshallas(UnmanagedType.U4)]
公共显示设备状态标志状态标志;
[Marshallas(UnmanagedType.ByValTStr,SizeConst=128)]
公共字符串设备ID;
[Marshallas(UnmanagedType.ByValTStr,SizeConst=128)]
公共字符串设备密钥;
}
[标志()]
枚举显示设备状态标志:int
{
///该设备是桌面的一部分。
AttachedToDesktop=0x1,
多驱动器=0x2,
///该设备是桌面的一部分。
主设备=0x4,
///表示用于镜像应用程序图形以用于远程处理或其他目的的伪设备。
镜像驱动程序=0x8,
///该设备与VGA兼容。
VGA兼容=0x16,
///设备是可移动的;它不能作为主显示器。
可移动=0x20,
///该设备的显示模式多于其输出设备支持的模式。
ModesPruned=0x8000000,
远程=0x4000000,
断开连接=0x2000000
}
公共静态无效启用SecondaryDisplay()
{
var二级指数=1;
var secondary=GetDisplayDevice(secondaryIndex);
var id=secondary.DeviceKey.Split('\\')[7];
使用(var key=Registry.CurrentConfig.OpenSubKey(string.Format(@“System\CurrentControlSet\Control\VIDEO\{0}”,id),true))
{
使用(var subkey=key.CreateSubKey(“000”+secondaryIndex))
{
SetValue(“Attach.ToDesktop”,1,RegistryValueKind.DWord);
SetValue(“Attach.RelativeX”,1024,RegistryValueKind.DWord);
SetValue(“DefaultSettings.XResolution”,1024,RegistryValueKind.DWord);
SetValue(“DefaultSettings.YResolution”,768,RegistryValueKind.DWord);
SetValue(“DefaultSettings.BitsPerPel”,32,RegistryValueKind.DWord);
}
}
更改显示设置(0,0);
}
专用静态显示设备GetDisplayDevice(int id)
{
var d=新的显示设备();
d、 cb=元帅人数(d);
如果(!EnumDisplayDevices(null,(uint)id,ref d,0))
抛出新的NotSupportedException(“找不到id为“+id”的监视器);
返回d;
}
}

我只在一台新安装的计算机上测试过这个

这是我的AutoIt脚本,用于切换监视器,因为我的ATI图形卡不允许我同时激活3个监视器。我有两台显示器和一台电视。这个脚本正在做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
只需将“三星”替换为您的第三个显示器/电视名称,您就万事俱备了!
正如您肯定知道的,您可以将其转换为在任何机器上运行的可执行文件,即使没有安装AutoIt

Windows 7、8和10应该附带一个小程序,它可以做到这一点:displayswitch.exe。列出以下参数:

displayswitch.exe /internal Disconnect projector (same as "Show only on 1" from the Display Properties dialog)
displayswitch.exe /clone        Duplicate screen
displayswitch.exe /extend    Extend screen
displayswitch.exe /external Projector only (disconnect local) (same as "Show only on 2" from the Display Properties dialog)
对于提出的问题的单击式解决方案,只需创建一个包含单行的*.bat文件

call displayswitch.exe /extend
并将其保存到您的桌面

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

autohotkey中的两行

第二个d
call displayswitch.exe /extend
RunWait C:\Windows\System32\DisplaySwitch.exe /extend
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