Brad
Brad

Reputation: 163518

Detect laptop lid closure and opening

Is it possible to detect when a laptop's lid is open or closed? From what I've read, this isn't possible, but SO has helped me with the impossible before.

The only thing I've found that might be in the right direction is an MSDN blog post about IOCTLs needed to report power buttons. Is it possible to "sniff" these as the OS calls them?

I'm using VB.NET, but will take suggestions in any language. Thank you for your time and advice.

Edit: My software will be (eventually) overriding the actions (based on user preference) that occur when the lid is closed, so listening for suspend and other actions that typically occur when the lid is closed isn't an option.

Upvotes: 29

Views: 16999

Answers (6)

RicardoBalda
RicardoBalda

Reputation: 1747

To detect when portable lid it is closed or opened.

For Windows Framework Net 4.8.

Code in C#

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;

    namespace DetectLid
    {
        public partial class Form1 : Form
        {

            [DllImport("User32.dll", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification",
        CallingConvention = CallingConvention.StdCall)]
            private static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid,
        int Flags);

            internal struct POWERBROADCAST_SETTING
            {
                public Guid PowerSetting;
                public uint DataLength;
                public byte Data;
            }

            Guid GUID_LIDSWITCH_STATE_CHANGE = new Guid(0xBA3E0F4D, 0xB817, 0x4094, 0xA2, 0xD1, 0xD5, 0x63, 0x79, 0xE6, 0xA0, 0xF3);
            const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
            const int WM_POWERBROADCAST = 0x0218;
            const int PBT_POWERSETTINGCHANGE = 0x8013;

            private bool? _previousLidState = null;

            public Form1()
            {
                InitializeComponent();
                this.Load += Form1_Load;
            }

            private void Form1_Load(object sender, EventArgs e)
            {
                RegisterForPowerNotifications();
            }

            private void RegisterForPowerNotifications()
            {
                IntPtr handle = this.Handle;
                IntPtr hLIDSWITCHSTATECHANGE = RegisterPowerSettingNotification(handle,
                     ref GUID_LIDSWITCH_STATE_CHANGE,
                     DEVICE_NOTIFY_WINDOW_HANDLE);
            }

            protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_POWERBROADCAST:
                        OnPowerBroadcast(m.WParam, m.LParam);
                        break;
                    default:
                        base.WndProc(ref m);
                        break;
                }
            }

            private void OnPowerBroadcast(IntPtr wParam, IntPtr lParam)
            {
                if ((int)wParam == PBT_POWERSETTINGCHANGE)
                {
                    POWERBROADCAST_SETTING ps = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(lParam, typeof(POWERBROADCAST_SETTING));
                    IntPtr pData = (IntPtr)((long)lParam + Marshal.SizeOf(ps));
                    Int32 iData = (Int32)Marshal.PtrToStructure(pData, typeof(Int32));
                    if (ps.PowerSetting == GUID_LIDSWITCH_STATE_CHANGE)
                    {
                        bool isLidOpen = ps.Data != 0;

                        if (!isLidOpen == _previousLidState)
                        {
                            LidStatusChanged(isLidOpen);
                        }

                        _previousLidState = isLidOpen;
                    }
                }
            }

            private void LidStatusChanged(bool isLidOpen)
            {
                if (isLidOpen)
                {
                    //Do some action on lid open event
                    Debug.WriteLine("{0}: Lid opened!", DateTime.Now);
                }
                else
                {
                    //Do some action on lid close event
                    Debug.WriteLine("{0}: Lid closed!", DateTime.Now);
                }
            }
        }
    }

Code in VB.Net:

    Imports System.Runtime.InteropServices

    Public Class Form2

        <DllImport("User32.dll", SetLastError:=True, EntryPoint:="RegisterPowerSettingNotification", CallingConvention:=CallingConvention.StdCall)>
        Private Shared Function RegisterPowerSettingNotification(hRecipient As IntPtr, ByRef PowerSettingGuid As Guid, Flags As Integer) As IntPtr
        End Function

        <StructLayout(LayoutKind.Sequential)>
        Public Structure POWERBROADCAST_SETTING
            Public PowerSetting As Guid
            Public DataLength As UInteger
            Public Data As Byte
        End Structure

        Private GUID_LIDSWITCH_STATE_CHANGE As New Guid("BA3E0F4D-B817-4094-A2D1-D56379E6A0F3")
        Private Const DEVICE_NOTIFY_WINDOW_HANDLE As Integer = &H0
        Private Const WM_POWERBROADCAST As Integer = &H218
        Private Const PBT_POWERSETTINGCHANGE As Integer = &H8013

        Private _previousLidState As Nullable(Of Boolean) = Nothing

        Public Sub New()
            InitializeComponent()
            AddHandler Me.Load, AddressOf Form2_Load
        End Sub


        Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            RegisterForPowerNotifications()

        End Sub

        Private Sub RegisterForPowerNotifications()
            Dim handle As IntPtr = Me.Handle
            Dim hLIDSWITCHSTATECHANGE As IntPtr = RegisterPowerSettingNotification(handle, GUID_LIDSWITCH_STATE_CHANGE, DEVICE_NOTIFY_WINDOW_HANDLE)
        End Sub

        Protected Overrides Sub WndProc(ByRef m As Message)
            Select Case m.Msg
                Case WM_POWERBROADCAST
                    OnPowerBroadcast(m.WParam, m.LParam)
                Case Else
                    MyBase.WndProc(m)
            End Select
        End Sub

        Private Sub OnPowerBroadcast(wParam As IntPtr, lParam As IntPtr)
            If CInt(wParam) = PBT_POWERSETTINGCHANGE Then
                Dim ps As POWERBROADCAST_SETTING = CType(Marshal.PtrToStructure(lParam, GetType(POWERBROADCAST_SETTING)), POWERBROADCAST_SETTING)
                Dim pData As IntPtr = CType(CLng(lParam) + Marshal.SizeOf(ps), IntPtr)
                Dim iData As Int32 = CInt(Marshal.PtrToStructure(pData, GetType(Int32)))
                If ps.PowerSetting = GUID_LIDSWITCH_STATE_CHANGE Then
                    Dim isLidOpen As Boolean = ps.Data <> 0

                    If Not isLidOpen = _previousLidState Then
                        LidStatusChanged(isLidOpen)
                    End If

                    _previousLidState = isLidOpen
                End If
            End If
        End Sub

        Private Sub LidStatusChanged(isLidOpen As Boolean)
            If isLidOpen Then
                'Do some action on lid open event
                Debug.WriteLine("{0}: Lid opened!", DateTime.Now)
            Else
                'Do some action on lid close event
                Debug.WriteLine("{0}: Lid closed!", DateTime.Now)
            End If
        End Sub

    End Class

Upvotes: 1

Icemanind
Icemanind

Reputation: 48726

Keep in mind that most laptops, when the lid closes, it depresses a button. This button is usually just a sleep button. The WMI classes expose the ACPI and you would ideally want to use the PowerManagement Class. Unfortunately, the class does not raise an event when the operating system is set to "do nothing". The only way around this would be to use the DDK (Driver Development Kit) to create a filter that intercepts the IOCTL_GET_SYS_BUTTON_EVENT event. Here are two links to help you get started:

Link

and

http://support.microsoft.com/kb/302092

Upvotes: 5

Fatih
Fatih

Reputation: 11

Here's a solution;

https://www.codeproject.com/Tips/480049/Shut-Down-Restart-Log-off-or-Lock-your-computer-in

and i did;

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        [DllImport(@"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification",
            CallingConvention = CallingConvention.StdCall)]

        private static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid,
            Int32 Flags);

        internal struct POWERBROADCAST_SETTING
        {
            public Guid PowerSetting;
            public uint DataLength;
            public byte Data;
        }

        Guid GUID_LIDSWITCH_STATE_CHANGE = new Guid(0xBA3E0F4D, 0xB817, 0x4094, 0xA2, 0xD1, 0xD5, 0x63, 0x79, 0xE6, 0xA0, 0xF3);
        const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
        const int WM_POWERBROADCAST = 0x0218;
        const int PBT_POWERSETTINGCHANGE = 0x8013;

        private bool? _previousLidState = null;

        public MainWindow()
        {
            InitializeComponent();
            this.SourceInitialized += MainWindow_SourceInitialized;
        }

        void MainWindow_SourceInitialized(object sender, EventArgs e)
        {
            RegisterForPowerNotifications();
            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            HwndSource.FromHwnd(hwnd).AddHook(new HwndSourceHook(WndProc));
        }

        private void RegisterForPowerNotifications()
        {
            IntPtr handle = new WindowInteropHelper(Application.Current.Windows[0]).Handle;
            IntPtr hLIDSWITCHSTATECHANGE = RegisterPowerSettingNotification(handle,
                 ref GUID_LIDSWITCH_STATE_CHANGE,
                 DEVICE_NOTIFY_WINDOW_HANDLE);
        }

        IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch (msg)
            {
                case WM_POWERBROADCAST:
                    OnPowerBroadcast(wParam, lParam);
                    break;
                default:
                    break;
            }
            return IntPtr.Zero;
        }

        private void OnPowerBroadcast(IntPtr wParam, IntPtr lParam)
        {
            if ((int)wParam == PBT_POWERSETTINGCHANGE)
            {
                POWERBROADCAST_SETTING ps = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(lParam, typeof(POWERBROADCAST_SETTING));
                IntPtr pData = (IntPtr)((int)lParam + Marshal.SizeOf(ps));
                Int32 iData = (Int32)Marshal.PtrToStructure(pData, typeof(Int32));
                if (ps.PowerSetting == GUID_LIDSWITCH_STATE_CHANGE)
                {
                    bool isLidOpen = ps.Data != 0;

                    if (!isLidOpen == _previousLidState)
                    {
                        LidStatusChanged(isLidOpen);
                    }

                    _previousLidState = isLidOpen;
                }
            }
        }

        private void LidStatusChanged(bool isLidOpen)
        {
            if (isLidOpen)
            {
                //Do some action on lid open event
                Debug.WriteLine("{0}: Lid opened!", DateTime.Now);
            }
            else
            {
                //Do some action on lid close event
                Debug.WriteLine("{0}: Lid closed!", DateTime.Now);
            }
        }
    }
}

it's locking screen when you close the lid.

Upvotes: 1

broslav
broslav

Reputation: 615

Complete working C# code for WPF application that shows how to listen to lid open/close events:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        [DllImport(@"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification",
            CallingConvention = CallingConvention.StdCall)]

        private static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid,
            Int32 Flags);

        internal struct POWERBROADCAST_SETTING
        {
            public Guid PowerSetting;
            public uint DataLength;
            public byte Data;
        }

        Guid GUID_LIDSWITCH_STATE_CHANGE = new Guid(0xBA3E0F4D, 0xB817, 0x4094, 0xA2, 0xD1, 0xD5, 0x63, 0x79, 0xE6, 0xA0, 0xF3);
        const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
        const int WM_POWERBROADCAST = 0x0218;
        const int PBT_POWERSETTINGCHANGE = 0x8013;

        private bool? _previousLidState = null;

        public MainWindow()
        {
            InitializeComponent();
            this.SourceInitialized += MainWindow_SourceInitialized;
        }

        void MainWindow_SourceInitialized(object sender, EventArgs e)
        {
            RegisterForPowerNotifications();
            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            HwndSource.FromHwnd(hwnd).AddHook(new HwndSourceHook(WndProc));
        }

        private void RegisterForPowerNotifications()
        {
            IntPtr handle = new WindowInteropHelper(Application.Current.Windows[0]).Handle;
            IntPtr hLIDSWITCHSTATECHANGE = RegisterPowerSettingNotification(handle,
                 ref GUID_LIDSWITCH_STATE_CHANGE,
                 DEVICE_NOTIFY_WINDOW_HANDLE);
        }

        IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch (msg)
            {
                case WM_POWERBROADCAST:
                    OnPowerBroadcast(wParam, lParam);
                    break;
                default:
                    break;
            }
            return IntPtr.Zero;
        }

        private void OnPowerBroadcast(IntPtr wParam, IntPtr lParam)
        {
            if ((int)wParam == PBT_POWERSETTINGCHANGE)
            {
                POWERBROADCAST_SETTING ps = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(lParam, typeof(POWERBROADCAST_SETTING));
                IntPtr pData = (IntPtr)((int)lParam + Marshal.SizeOf(ps));
                Int32 iData = (Int32)Marshal.PtrToStructure(pData, typeof(Int32));
                if (ps.PowerSetting == GUID_LIDSWITCH_STATE_CHANGE)
                {
                    bool isLidOpen = ps.Data != 0;

                    if (!isLidOpen == _previousLidState)
                    {
                        LidStatusChanged(isLidOpen);
                    }

                    _previousLidState = isLidOpen;
                }
            }
        }

        private void LidStatusChanged(bool isLidOpen)
        {
            if (isLidOpen)
            {
                //Do some action on lid open event
                Debug.WriteLine("{0}: Lid opened!", DateTime.Now);
            }
            else
            {
                //Do some action on lid close event
                Debug.WriteLine("{0}: Lid closed!", DateTime.Now);
            }
        }
    }
}

Upvotes: 21

Zabba
Zabba

Reputation: 65507

Use WM_POWERBROADCAST. Here's a link that can help you: Lid Close Action change notification

Upvotes: 8

Lukasz Madon
Lukasz Madon

Reputation: 15004

Power Managment

Handling Power-Saving Events The focus so far has been on conserving the battery life while your application is running. There is an additional consideration that you should make: how your application behaves when the computer suspends operation. There are two key scenarios to consider here:

  • When the computer is idle for a certain period of time, the active Power Scheme is likely to specify that the hardware either goes into Stand By or Hibernate mode.
  • When the user takes an action that puts the computer into a suspend operation, such as shutting the lid of a laptop or pressing the power button.

I hope it give u some direction :)

Upvotes: 0

Related Questions