Is it programmatically possible to turn a monitor on/off through code (C#)?
Did you even try googling it?
First hit:
http://www.codeproject.com/KB/cs/Monitor_management_guide.aspx
I am not surprised you need to use some DLL's supplied by Windows.
(I guessed you needed a C# solution, because that's the only tag you applied).
EDIT February 8th 2013:
It was mentioned that the solution no longer worked under Windows 7 en 8. Well here is one that works nicely under Windows 7, haven't tried Windows 8 yet.
http://cocoa.ninja/posts/Turn-off-your-monitor-in-Csharp.html
namespace MonitorOff {
public enum MonitorState {
MonitorStateOn = -1,
MonitorStateOff = 2,
MonitorStateStandBy = 1
}
public partial class Form1 : Form {
[DllImport("user32.dll")]
private static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);
public Form1() {
InitializeComponent();
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
}
void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e) {
SetMonitorInState(MonitorState.MonitorStateOff);
}
private void button1_Click(object sender, EventArgs e) {
SetMonitorInState(MonitorState.MonitorStateOff);
}
private void SetMonitorInState(MonitorState state) {
SendMessage(0xFFFF, 0x112, 0xF170, (int)state);
}
}
}
The answer https://stackoverflow.com/a/713504/636189 above works great for turning off a Windows 7/8 monitor but not for waking it up. On those systems you'll need to do something hackish like this (as found https://stackoverflow.com/a/14171736/636189):
[DllImport("user32.dll")]
static extern void mouse_event(Int32 dwFlags, Int32 dx, Int32 dy, Int32 dwData, UIntPtr dwExtraInfo);
private const int MOUSEEVENTF_MOVE = 0x0001;
private void Wake(){
mouse_event(MOUSEEVENTF_MOVE, 0, 1, 0, UIntPtr.Zero);
Sleep(40);
mouse_event(MOUSEEVENTF_MOVE, 0, -1, 0, UIntPtr.Zero);
}
Press the on/off button
If you want to do it in code, apparently this is possible in the Win32 API:
SendMessage hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, param
where WM_SYSCOMMAND = 0x112 and
SC_MONITORPOWER = 0xF170 and
param indicates the mode to put the monitor in:
-1 : on
2 : off
1 : energy saving mode
hWnd can be a handle for any window - so if you have a Form, something like this should work
int WM_SYSCOMMAND = 0x112;
int SC_MONITORPOWER = 0xF170;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
public static void Main(string[] args)
{
Form f = new Form();
bool turnOff = true; //set true if you want to turn off, false if on
SendMessage(f.Handle, WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)(turnOff ? 2 : -1));
}
Note I haven't actually tried this...
For who wants this functionality on a console application:
using System;
using System.Runtime.InteropServices;
using System.Timers;
namespace TurnScreenOFF
{
class Program
{
private static int WM_SYSCOMMAND = 0x0112;
private static uint SC_MONITORPOWER = 0xF170;
public static void Main(string[] args)
{
SendMessage(GetConsoleWindow(), WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)2);
}
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
}
}
Adaptated and tested. 100% working on Windows 8.
This code can be useful for turning on and turning off.. It worked in Windows 7 also.
private int SC_MONITORPOWER = 0xF170;
private uint WM_SYSCOMMAND = 0x0112;
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
enum MonitorState
{
ON = -1,
OFF = 2,
STANDBY = 1
}
private void SetMonitorState(MonitorState state)
{
Form frm = new Form();
SendMessage(frm.Handle, WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)state);
}
For calling the function you must do like:
SetMonitorState(MonitorState.ON);
OR
SetMonitorState(MonitorState.OFF);
Note: This code tested in WPF Application. With the below namespaces:
using System.Runtime.InteropServices;
using System.Windows.Forms;
I could not find a copy paste example, so created one myself, dont forget to add a reference to System.Windows.Forms.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace monitor_on_off
{
class Program
{
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern void mouse_event(Int32 dwFlags, Int32 dx, Int32 dy, Int32 dwData, UIntPtr dwExtraInfo);
private const int WmSyscommand = 0x0112;
private const int ScMonitorpower = 0xF170;
private const int MonitorShutoff = 2;
private const int MouseeventfMove = 0x0001;
public static void MonitorOff(IntPtr handle)
{
SendMessage(handle, WmSyscommand, (IntPtr)ScMonitorpower, (IntPtr)MonitorShutoff);
}
private static void MonitorOn()
{
mouse_event(MouseeventfMove, 0, 1, 0, UIntPtr.Zero);
Thread.Sleep(40);
mouse_event(MouseeventfMove, 0, -1, 0, UIntPtr.Zero);
}
static void Main()
{
var form = new Form();
while (true)
{
MonitorOff(form.Handle);
Thread.Sleep(5000);
MonitorOn();
Thread.Sleep(5000);
}
}
}
}
I have gone through every single method that everyone has published for putting a monitor to sleep and waking it later at some other time. Granted the SendMessage() does work with Windows XP but it doesn't wake the monitor after the monitor has been a sleep for a period of time. I have tried using C#, DOS, scripts for playing with power profiles, and Powershell. Eventually I gave up and went back to the beginning and my first thought was proven correct. You need to use the PostMessage() after the monitor has been turned off, better yet, you should probably always use PostMessage();
So all the code that you have seen before is correct, instead use the following:
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
static extern IntPtr PostMessage(int hWnd, int msg, int wParam, int lParam);
PostMessage(-1, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_OFF);
At this time of execution and working appropriately (May 11, 2015) I am running
Windows 7 Professional Version 6.1.7601 Service Pack 1 Build 7601
Visual Studio Profesional 2013 Version 12.0.31101.00 Update 4
.NET Framework 4.5.51209
C#
My system is completely up to date.
The answer with the least SLOC:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
static class Program
{
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[STAThread]
static void Main()
{
SendMessage(new Form().Handle, 0x0112, 0xF170, 2);
}
}
For Windows 10 (tested on Pro 64 bits), I was able to turn off the monitor using the SendMessage() technique mentioned in this page.
However, impossible for me to turn the monitor back on: the "mouse move" trick did not work, the SendMessage() technique would turn the screen back on for one second then back off and using PostMessage() did not change anything.
But the trick is in fact really simple, all I had to do was simulate a keypress with SendKeys(). I'm using ALT here because in itself it has no impact on the system but it could be any other key.
SendKeys.SendWait("%");
If you're not using Windows.Forms, sending "ALT" also works using SendInput() but it's longer to implement.
Related
I am wondering how can I restore application window to specific location on the screen.
I did managed to write some code, but somehow I do have a feeling that there must be and easier and more efficient way to do it programaticaly... as my current version is restoring window first and then moving it to required location. I would like to restore it without being forced to move it afterwards.
my code:
public void Send()
{
if (Process.GetProcessesByName("report").Any())
{
Process proc = Process.GetProcessesByName("report").First();
if (proc != null)
{
IntPtr pointer = proc.MainWindowHandle;
SetForegroundWindow(pointer);
SendMessage(pointer, WM_SYSCOMMAND, SC_RESTORE, 0);
Program.MoveWindow(pointer, 0, 0, 100, 100, true);
}
}
// do something
}
of course this is followed with :
[DllImport("user32.dll")]
static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
internal static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);
static Int32 WM_SYSCOMMAND = 0x0112;
static Int32 SC_RESTORE = 0xF120;
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
I would be very grateful for further solutions and explanation.
I'm trying to emulate some key events by using WinAPI. I want to press a WIN-key, but my code is not working. In example i use VK_F1 for every proc.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ConsoleApplication69
{
class Program
{
const UInt32 WM_KEYDOWN = 0x0100;
const UInt32 WM_KEYUP = 0x0101;
const int VK_F1 = 112;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static void Main(string[] args)
{
Process[] processes = Process.GetProcesses();
foreach (Process proc in processes)
{
SendMessage(proc.MainWindowHandle, WM_KEYDOWN, new IntPtr(VK_F1), IntPtr.Zero);
SendMessage(proc.MainWindowHandle, WM_KEYUP, new IntPtr(VK_F1), IntPtr.Zero);
}
}
}
}
To simulate keyboard input, use SendInput. That's exactly what this API does. "Sending F1 to every window" is not a good idea, because you'll be sending keystrokes to windows that don't have keyboard focus. Who knows how they'll respond.
There are a lot more nuances to keyboard input than you're accounting for; See this question for considerations about emulating keyboard input.
So I used code from this url: SendKeys.Send and Windows Key
And it works fine!
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ConsoleApplication69
{
class Program
{
static void Main(string[] args)
{
KeyboardSend.KeyDown(Keys.LWin);
KeyboardSend.KeyUp(Keys.LWin);
}
}
static class KeyboardSend
{
[DllImport("user32.dll")]
private static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
private const int KEYEVENTF_EXTENDEDKEY = 1;
private const int KEYEVENTF_KEYUP = 2;
public static void KeyDown(Keys vKey)
{
keybd_event((byte)vKey, 0, KEYEVENTF_EXTENDEDKEY, 0);
}
public static void KeyUp(Keys vKey)
{
keybd_event((byte)vKey, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
}
}
}
tnx for help everybody!
How can I get the list of opened of folders, enumerate through it and minimize each folder programmatically?
At times some opened folders do steal focus from the tool when jumping from one form in the application to another. Preventing this is of high priority for our client. The customers are visually impaired people, so they access the machine only via screen readers. Minimizing other windows (folders) is not at all a problem, in fact a requirement.
I tried this:
foreach (Process p in Process.GetProcessesByName("explorer"))
{
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
}
As expected it did no good.
Update:
From the answers here, I tried this:
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processID)
{
List<IntPtr> handles = new List<IntPtr>();
EnumThreadDelegate addWindowHandle = delegate(IntPtr hWnd, IntPtr param)
{
handles.Add(hWnd);
return true;
};
foreach (ProcessThread thread in Process.GetProcessById(processID).Threads)
EnumThreadWindows(thread.Id, addWindowHandle, IntPtr.Zero);
return handles;
}
const int SW_MINIMIZED = 6;
[DllImport("user32.dll")]
static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
private void button1_Click(object sender, EventArgs e)
{
foreach (IntPtr handle in EnumerateProcessWindowHandles(Process.GetProcessesByName("explorer")[0].Id))
ShowWindow(handle, SW_MINIMIZED);
}
This creates a whole lot of invisible explorer windows to be suddenly listed in the taksbar out of no where. I am bit noob in dealing with Windows API, so the code itself will actually help.
Please try this (the code is somewhat messy but for the purpose you should be able to go through it ;))
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Text;
using System.Globalization;
namespace WindowsFormsApplication20
{
static class Program
{
[STAThread]
static void Main()
{
foreach (IntPtr handle in EnumerateProcessWindowHandles(Process.GetProcessesByName("explorer")[0].Id))
{
SendMessage(handle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
}
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
static string GetDaClassName(IntPtr hWnd)
{
int nRet;
StringBuilder ClassName = new StringBuilder(100);
//Get the window class name
nRet = GetClassName(hWnd, ClassName, ClassName.Capacity);
if (nRet != 0)
{
return ClassName.ToString();
}
else
{
return null;
}
}
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processID)
{
List<IntPtr> handles = new List<IntPtr>();
EnumThreadDelegate addWindowHandle = delegate(IntPtr hWnd, IntPtr param)
{
string className = GetDaClassName(hWnd);
switch (className)
{
case null:
break;
case "ExploreWClass":
handles.Add(hWnd);
break;
case "CabinetWClass":
handles.Add(hWnd);
break;
default:
break;
}
return true;
};
foreach (ProcessThread thread in Process.GetProcessById(processID).Threads)
EnumThreadWindows(thread.Id, addWindowHandle, IntPtr.Zero);
return handles;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
const int WM_SYSCOMMAND = 274;
const int SC_MINIMIZE = 0xF020;
}
}
Best regards,
Żubrówka
//Create Instance Of Shell Class by referencing COM Library "Microsoft Shell Controls And Automation" -shell32.dll
Shell32.ShellClass objShell = new Shell32.ShellClass();
//Show Desktop
((Shell32.IShellDispatch4)objShell).ToggleDesktop();
Edit: to show your application (Activate or Maximize/Restore) after toggling actually turned out to be quite difficult:
I tried:
Application.DoEvents();
System.Threading.Thread.Sleep(5000);
Even overriding the WndProc didn't manage to capture the event:
private const Int32 WM_SYSCOMMAND = 0x112;
private const Int32 SC_MINIMIZE = 0xf020;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SYSCOMMAND)
{
if (m.WParam.ToInt32() == SC_MINIMIZE)
return;
}
base.WndProc(ref m);
}
So I suggest instead of Minimising all other windows, just stick yours on top during the operation, then once your finished turn off Always On Top:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
public static void MakeTopMost (IntPtr hWnd)
{
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
}
There is a less 'hacky' solution than the accepted answer available here: Minimize a folder
It's based on the Shell Objects for Scripting. Sample:
const int SW_SHOWMINNOACTIVE = 7;
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
static void MinimizeWindow(IntPtr handle)
{
ShowWindow(handle, SW_SHOWMINNOACTIVE);
}
//call it like:
foreach (IWebBrowser2 window in new Shell().Windows())
{
if (window.Name == "Windows Explorer")
MinimizeWindow((IntPtr)window.HWND);
}
The same thing can be achieved using the Internet Explorer Object Model
// add a reference to "Microsoft Internet Controls" COM component
// also add a 'using SHDocVw;'
foreach (IWebBrowser2 window in new ShellWindows())
{
if (window.Name == "Windows Explorer")
MinimizeWindow((IntPtr)window.HWND);
}
If you are willing to use p-invoke you can use EnumThreadWindows to enumerate all windows of a process. Then use ShowWindow to minimize them.
I know this is an old post, but here is a much shorter, simpler way in case people are still looking for a solution:
Using Windows API:
Declare a windows handle: (minimizes the calling executable)
HWND wHandle; //can be any scope - I use it in main
Call the following anywhere (depending on scope of wHandle):
wHandle = GetActiveWindow();
ShowWindow(wHandle, SW_SHOWMINNOACTIVE);
I am trying to override the window procedure for the winmobile taskbar (in order to catch and block pressed buttons) by using SetWindowLong. I have created a class with one method for overriding and one for restoring the window procedure. The MessageReceived method is the one I use to replace the taskbar window procedure with. My class looks the following way:
class ButtonBlocker
{
public delegate IntPtr WindowProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam);
public static WindowProc newWindowDeleg;
private static IntPtr oldWindowProc;
[DllImport("coredll.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("coredll.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("coredll.dll")]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("coredll.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
private static IntPtr MessageReceived(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam)
{
Debug.WriteLine("Message received");
return CallWindowProc(oldWindowProc, hwnd, uMsg, wParam, lParam);
}
public static void OverrideWindowProc()
{
newWindowDeleg = MessageReceived;
IntPtr taskBarHandle = FindWindow("HHTaskBar", null);
int newWndProc = Marshal.GetFunctionPointerForDelegate(newWindowDeleg).ToInt32();
int result = SetWindowLong(taskBarHandle, -4, newWndProc);
oldWindowProc = (IntPtr)result;
if (result == 0)
{
MessageBox.Show("Failed to SetWindowLong");
}
}
public static void RestoreWindowProc()
{
IntPtr taskBarHandle = FindWindow("HHTaskBar", null);
int result = SetWindowLong(taskBarHandle, -4, oldWindowProc.ToInt32());
}
}
The behavior in the mobile emulator is the following - after I press a button, "Message received" is displayed in the Debug output, but the program crashes and offers to send a crash report to Microsoft. The visual studio debugger hangs and doesn't react to the stop command. It unfreezes only after an emulator reset.
The problem seems to be with the CallWindowProc at the end of the MessageReceived method. Most probably, there is some issue with the old windowproc address.
If I try to execute the RestoreWindowProc code immediately after the OverrideWindowProc code (with no messages being intercepted by my function), the application exits okay and the debugger does not freeze.
Any ideas on how to get this to work would be appreciated.
I am using Visual Studio 2008 SP1. The project targets .NET framework v3.5, Windows Mobile 6 Professional.
You cannot replace the window procedure of a window that is owned by another process. The address of the replacement function is only valid in your process. It causes an immediate bomb in the other process. You would have to inject a DLL into the target process to work around this problem. You can't inject DLLs written in a managed language, the CLR isn't initialized.
I'm trying to use SendMessage to Notepad, so that I can insert written text without making Notepad the active window.
I have done something like this in the past using SendText, but that required giving Notepad focus.
Now, first I'm retrieving the Windows handle:
Process[] processes = Process.GetProcessesByName("notepad");
Console.WriteLine(processes[0].MainWindowHandle.ToString());
I've confirmed it's the right handle for Notepad, the same shown within Windows Task Manager.
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);
From here, I haven't been able to get SendMessage to work in all my experimentation. Am I going in the wrong direction?
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
private void button1_Click(object sender, EventArgs e)
{
Process [] notepads=Process.GetProcessesByName("notepad");
if(notepads.Length==0)return;
if (notepads[0] != null)
{
IntPtr child= FindWindowEx(notepads[0].MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessage(child, 0x000C, 0, textBox1.Text);
}
}
WM_SETTEXT=0x000c
You first have to find the child window where the text is entered. You can do this by finding the child window with the window class "Edit".
Once you have that window handle, use WM_GETTEXT to get the text which is already entered, then modify that text (e.g., add your own), then use WM_SETTEXT to send the modified text back.
using System.Diagnostics;
using System.Runtime.InteropServices;
static class Notepad
{
#region Imports
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
//this is a constant indicating the window that we want to send a text message
const int WM_SETTEXT = 0X000C;
#endregion
public static void SendText(string text)
{
Process notepad = Process.Start(#"notepad.exe");
System.Threading.Thread.Sleep(50);
IntPtr notepadTextbox = FindWindowEx(notepad.MainWindowHandle, IntPtr.Zero, "Edit", null);
SendMessage(notepadTextbox, WM_SETTEXT, 0, text);
}
}