【问题标题】:"Extend my Windows desktop onto this monitor" programmatically以编程方式“将我的 Windows 桌面扩展到此显示器上”
【发布时间】:2010-09-21 11:07:09
【问题描述】:

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

【问题讨论】:

    标签: windows powershell wmi multiple-monitors


    【解决方案1】:

    Windows 7、8 和 10 应该附带一个执行此操作的小程序:displayswitch.exe。 This page 列出以下参数:

    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 上运行。]

    【讨论】:

    • Powershell 不需要call
    • ? 应该是答案
    • 想知道如何将其插入板载 + 专用显卡。或者我猜你会遇到超过 3 个显示器的问题。
    • 在我的 Win10 版本中,这只是调出投影仪面板。它实际上并没有切换显示类型。我必须先运行 Powershell。这是我的批处理文件:powershell -Command "displayswitch.exe /clone"
    【解决方案2】:

    我制作了一个不使用 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;
        }
    }
    

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

    【讨论】:

    • VGACompatible 应该是0x10,而不是0x16
    【解决方案3】:

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

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

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

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

    【讨论】:

      【解决方案4】:

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

      VB script(也是described here,但使用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
      

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

      【讨论】:

      • 恶心!我知道有一些悲惨的案例,你只能做 GUI 机器人,但不知何故,我认为这不是其中之一。
      【解决方案5】:

      自动热键中的 2 行

      第二次显示:

      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
      

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

      【讨论】:

        【解决方案6】:

        这是我用于切换显示器的 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
        

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

        【讨论】:

          【解决方案7】:

          这是另一个解决方案,在 C# 中(通过 how to set primary monitor for Windows-7, in C#):

          [Flags]
          public enum SetDisplayConfigFlags : uint
          {
              SDC_TOPOLOGY_INTERNAL = 0x00000001,
              SDC_TOPOLOGY_CLONE = 0x00000002,
              SDC_TOPOLOGY_EXTEND = 0x00000004,
              SDC_TOPOLOGY_EXTERNAL = 0x00000008,
              SDC_APPLY = 0x00000080
          }
          
          [DllImport("user32.dll", CharSet = CharSet.Unicode)]
          private static extern long SetDisplayConfig(uint numPathArrayElements,
              IntPtr pathArray, uint numModeArrayElements, IntPtr modeArray, SetDisplayConfigFlags flags);
          
          static void CloneDisplays() {
              SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, SetDisplayConfigFlags.SDC_TOPOLOGY_CLONE | SetDisplayConfigFlags.SDC_APPLY);
          }
          
          static void ExtendDisplays() {
              SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, SetDisplayConfigFlags.SDC_TOPOLOGY_EXTEND | SetDisplayConfigFlags.SDC_APPLY);
          }
          
          static void ExternalDisplay() {
              SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, SetDisplayConfigFlags.SDC_TOPOLOGY_EXTERNAL | SetDisplayConfigFlags.SDC_APPLY);
          }
          
          static void InternalDisplay() {
              SetDisplayConfig(0, IntPtr.Zero, 0, IntPtr.Zero, SetDisplayConfigFlags.SDC_TOPOLOGY_INTERNAL | SetDisplayConfigFlags.SDC_APPLY);
          }
          

          【讨论】:

            【解决方案8】:

            我必须做一些小的修改才能让 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}")
            

            【讨论】:

              【解决方案9】:

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

              【讨论】:

              • 当然,但问题是在代码中做到这一点,而不是交互。
              猜你喜欢
              • 1970-01-01
              • 2011-09-14
              • 2016-12-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-01-14
              • 1970-01-01
              相关资源
              最近更新 更多