Detect laptop lid closure and opening
Asked Answered
C

5

28

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.

Canula answered 28/7, 2010 at 17:25 Comment(5)
I am really curious why you would want this.Tobolsk
I need to execute certain actions when the lid is closed. For example, I might want to lock the desktop, or log in a file when the lid was closed and opened. This exercise isn't academic, I just can't disclose the specific reason at this time due to NDA. Sorry.Canula
I'm no expert, but I think listening in on OS calls would require COM or P/INVOKE of some sort. You might want to add those tags to get a more knowledgeable readership.Length
I actually have my laptop configured to "do nothing" when the laptop lid is closed, Windows definitely knows about the lid close events. I'm not sure about an API function that taps in to this, see "Power Management In Windows" msdn.microsoft.com/en-us/library/ms704147(VS.85).aspx.Kleptomania
@hamish-grubijan On laptop's lid closed I want to change power plan to Power Saving, turn off sound volume and start some torrents.Danell
M
19

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);
            }
        }
    }
}
Maltese answered 27/4, 2014 at 18:29 Comment(5)
thanks! but where did you get the value for GUID_LIDSWITCH_STATE_CHANGE from?Gelid
Worked for me :) I just had to change this line: IntPtr handle = new WindowInteropHelper(Application.Current.Windows[0]).Handle; to this: IntPtr handle = new WindowInteropHelper(this).Handle;Cortneycorty
How would this work for a console application in c#?Corell
@Red_Phoenix, I guess in console application you can make a new Window object (add reference to either WinForms or WPF) without showing it and use it's handle.Maltese
how can we run this in Powershell?Ashcraft
H
8

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

Hoogh answered 28/7, 2010 at 17:53 Comment(4)
Thanks Zabba! This is exactly what I was looking for, but it seems it will only work for Vista and 7. Any thoughts out there for something equivalent in XP? Thanks again.Canula
It will work on Windows 2000 and greater - msdn.microsoft.com/en-us/library/aa373247%28VS.85%29.aspxHoogh
The RegisterPowerSettingNotification step requires Vista or higher.Woodwind
If you're attempting to support OSs that are older than Windows 7/Vista, you can always manipulate the "On lid close" hibernate/standby/shutdown operation.Ecclesia
K
5

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

Khudari answered 28/7, 2010 at 18:0 Comment(1)
@icemanind: Any insight on whether it's possible to add your suggested "filter that intercepts the IOCTL_GET_SYS_BUTTON_EVENT event" from a local service?Confraternity
L
1

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.

Leatherette answered 8/8, 2019 at 15:57 Comment(1)
How to run this in Powershell?Ashcraft
B
0

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 :)

Barros answered 28/7, 2010 at 17:45 Comment(1)
Sorry, suspend is not an appropriate trigger in my case.Canula

© 2022 - 2024 — McMap. All rights reserved.