Reputation: 163518
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
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
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:
and
http://support.microsoft.com/kb/302092
Upvotes: 5
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
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
Reputation: 65507
Use WM_POWERBROADCAST. Here's a link that can help you: Lid Close Action change notification
Upvotes: 8
Reputation: 15004
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:
I hope it give u some direction :)
Upvotes: 0