How can I force the monitor into power saving mode immediately in C#?
Asked Answered
M

1

7

I have two monitors (HP EliteDisplay E190i), both are connected to two computers (2x VGA + DP).
These monitors also support DVI, so instead of having a stupid toggle button, every time I wish to change between computers I have to navigate through the monitors' menu. I used to have dumber monitors and switching was really easy, but I just can't get used to the whole navigation thing - it often gets confusing...

So here's the deal - I want to be able to quickly switch between the computers by executing a command. Obviously this cannot be done directly (the computers are not connected to each other in any way), but when the monitors enter power saving mode (or when the OS turns them off), the monitors start scanning for available inputs. This way they would lock into the other computer and problem solved.

Enough introduction though, I've tried this solution and it worked great, but it wasn't perfect:

  • It had a fade out animation which took a couple seconds before the monitor actually turned off
  • I had to not touch the mouse/keyboard for the duration of the above fade out animation, otherwise it would get canceled

I tried disabling the input according to this answer before sending the monitor to sleep, then re-enabling it after 5 seconds, but that also didn't work because:

  • It required me to run the application with admin rights, otherwise input would not be blocked
  • Even though the input was blocked when running with admin rights, I could still move the mouse or hit some keys on the keyboard during the fade out animation to cancel it (even though the pointer did not move, or the keyboard input was ignored).

Here's my code:

[DllImport("user32.dll")]
private static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);

[DllImport("user32.dll")]
private static extern int BlockInput(int fBlockIt);

static void Main()
{
    SendMessage(0xFFFF, 0x112, 0xF170, 2);
    try
    {
        int result = BlockInput(1);
        Console.WriteLine(result);
        Thread.Sleep(5000);
    }
    finally
    {
        BlockInput(0);
    }
}

I use Windows 7 Enterprise x64 on both computers.

Is there any way to get this whole ceremony to work?

Mayhem answered 8/3, 2016 at 20:22 Comment(6)
So the problem with the second solution was that you were not able to run it with admin rights?Serinaserine
No, the input was blocked when I ran it with admin rights, as expected. However, even though the input was blocked, moving the mouse or pressing a key on my keyboard (physically) canceled the fade out animation - even though the pointer did not move and the keyboard input was ignored (keys were pressed, but no text was typed into notepad).Mayhem
how about blocking the input before sending the sleep commandSchiller
@RomanCoo Did not work either.Mayhem
Thought about simply buying a hardware KVM switch?Vintager
@Vintager Yes, but it is not allowed nor budgeted by my organization at this timeMayhem
F
0

I've had to edit my answer because stupidly I didn't read your question in it's entirety.

I would suggest creating an application that sits on both computers that send requests to each other via a simple TCP Server/Client socket.

This would allow you to, for example, press a button on PC 1 that will put it to sleep and do the same for the monitors, and send a message to PC 2 to wake up and steal the monitor inputs. In terms of the speed of this, I guess that depends on several variables, but this can be worked on at a later time.

TCP Client/Server:

using System;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.IO;

namespace ClientSocket_System
  {
   class tcpClientSocket
   {

    #region Global Variables

    TcpClient tcpService;
    IPEndPoint serverEndPoint;
    NetworkStream netStream;

    #endregion


    public void commToServer()
    {
        tcpService = new TcpClient();
        serverEndPoint = new IPEndPoint(IPAddress.Parse("xxx.xxx.xxx.xx"), xxxx); //Enter IP address of computer here along with a port number if needed
        try
        {
            tcpService.Connect(serverEndPoint);
            netStream = tcpService.GetStream();
            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] buffer = encoder.GetBytes("SwitchComputers");
            netStream.Write(buffer, 0, buffer.Length);
            netStream.Flush();
            tcpService.Close();
        }
        catch(Exception ex)
        {
        }
    }
   }
}

And the Server:

using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.IO;

namespace ClientSocket_System
{
    class tcpServerTerminal
    {



        private TcpListener tcpListener;
        private Thread listenThread;
        private TcpClient tcpService;
        string msgFromClient;



        public void ServerStart()
        {
            tcpListener = new TcpListener(IPAddress.Any, 5565);
            listenThread = new Thread(new ThreadStart(ListenForClients));
            listenThread.Start();
        }

        public void ListenForClients()
        {
            tcpListener.Start();

            while (true)
            {
                //blocks until a client has connected to the server
                TcpClient client = this.tcpListener.AcceptTcpClient();

                //create a thread to handle communication 
                //with connected client
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
                clientThread.Start(client);
            }
        }

        public void HandleClientComm(object client)
        {
            tcpService = (TcpClient)client;

            NetworkStream netStream = tcpService.GetStream();

            byte[] message = new byte[4096];
            int bytesRead;

            while (true)
            {
                bytesRead = 0;

                try
                {
                    //blocks until a client sends a message
                    bytesRead = netStream.Read(message, 0, 4096);
                }
                catch
                {
                    //a socket error has occured
                    break;
                }

                if (bytesRead == 0)
                {
                    //the client has disconnected from the server
                    break;
                }

                //message has successfully been received
                ASCIIEncoding encoder = new ASCIIEncoding();

                msgFromClient = encoder.GetString(message, 0, bytesRead);

                if (msgFromClient == "SwitchComputers")
                {

                    //RUN CODE HERE TO ACTION PC SLEEP AND MONITOR SLEEP

                    msgFromClient = null;
                }
            }
        }

        public void SocketSend()
        {
            NetworkStream streamToClient = tcpService.GetStream();
            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] buffer = encoder.GetBytes("SwitchComputers");

            streamToClient.Write(buffer, 0, buffer.Length);
            streamToClient.Flush();
        }

    }
}

Something like that perhaps, worth looking into at the very least, the code above isn't exactly refined but it would allow you to control the actions of both computers via your home network allowing for specific commands to be performed concurrently: Sleep/Wake etc.

Hope this gives you a new direction to investigate.

Also I believe it is best practice for your code for blocking input to be formatted as such:

[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
public static extern bool BlockInput([In, MarshalAs(UnmanagedType.Bool)] bool fBlockIt);
Familial answered 16/3, 2016 at 12:0 Comment(2)
This won't do. The computers are not connected to each other in any way (also mentioned in my question). Besides, I'm mainly having trouble with the RUN CODE HERE TO ACTION PC SLEEP AND MONITOR SLEEP part, and not the communication between the computers.Mayhem
Apologies I made the assumption that they would both at least be connected to a router or modem inside the house. The reason that I suggested it was primarily because when one computer is asleep and another wakes up it automatically pushes data to the visual pins which is what steals display, so it would have been an interesting way to have solved your problem. That being said however when I get back home I'll submit some of the code that I use to stop the visual signal from my computer to my monitors and we can see if it works better than your current solution.Familial

© 2022 - 2024 — McMap. All rights reserved.