10

I'm writing a C# app (specifically for laptops) and I want to be aware of the state of the lid, i.e. when is it open and when is it closed.

I've already used pInvoke along with Microsoft's RegisterPowerSettingNotification function with the help of this SO answer.

So, with the above I know when the lid is being closed or opened and know its state, BUT I want to get the state of the lid before anything else continues. I've noticed that even if there is no change to the lid, the callback function is called and I can check its parameter to know the lid's state, but this not good for me since it is an event and I can't simply wait for it to occur (maybe there's another way to work around this?).

I've also tried PowerEnumerate function but couldn't get something from it (maybe there is something I don't know there that could help)

EDIT 1: I can't wait or delay the app, and there are 2 issues that require me to get the state without a CB, the first is that a lid can be closed while the laptop is connected to a monitor thus allowing the user to use the laptop. If the user starts the app in this situation it should not start. The second reason is that I want the app to be user friendly and notify what happens when it starts or stops, and if I get the cb in the wrong time it might be ignored, due to the app's inner logic, and then will have to wait until the next time I get a cb, I'd at all.

So, (no pun intended) Is there a way to get this information right away?

EDIT 2: It seems from comments that it's not clear what I need. I need a way to query to state of the lid. i.e. - call some API or something and that the return value is the state. thanks!

Keywords: GUID_LIDSWITCH_STATE_CHANGE , WM_POWERBROADCAST, Power Setting

Community
  • 1
  • 1
ZivS
  • 2,094
  • 2
  • 27
  • 48
  • possible duplicate of [Detect laptop lid closure and opening](http://stackoverflow.com/questions/3355606/detect-laptop-lid-closure-and-opening) – cha0site Nov 27 '14 at 11:48
  • 6
    possible that you didn't read what I wrote – ZivS Nov 27 '14 at 12:47
  • How about caching the last result? – Yam Marcovic Nov 27 '14 at 13:06
  • In WinAPI terms (convert to C++/C# as you see fit) how about using `CreateEvent()` and then `WaitForSingleObject()` on that event; when the lid-state callback fires, it can set the event to signalled and the main program can continue. You'd probably want a timeout on the WFSO call in case the callback ever doesn't fire (at which point you can either abort or assume the state of the lid as appropriate). – TripeHound Nov 27 '14 at 13:26
  • The app takes about 3-5 seconds to start running as it is, I don't want to add any more delays. I already have a workaround which works but I don't want to leave it like that – ZivS Nov 27 '14 at 13:40
  • @YamMarcovic , I already save the prev state, but it doesn't help. I will edit my post and hopefully you'll see why – ZivS Nov 27 '14 at 13:41
  • You say you _want to get the state of the lid before anything else continues_ but then _I don't want to add any more delays_ ... unless I'm missing something, you can't have it both ways. How long does it take to get the first callback after you've registered to receive them? If you register at the earliest point during startup, does your existing 3-5 seconds of startup give it enough time that you'd [rarely] have to wait by the time you got to the callback? Or do you need to know the state _during_ the startup phase? – TripeHound Nov 27 '14 at 14:51
  • The problem is that the cb is recieved during those 3-5 seconds, and I need it either right before or right after that time, if I get the state while the app is starting it might ignore the change due to race conditions or the logic of the app ...this would be a good time to say that this is a massive app and already contains thousands of lines of code. The main point is that I wish to have a way to query the state instead of registering to an event – ZivS Nov 27 '14 at 17:47
  • Sounds to me that a singleton which has a `lidState` attribute which is updated by the callback is what you want. The callback is registered by the constructor of the singleton (and therefore registered during loading of your application). Then the rest of your application can simply query the lidState attribute of the singleton without having to care about when the callback fires. You have to consider multi threading issues though... – Lukas Thomsen Nov 27 '14 at 19:26
  • @Lukas Thomsen - how is your suggestions different than a simple boolean member in my class? I don't see why a singleton is needed here or how is this helping me with the issues I raised – ZivS Nov 27 '14 at 22:45
  • @ShaZiv Maybe you have to be a little more clear about what you want, and especially why you want it. – Simon Dec 02 '14 at 14:10

3 Answers3

2

I don't think 'lid state' is guaranteed to be reflected in the power management properties. Is your interest in the lid state simply down to the monitor? If so, you could find a way to determine the properties of the integrated screen (could be an issue if they install it while connected to an external monitor) and save them. Then check the properties of the screen/s in use and act accordingly.

See System.Windows.Forms.Screen - http://msdn.microsoft.com/en-us/library/system.windows.forms.screen%28v=vs.110%29.aspx

Serial numbers can be extracted from the 'Device Parameters' keys in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY. How to extract it (along with other info) is demonstrated here:

http://sourceforge.net/p/wmimonitor/code/HEAD/tree/DisplayInfoWMIProvider/WMIProvider/WMIProvider.cs

Alternatively, assuming that the callbacks still fire even if the lid close event in Windows is set to 'Do Nothing', you could perhaps consider a helper process or service to run all the time and keep track of changes. You could share the state via the registry or a file. You could also look into whether any other items of hardware (eg. touchpad) change power state on close regardless of the 'power plan' and use the GetDevicePowerState API function.

Llwyd
  • 106
  • 1
  • 3
  • 1
    Could you explain why not? There may be another way but ACPI is hardware / manufacturer specific. Considering that tracking the changes from boot is not satisfactory, detecting whether the laptop screen is powered on / connected seems like a pretty sensible work around based on the info provided – Llwyd Dec 02 '14 at 14:57
  • The first suggestion is not a sensible work around since it doesn't tell me in a definite way that the lid is open\closed. Regarding having another way, there might be another - I'v looked up in other places but could'n t find any thing and that's why I asked for help here. Letting me know I can try to find a solution and not telling me how isn't very helping. I thank you for trying but I want something more concrete. – ZivS Dec 03 '14 at 09:48
1

So, after a long search led by @Llwyd's answer, I found a solution.

I noticed that when the lid is closed, the device is removed from the device manager, so the solution is to check whether it is there or not.

The code behind it is a bit massive to upload here so I won't, but the following was need:

  1. Identify Internal screen's name\ID with WmiMonitorConnectionParams and VideoOutputTechnology and state (DISPLAY_DEVICE_ATTACHED and/or DISPLAY_DEVICE_ACTIVE).

  2. Once I know the device's name/ID, I can easily look it up by enumerating System.Windows.Forms.Screen.AllScreens (or save the value from the DISPLAY_DEVICE_ATTACHED).

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
ZivS
  • 2,094
  • 2
  • 27
  • 48
  • 2
    I wish you would share the code in either github repo or at least a gist. – Maxim V. Pavlov May 17 '18 at 18:08
  • Check this [C++ code](https://github.com/dechamps/laplock/blob/master/laplock.cpp) or [C# code](https://stackoverflow.com/a/23327280/6327676). – DxTx Apr 13 '19 at 01:39
0

This is one way to do it in windows with c++:

static HPOWERNOTIFY sLidHandle = nullptr;
static HWND sHWnd_Lid = nullptr;
static int window_lid_state = 1;   // starts as open

static LRESULT CALLBACK LidWindowProc(HWND hwnd, UINT msg, WPARAM wParam,
                                          LPARAM lParam) {
  if (wParam == PBT_POWERSETTINGCHANGE){
      PPOWERBROADCAST_SETTING ptype = (PPOWERBROADCAST_SETTING)lParam;
      if ( ptype->PowerSetting == GUID_LIDSWITCH_STATE_CHANGE){
          window_lid_state = ptype->Data[0];
      }
  }
  return TRUE;
}

void EnableLidNotifications() {
  // Create custom window to watch Lid events
  if (sHWnd_Lid == nullptr) {
    WNDCLASSW wc;
    HMODULE hSelf = GetModuleHandle(nullptr);
    if (!GetClassInfoW(hSelf, L"wainCamLidClass", &wc)) {
      ZeroMemory(&wc, sizeof(WNDCLASSW));
      wc.hInstance = hSelf;
      wc.lpfnWndProc = LidWindowProc;
      wc.lpszClassName = L"waincamLidClass";
      RegisterClassW(&wc);
    }
    sHWnd_Lid = CreateWindowW(L"wainCamLidClass", L"Lid Watcher", 0, 0, 0,
                          0, 0, nullptr, nullptr, hSelf, nullptr);
  }
  window_lid_state = 1;
  sLidHandle = RegisterPowerSettingNotification(
     sHWnd_Lid,&GUID_LIDSWITCH_STATE_CHANGE, DEVICE_NOTIFY_WINDOW_HANDLE);
}

int my_chk_lid_open(){
    int oflag = 1;
   static QMutex lidstate_mutex;
   if (sLidHandle == nullptr){
       lidstate_mutex.lock();
       if (sLidHandle == nullptr){
         EnableLidNotifications();
         my_assert(sLidHandle!=NULL,"");
       }
       lidstate_mutex.unlock();
   }
   oflag = window_lid_state;
   return(oflag);
}

main(){
   my_chk_lid_open();
   MSG msg;
   BOOL bRet;
   // need to process the initial message to detect the lid state
   bRet = GetMessage(&msg, NULL, 0, 0);  
   printf("laptop lid state=%s\n",my_chk_lid_open() ? "open" : "closed");
}
wainCam
  • 11
  • 2