none
Properly Turn Off Monitor Without Interrupting Apps RRS feed

  • Question

  • Hi there,

    I have developed a software to turn off the monitor display and the code I am using is:

    Option Explicit On
    Imports System.Runtime.InteropServices
    Imports Microsoft.Win32
    Public Class Form1
        Const HWND_BROADCAST As Integer = &HFFFF
        Const SC_MONITORPOWER As Integer = &HF170
        Const WM_SYSCOMMAND As Short = &H112S
        Const GWL_EXSTYLE As Integer = -20
        Const WS_EX_TOOLWINDOW As Integer = &H80
        Const WS_EX_APPWINDOW As Integer = &H40000
        <DllImport("user32.dll")>
        Private Shared Function SendMessage(ByVal hWnd As Integer, ByVal hMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
        End Function
    
        Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
        Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
        Public Const WM_CLOSE = &H10
    
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            On Error Resume Next
            SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, 2)
            End
        End Sub
    End Class

    However for most users the program is not functioning as expected. Although its working fine for me. Some users have reported their music stops as soon as they launch the program. For some, screen turns off for first time and afterwards it doesn't unless it's ended from Task Manager. Some users' system goes to sleep.

    This program doesn't have UI and ends as soon as started after executing the code.

    Is there some problem with code? or any alternative code.


    • Edited by Paras Sidhu Thursday, February 4, 2016 12:03 PM
    Thursday, February 4, 2016 11:59 AM

Answers

  •  The first thing you need to do is make sure you are using the correct Vb.Net signature for the SendMessage Api function as shown in my example below. The hWnd parameter needs to be an IntPtr type, not an Integer type.  Win32 Api functions need to use Integer or UInteger types instead of Long types,  long types where used in VB6 signatures.  You will only find a few Api functions here and there that require the use of a Long type.

     Also, make sure you have the constants declared properly, they should be Integer or UInteger types.  Usually Uinteger but, in some cases when the value of the constant is a negative value,  use an Integer.  There are some exceptions here and there where they will need to be a different type though.

     Some of the things mentioned above may very well have been a problem on some of the systems.  However, if you read the msdn documents on the WM_SYSCOMMAND message for the SC_MONITORPOWER flag you will see it says that it will only work for systems that support  power-saving features, such as a battery-powered personal computer.  So, there may be some systems that this may be a problem.

     I can only imagine that maybe some of the monitor`s settings on the computer may have been changed by the user and may effect this too.  That is something you will have to test/check.  You will also need to find out what apps are effected by this and test those out too.  There may very well be some apps that handle this message when it is sent to them and use it to stop the app from playing or doing something.

    Imports System.Runtime.InteropServices
    
    Public Class Form1
        Private Const HWND_BROADCAST As UInteger = &HFFFF
        Private Const SC_MONITORPOWER As UInteger = &HF170
        Private Const WM_SYSCOMMAND As UInteger = &H112
    
        <DllImport("user32.dll", EntryPoint:="SendMessageW")> _
        Private Shared Function SendMessageW(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As UInteger, ByVal lParam As Integer) As Integer
        End Function
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            SendMessageW(New IntPtr(HWND_BROADCAST), WM_SYSCOMMAND, SC_MONITORPOWER, 2)
        End Sub
    End Class


    If you say it can`t be done then i`ll try it

    • Edited by IronRazerz Friday, February 5, 2016 12:11 AM
    • Marked as answer by Paras Sidhu Saturday, February 6, 2016 1:09 PM
    Friday, February 5, 2016 12:07 AM

All replies

  • HWND_BROADCAST is attempting to send to all top level windows...who knows what's running...

    You need to find the monitor with GetDesktopWindow or FindWindow


    Cyrille Precetti <br/>

    Thursday, February 4, 2016 12:15 PM
  • Hi, Thanks for the response! Any code sample? Or what code change should I make? Not familiar with FindWindow or GetDesktopWindiw.
    Thursday, February 4, 2016 12:26 PM
  • try this:
    <dllimport setlasterror:="True)" user32.dll="user32.dll"> _
        Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
    
    Public Sub SwitchOffLCD()
            Dim num As Integer = 0
            num = SendMessage(FindWindow(Nothing, Nothing).ToInt32, Me.WM_SYSCOMMAND, Me.SC_MONITORPOWER, 2)
        End Sub


    Cyrille Precetti

    If it helped, mark it as answer or Vote


    Thursday, February 4, 2016 2:04 PM
  •  The first thing you need to do is make sure you are using the correct Vb.Net signature for the SendMessage Api function as shown in my example below. The hWnd parameter needs to be an IntPtr type, not an Integer type.  Win32 Api functions need to use Integer or UInteger types instead of Long types,  long types where used in VB6 signatures.  You will only find a few Api functions here and there that require the use of a Long type.

     Also, make sure you have the constants declared properly, they should be Integer or UInteger types.  Usually Uinteger but, in some cases when the value of the constant is a negative value,  use an Integer.  There are some exceptions here and there where they will need to be a different type though.

     Some of the things mentioned above may very well have been a problem on some of the systems.  However, if you read the msdn documents on the WM_SYSCOMMAND message for the SC_MONITORPOWER flag you will see it says that it will only work for systems that support  power-saving features, such as a battery-powered personal computer.  So, there may be some systems that this may be a problem.

     I can only imagine that maybe some of the monitor`s settings on the computer may have been changed by the user and may effect this too.  That is something you will have to test/check.  You will also need to find out what apps are effected by this and test those out too.  There may very well be some apps that handle this message when it is sent to them and use it to stop the app from playing or doing something.

    Imports System.Runtime.InteropServices
    
    Public Class Form1
        Private Const HWND_BROADCAST As UInteger = &HFFFF
        Private Const SC_MONITORPOWER As UInteger = &HF170
        Private Const WM_SYSCOMMAND As UInteger = &H112
    
        <DllImport("user32.dll", EntryPoint:="SendMessageW")> _
        Private Shared Function SendMessageW(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As UInteger, ByVal lParam As Integer) As Integer
        End Function
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            SendMessageW(New IntPtr(HWND_BROADCAST), WM_SYSCOMMAND, SC_MONITORPOWER, 2)
        End Sub
    End Class


    If you say it can`t be done then i`ll try it

    • Edited by IronRazerz Friday, February 5, 2016 12:11 AM
    • Marked as answer by Paras Sidhu Saturday, February 6, 2016 1:09 PM
    Friday, February 5, 2016 12:07 AM
  • Thanks everyone for the help. Will try the code in evening and will then reply. It's morning here.
    Friday, February 5, 2016 2:55 AM
  • So the code by IronRazers is working fine for me but I have sent it to a user for testing who was facing the problem. Let's see if good result is obtained.

    Code by Cyrille Précetti actually giving an error for "setlasterror". I think some typo there.

    Friday, February 5, 2016 3:08 PM
  • Yes it should be "True" and not "True)"...

    Cyrille Precetti <br/>

    Friday, February 5, 2016 5:13 PM
  • Unfortunately, IronRazers code not working for the tester. Maybe some problem on his end.
    Saturday, February 6, 2016 12:50 PM
  • Unfortunately, IronRazers code not working for the tester. Maybe some problem on his end.

     Yes,  as i have mentioned in my post,  there are a few different reasons that this may not work on some systems.

    If you say it can`t be done then i`ll try it

    Saturday, February 6, 2016 12:59 PM
  • Have you tried this (with corrected typo... :) )

    'Your form
    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            'Declare a new instance of SwithOff
            Dim mySwitchOff As New SwitchOffScreen
    
            'Here turn off screen
            Call mySwitchOff.SwitchOffLCD()
        End Sub
    
    End Class
    
    'The SwitchOff class
    Imports System.Runtime.InteropServices
    
    Class SwitchOffScreen
        Public SetLastError As Boolean
        Public WM_SYSCOMMAND As Integer = &H0112
        Public SC_MONITORPOWER As Integer = &HF170
    
        Private Const MonitorToLowPower As Integer = 1 'Apparently....
        Private Const MonitorShutoff As Integer = 2
    
        <DllImport("user32.dll", SetLastError:=True)>
        Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
        <DllImport("user32.dll", SetLastError:=True)>
        Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal wCmd As Integer,
            ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
        End Function
    
        Public Sub SwitchOffLCD()
            Dim num As Integer = 0
            num = SendMessage(FindWindow(Nothing, Nothing).ToInt32, Me.WM_SYSCOMMAND, Me.SC_MONITORPOWER, MonitorShutoff)
        End Sub
    
    End Class
    It feels a bit klutzy but it works fine.


    Cyrille Precetti

    Mark as answer if it works for you, and Vote :)
    Saturday, February 6, 2016 1:45 PM
  • @ Cyrille Précetti,

     Try turning Option Strict on.  You will see that the code is still not correct.  The .ToInt32 is converting the IntPtr returned by the FindWindow function to an Integer but,  the hWnd parameter of the SendMessage function is suppose to be an IntPtr type.

    FindWindow(Nothing, Nothing).ToInt32

     I am also not seeing what the SetLastError boolean variable is declared for.  SetLastError is used in the Api Signatures if you want to use the Marshal.GetLastWin32Error() method when the api function fails for some reason and you want to get the Win32 error code.

     You are also using an overload of the SendMessage Api that has the lParam and wParam declared as IntPtr types but,  you are passing Integer type constants to them.


    If you say it can`t be done then i`ll try it

    Saturday, February 6, 2016 2:29 PM
  • @cyrille Sure will give a try tomorrow. Just see if any correction is needed as suggested by IronRazers :) Thanks btw :)
    Saturday, February 6, 2016 2:48 PM
  • @cyrille Sure will give a try tomorrow. Just see if any correction is needed as suggested by IronRazers :) Thanks btw :)

     That would look like this with the corrections.

    Imports System.Runtime.InteropServices
    
    Public Class Form1
        Private Const SC_MONITORPOWER As UInteger = &HF170
        Private Const WM_SYSCOMMAND As UInteger = &H112
    
        <DllImport("user32.dll", EntryPoint:="SendMessageW")> _
        Private Shared Function SendMessageW(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As UInteger, ByVal lParam As Integer) As Integer
        End Function
    
        <DllImport("user32.dll", EntryPoint:="FindWindowW")> _
        Private Shared Function FindWindowW(<MarshalAs(UnmanagedType.LPTStr)> ByVal lpClassName As String, <MarshalAs(UnmanagedType.LPTStr)> ByVal lpWindowName As String) As IntPtr
        End Function
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            SendMessageW(FindWindowW(Nothing, Nothing), WM_SYSCOMMAND, SC_MONITORPOWER, 2)
        End Sub
    End Class
    


    If you say it can`t be done then i`ll try it

    Saturday, February 6, 2016 4:06 PM
  • So both (corrected and original) are working fine for me, again. Let me test on some other system. Thanks again both of you!
    Sunday, February 7, 2016 4:11 PM