“Extend my Windows desktop onto this monitor” programmatically

后端 未结 8 1042
不思量自难忘°
不思量自难忘° 2020-12-24 03:02

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 knowle

相关标签:
8条回答
  • 2020-12-24 03:16

    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.

    0 讨论(0)
  • 2020-12-24 03:21

    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.

    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.

    0 讨论(0)
  • 2020-12-24 03:22

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

    VB script (also described here but in Autoit language):

    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:

    ;
    ; — 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.
    ; 
    
    0 讨论(0)
  • 2020-12-24 03:27

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

    ;
    ; — 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}")
    
    0 讨论(0)
  • 2020-12-24 03:35

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

    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)
    

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

    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.]

    0 讨论(0)
  • 2020-12-24 03:38

    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.

    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".

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

    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.

    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"
    
    0 讨论(0)
提交回复
热议问题