class WakeUP
{
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes,
bool bManualReset,
string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer,
[In] ref long pDueTime,
int lPeriod,
IntPtr pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine,
bool fResume);
public event EventHandler Woken;
private BackgroundWorker bgWorker = new BackgroundWorker();
public WakeUP()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
public void SetWakeUpTime(DateTime time, String tName)
{
// Create a 7-tuple.
var wutargs = new Tuple<string, string>(time.ToFileTime().ToString(), tName.ToString());
bgWorker.RunWorkerAsync(wutargs);
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Woken != null)
{
Woken(this, new EventArgs());
}
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
var thetuple = e.Argument;
long wakeuptime = (long)thetuple.Item1;
using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, "Timer"))
{
//if (SetWaitableTimer(handle, ref "12:00 AM", 0, IntPtr.Zero, IntPtr.Zero, true))
//{
//using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
//{
//wh.SafeWaitHandle = handle;
//wh.WaitOne();
//}
//}
//else
//{
//throw new Win32Exception(Marshal.GetLastWin32Error());
//}
}
}
Visual Studio is telling me in regards to:
long wakeuptime = (long)thetuple.Item1;
Error 1 'object' does not contain a definition for 'Item1' and no extension method 'Item1' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) C:\Users\esepich\Documents\Visual Studio 2013\Projects\SepysAlarmV1ecs\SepysAlarmV1\WakeUP.cs 57 46 SepysAlarmV1
How am I supposed to access the elements of the tuple?
Thank you for posting...
The argument could be any type. It's passed as an object, so cast it back to the original type:
var thetuple = (Tuple<string, string>)e.Argument;
You're going to get a compiler error on the next line too:
long wakeuptime = (long)thetuple.Item1;
Consider using Convert.ToInt64 or Int64.TryParse.
Related
I have some operations in my application which rely on short timers. Using the example code below I have timers firing every ~5ms as required.
On an Intel i5 10400H CPU the timings are observed to be off, and the callback occurs after ~15ms (or a multiple of 15). Using the ClockRes sysinternals tool shows that the machine has a system timer resolution of 15ms even when run after the call to timeBeginPeriod(1) made in the code below.
Using https://cms.lucashale.com/timer-resolution/ to set the resolution to the maximum supported value (0.5ms) does not change the behaviour of the example code.
From what I can see the machine is using the Invariant TSC acpi timer, and forcing it to use HPET (with bcdedit /set useplatformclock true and rebooting) did not change the behaviour.
I can't see anything in the CPU documentation or errata that would explain this.
I don't know where the problem lies and if it is something that is fixable on my end, any ideas?
Edit: Having this program (DPC Latency Checker) open results in the timer queue firing when expected, so it's solveable.
Example code:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
using (new TimePeriod(1))
RunTimer();
}
public static void RunTimer()
{
var completionEvent = new ManualResetEvent(false);
var stopwatch = Stopwatch.StartNew();
var i = 0;
var previous = 0L;
using var x = TimerQueue.Default.CreateTimer((s) =>
{
if (i > 100)
completionEvent.Set();
i++;
var now = stopwatch.ElapsedMilliseconds;
var gap = now - previous;
previous = now;
Console.WriteLine($"Gap: {gap}ms");
}, "", 10, 5);
completionEvent.WaitOne();
}
}
public class TimerQueueTimer : IDisposable
{
private TimerQueue MyQueue;
private TimerCallback Callback;
private object UserState;
private IntPtr Handle;
internal TimerQueueTimer(
TimerQueue queue,
TimerCallback cb,
object state,
uint dueTime,
uint period,
TimerQueueTimerFlags flags)
{
MyQueue = queue;
Callback = cb;
UserState = state;
bool rslt = TQTimerWin32.CreateTimerQueueTimer(
out Handle,
MyQueue.Handle,
TimerCallback,
IntPtr.Zero,
dueTime,
period,
flags);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error creating timer.");
}
}
~TimerQueueTimer()
{
Dispose(false);
}
public void Change(uint dueTime, uint period)
{
bool rslt = TQTimerWin32.ChangeTimerQueueTimer(MyQueue.Handle, ref Handle, dueTime, period);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error changing timer.");
}
}
private void TimerCallback(IntPtr state, bool bExpired)
{
Callback.Invoke(UserState);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private IntPtr completionEventHandle = new IntPtr(-1);
public void Dispose(WaitHandle completionEvent)
{
completionEventHandle = completionEvent.SafeWaitHandle.DangerousGetHandle();
this.Dispose();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
bool rslt = TQTimerWin32.DeleteTimerQueueTimer(MyQueue.Handle,
Handle, completionEventHandle);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error deleting timer.");
}
disposed = true;
}
}
}
public class TimerQueue : IDisposable
{
public IntPtr Handle { get; private set; }
public static TimerQueue Default { get; private set; }
static TimerQueue()
{
Default = new TimerQueue(IntPtr.Zero);
}
private TimerQueue(IntPtr handle)
{
Handle = handle;
}
public TimerQueue()
{
Handle = TQTimerWin32.CreateTimerQueue();
if (Handle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error creating timer queue.");
}
}
~TimerQueue()
{
Dispose(false);
}
public TimerQueueTimer CreateTimer(
TimerCallback callback,
object state,
uint dueTime,
uint period)
{
return CreateTimer(callback, state, dueTime, period, TimerQueueTimerFlags.ExecuteInPersistentThread);
}
public TimerQueueTimer CreateTimer(
TimerCallback callback,
object state,
uint dueTime,
uint period,
TimerQueueTimerFlags flags)
{
return new TimerQueueTimer(this, callback, state, dueTime, period, flags);
}
private IntPtr CompletionEventHandle = new IntPtr(-1);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(WaitHandle completionEvent)
{
CompletionEventHandle = completionEvent.SafeWaitHandle.DangerousGetHandle();
Dispose();
}
private bool Disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!Disposed)
{
if (Handle != IntPtr.Zero)
{
bool rslt = TQTimerWin32.DeleteTimerQueueEx(Handle, CompletionEventHandle);
if (!rslt)
{
int err = Marshal.GetLastWin32Error();
throw new Win32Exception(err, "Error disposing timer queue");
}
}
Disposed = true;
}
}
}
public enum TimerQueueTimerFlags : uint
{
ExecuteDefault = 0x0000,
ExecuteInTimerThread = 0x0020,
ExecuteInIoThread = 0x0001,
ExecuteInPersistentThread = 0x0080,
ExecuteLongFunction = 0x0010,
ExecuteOnlyOnce = 0x0008,
TransferImpersonation = 0x0100,
}
public delegate void Win32WaitOrTimerCallback(
IntPtr lpParam,
[MarshalAs(UnmanagedType.U1)] bool bTimedOut);
static public class TQTimerWin32
{
[DllImport("kernel32.dll", SetLastError = true)]
public extern static IntPtr CreateTimerQueue();
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueue(IntPtr timerQueue);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueueEx(IntPtr timerQueue, IntPtr completionEvent);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool CreateTimerQueueTimer(
out IntPtr newTimer,
IntPtr timerQueue,
Win32WaitOrTimerCallback callback,
IntPtr userState,
uint dueTime,
uint period,
TimerQueueTimerFlags flags);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool ChangeTimerQueueTimer(
IntPtr timerQueue,
ref IntPtr timer,
uint dueTime,
uint period);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueueTimer(
IntPtr timerQueue,
IntPtr timer,
IntPtr completionEvent);
}
public sealed class TimePeriod : IDisposable
{
private const string WINMM = "winmm.dll";
private static TIMECAPS timeCapabilities;
private static int inTimePeriod;
private readonly int period;
private int disposed;
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeGetDevCaps(ref TIMECAPS ptc, int cbtc);
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeBeginPeriod(int uPeriod);
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeEndPeriod(int uPeriod);
static TimePeriod()
{
int result = timeGetDevCaps(ref timeCapabilities, Marshal.SizeOf(typeof(TIMECAPS)));
if (result != 0)
{
throw new InvalidOperationException("The request to get time capabilities was not completed because an unexpected error with code " + result + " occured.");
}
}
internal TimePeriod(int period)
{
if (Interlocked.Increment(ref inTimePeriod) != 1)
{
Interlocked.Decrement(ref inTimePeriod);
throw new NotSupportedException("The process is already within a time period. Nested time periods are not supported.");
}
if (period < timeCapabilities.wPeriodMin || period > timeCapabilities.wPeriodMax)
{
throw new ArgumentOutOfRangeException("period", "The request to begin a time period was not completed because the resolution specified is out of range.");
}
int result = timeBeginPeriod(period);
if (result != 0)
{
throw new InvalidOperationException("The request to begin a time period was not completed because an unexpected error with code " + result + " occured.");
}
this.period = period;
}
internal static int MinimumPeriod
{
get
{
return timeCapabilities.wPeriodMin;
}
}
internal static int MaximumPeriod
{
get
{
return timeCapabilities.wPeriodMax;
}
}
internal int Period
{
get
{
if (this.disposed > 0)
{
throw new ObjectDisposedException("The time period instance has been disposed.");
}
return this.period;
}
}
public void Dispose()
{
if (Interlocked.Increment(ref this.disposed) == 1)
{
timeEndPeriod(this.period);
Interlocked.Decrement(ref inTimePeriod);
}
else
{
Interlocked.Decrement(ref this.disposed);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct TIMECAPS
{
internal int wPeriodMin;
internal int wPeriodMax;
}
}
}
This seem to be an issue with windows 10 2004. I would guess that it has nothing to do with the processor/motherboard.
A possible workaround might be to use a stopwatch and spinwait on a thread. This would be inadvisable for regular consumer applications since it would consume a full thread, but might be feasible if you have full control of the system.
I encountered the exact same problem under Windows 10 2004. Previous versions did not seem to exhibit the same behavior. CreateTimerQueueTimer does not seem to honor timeBeginPeriod anymore and its minimum period seems to be 15ms (good old 15 ms...).
There are a few people complaining about this problem around, but not a lot. (see this forum entry for example.
I do not know if this is a bug introduced in v2004, or a power-saving "feature" that got sneaked past us.
That being said, official documentation never linked TimerQueueTimers and timeBeginPeriod, so if might have been a bug to begin with that they honored the timeBeginPeriod setting.
In any case, I ended up re-implementing a TimerQueue on top of timeBeginPeriod/timeSetEvent to achieve the required timer frequency.
Running into the same problem, I'm using CreateTimerQueueTimer. What still works is timeSetEvent. You'll loose some precision as it's in whole milliseconds, but it's better than nothing.
I'm working with TreeView Control in Windows Forms in C#.
Since actual population part of TreeView Control takes a lot of time it freezes the UI. So I'm attempting to do the population using PostMessage Win32 API from a background thread but I found that the Treeview isn't getting inserted with Items.
So I moved the code from background thread to main thread. But then also the Insert Item Code is not working. I got similar code working with TreeView in C++ and trying to do the same thing with C# using interop routines.
I'm not going the usual C# way of treeView1.Nodes.Add("...") because it freezes the UI even if I follow the Delegate method and BackgroundWorker method for populating UI controls from another thread. I'm giving the code I use below. Can some one please help to find the issue with the code.
Also Please note for the TreeView Control I'm using my own simple class derived from TreeView class, where I have overriden the WndProc method to verify the flow of Windows Messages and I can see the messages(TVM_INSERTITEM) are actually getting through but still the item is not getting populated
Also I have got similar interop code working fine from Background Thread for ListView Control but my attempts with TreeView haven't succeeded so far.
Form Class Code
using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace UpdateTreeViewFromAnotherThread
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct TVITEM
{
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public uint lParam;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct TVINSERTSTRUCT
{
public IntPtr hParent;
public IntPtr hInsertAfter;
public TVITEM item;
}
public enum TreeViewInsert
{
TVI_ROOT = -0x10000,
}
[Flags]
public enum TreeViewItemMask
{
TVIF_TEXT = 0x0001,
}
public partial class Form1 : Form
{
const int TV_FIRST = 0x1100;
IntPtr tvInsItemPtr;
TVINSERTSTRUCT tvins;
IntPtr handleTreeView;
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
public enum TreeViewMessage
{
TVM_INSERTITEM = TV_FIRST + 50,
}
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
handleTreeView = treeView1.Handle;
//treeView1.Nodes.Add("hello");
PopulateTree(handleTreeView);
}
public void PopulateTree(IntPtr handle)
{
tvins = new TVINSERTSTRUCT();
tvins.item.mask = (uint)TreeViewItemMask.TVIF_TEXT;
// Set the text of the item.
string productName = "Product";
string value = productName;
byte[] buffer = new byte[100];
buffer = Encoding.Unicode.GetBytes(value + "\0");
tvins.item.pszText = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, tvins.item.pszText, buffer.Length);
tvins.hParent = IntPtr.Zero;
tvins.hInsertAfter = (IntPtr)(TreeViewInsert.TVI_ROOT);
tvInsItemPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tvins));
Marshal.StructureToPtr(tvins, tvInsItemPtr, true);
PostMessage(treeView1.Handle, (uint)TreeViewMessage.TVM_INSERTITEM, IntPtr.Zero, tvInsItemPtr);
//SendMessage(treeView1.Handle, (int)TreeViewMessage.TVM_INSERTITEM, 0, tvInsItemPtr);
}
}
}
MyTreeView Class Code
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace UpdateTreeViewFromAnotherThread
{
class MyTreeView:TreeView
{
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x1132)
{
TVINSERTSTRUCT anotherTVInsertStruct;
anotherTVInsertStruct = (TVINSERTSTRUCT)Marshal.PtrToStructure(m.LParam, typeof(TVINSERTSTRUCT));
string anotherNodeText = Marshal.PtrToStringAnsi(anotherTVInsertStruct.item.pszText);
}
if(m.Msg == 0x113F)
{
TVITEM anotherTVItem;
anotherTVItem = (TVITEM)Marshal.PtrToStructure(m.LParam, typeof(TVITEM));
string anotherNodeText = Marshal.PtrToStringAnsi(anotherTVItem.pszText);
}
base.WndProc(ref m);
//Trace.WriteLine(m.Msg.ToString() + ", " + m.ToString());
}
}
}
Update_1
Prevented NM_CUSTOMDRAW for Treeview using the below code. Thanks to the code atlink.
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_REFLECT + WM_NOTIFY:
{
NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
switch ((int)nmhdr.code)
{
case NM_CUSTOMDRAW:
NMTVCUSTOMDRAW nmTvDraw = (NMTVCUSTOMDRAW)m.GetLParam(typeof(NMTVCUSTOMDRAW));
switch (nmTvDraw.nmcd.dwDrawStage)
{
case CDDS_ITEMPREPAINT:
m.Result = (IntPtr)CDRF_DODEFAULT;
break;
}
Marshal.StructureToPtr(nmTvDraw, m.LParam, false);
return;
}
break;
}
}
base.WndProc(ref m);
}
So Now If I change my earlier PopulateTree function (note Thread.Sleep()) and its invocation to a background thread as below it will not freeze the UI during the population process
private void button1_Click(object sender, EventArgs e)
{
handleTreeView = treeView1.Handle;
Thread backgroundThread = new Thread(() => PopulateTree(handleTreeView));
backgroundThread.Start();
}
public void PopulateTree(IntPtr handle)
{
for(int i =0; i< 1000; i++)
{
tvins = new TVINSERTSTRUCT();
tvins.item.mask = (uint)TreeViewItemMask.TVIF_TEXT;
// Set the text of the item.
string productName = "Product_" + i.ToString();
string value = productName;
byte[] buffer = new byte[100];
buffer = Encoding.Unicode.GetBytes(value + "\0");
tvins.item.pszText = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, tvins.item.pszText, buffer.Length);
tvins.hParent = IntPtr.Zero;
tvins.hInsertAfter = (IntPtr)(TreeViewInsert.TVI_ROOT);
tvInsItemPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tvins));
Marshal.StructureToPtr(tvins, tvInsItemPtr, true);
PostMessage(handle, (uint)TreeViewMessage.TVM_INSERTITEM, IntPtr.Zero, tvInsItemPtr);
Thread.Sleep(1000);
}
}
Thanks Jimi and MikiD I was able to produce same non-freezing UI behaviour using the BeginUpdate and BeginInvoke approach. I changed my code as below
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => PopulateTree());
}
private async void PopulateTree()
{
for(int i = 0;i< 1000;i++)
{
treeView1.BeginInvoke( (MethodInvoker)delegate ()
{
treeView1.BeginUpdate();
treeView1.Nodes.Add("Product_" + i.ToString());
treeView1.EndUpdate();
}
);
System.Threading.Thread.Sleep(1000);
}
}
The following code example (from here) creates a waitable timer and set the system in hibernation mode. After resuming back I expected an event with executable code:
class WakeUpTimer
{
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes,
bool bManualReset,
string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer,
[In] ref long pDueTime,
int lPeriod,
IntPtr pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine,
bool fResume);
public event EventHandler Woken;
private BackgroundWorker bgWorker = new BackgroundWorker();
public WakeUpTimer()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
public void SetWakeUpTime(DateTime time)
{
bgWorker.RunWorkerAsync(time.ToFileTime());
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Woken != null)
{
Woken(this, new EventArgs());
}
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
long waketime = (long)e.Argument;
using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, this.GetType().Assembly.GetName().Name.ToString() + "Timer"))
{
if (SetWaitableTimer(handle, ref waketime, 0, IntPtr.Zero, IntPtr.Zero, true))
{
using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
{
wh.SafeWaitHandle = handle;
wh.WaitOne();
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
Usage of this class is as follows:
private void setHibernation(DateTime dateTime)
{
WakeUpTimer wakeUpTimer = new WakeUpTimer();
wakeUpTimer.Woken += backFromHibernate;
wakeUpTimer.SetWakeUpTime(dateTime);
Application.SetSuspendState(PowerState.Hibernate, false, false);
Debug.WriteLine("---Hibernation starts at " + DateTime.Now + " ---");
}
private void backFromHibernate(object sender, EventArgs e)
{
Debug.WriteLine("I execute this now " + DateTime.Now);
}
As I tested before this scenario several times, the code in the method backFromHibernation is executed immediately (1 sec after setSupsendState duo to debug logs).
Am I wrong or should the method be called when the system is back up from hibernation?
Would appriciate some help, would need such an event - fired automatically after wake up.
EDIT: Set hibernation for 2 min, all works great system comes up automatically.
Here is the code
[DllImport(#"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification",
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid, Int32 Flags);
static Guid GUID_LIDSWITCH_STATE_CHANGE = new Guid(0xBA3E0F4D, 0xB817, 0x4094, 0xA2, 0xD1, 0xD5, 0x63, 0x79, 0xE6, 0xA0, 0xF3);
private const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
private const int WM_POWERBROADCAST = 0x0218;
const int PBT_POWERSETTINGCHANGE = 0x8013;
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct POWERBROADCAST_SETTING
{
public Guid PowerSetting;
public uint DataLength;
public byte Data;
}
private bool? _previousLidState = null;
public TrayIcon()
{
RegisterForPowerNotifications();
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_POWERBROADCAST:
OnPowerBroadcast(m.WParam, m.LParam);
break;
default:
break;
}
base.WndProc(ref m);
}
private void RegisterForPowerNotifications()
{
IntPtr handle = this.Handle;
Debug.WriteLine("Handle: " + handle.ToString()); //If this line is omitted, then lastError = 1008 which is ERROR_NO_TOKEN, otherwise, lastError = 0
IntPtr hLIDSWITCHSTATECHANGE = RegisterPowerSettingNotification(handle,
ref GUID_LIDSWITCH_STATE_CHANGE,
DEVICE_NOTIFY_WINDOW_HANDLE);
Debug.WriteLine("Registered: " + hLIDSWITCHSTATECHANGE.ToString());
Debug.WriteLine("LastError:" + Marshal.GetLastWin32Error().ToString());
}
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
MessageBox.Show("Lid is now open");
}
else
{
//Do some action on lid close event
MessageBox.Show("Lid is now closed");
}
}
}
}
I have no idea what the problem is. I get calls to WndProc function, but nothing happens when the lid is closed or opened. LidStatusChanged is never called.
I have followed this post but that doesn't help as everything matches.
I have no idea what the heck I did wrong. All help is greatly appreciated.
ShowInTaskbar = Visible = false;
The bug is no longer visible in the snippet. It is the ShowInTaskbar property assignment that caused the problem. It is a "difficult" property, it can only be specified in the style flags passed to CreateWindowEx(). So that forces Winforms to destroy the current window and create a new one, it now gets a different Handle value. No more notifications.
You probably got into this trouble by trying to keep the window invisible. Proper way to do that is:
protected override void SetVisibleCore(bool value) {
if (!IsHandleCreated) {
this.CreateHandle();
value = false;
}
base.SetVisibleCore(value);
}
Delete OnLoad(), no longer necessary and not called until the window actually becomes visible. And you want to make sure that, even if the Handle value changes for some reason (there are several "difficult" properties), you still get a notification. Which you do by deleting the code from the constructor and:
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
RegisterForPowerNotifications();
}
This code works fine with me:
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr token);
enum LogonType
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkClearText = 8,
NewCredentials = 9
}
enum LogonProvider
{
Default = 0,
WinNT35 = 1,
WinNT40 = 2,
WinNT50 = 3
}
private void Button1_Click()
{
IntPtr token = IntPtr.Zero;
LogonUser("Administrator",
"192.168.1.244",
"PassWord",
(int)LogonType.NewCredentials,
(int)LogonProvider.WinNT50,
ref token);
using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token))
{
CloseHandle(token);
/*
Code_of_Do_Something
*/
}
}
BUT...This means I have to repeat the last code which inside "Button1_Click()" each time I need to do impersonation ( Doing something on the remote machine = server).
So my question: Is it possible to do something like this illustration?:
You can use delegates for this purpose. The easiest way is to use Action or Func. If you don't need a return value, use an Action:
private void RunImpersonated(Action act)
{
IntPtr token = IntPtr.Zero;
LogonUser("Administrator",
"192.168.1.244",
"PassWord",
(int)LogonType.NewCredentials,
(int)LogonProvider.WinNT50,
ref token);
try
{
using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token))
{
// Call action
act();
}
}
finally
{
CloseHandle(token);
}
}
Note that there are lots of variations with generic type parameters that allow you to also provide parameters to Action or Func delegates in a strongly typed way. If you need an into parameter for instance, use Action<int> instead of just Action.
Also note that I created a finally block that closes the handle whether an exception occurs or not.
In order to call the function, you can use a lambda expression:
private void button1_Click(object sender, EventArgs e)
{
RunImpersonated(() => {
DirectoryInfo dir = new DirectoryInfo( #"\\192.168.1.244\repository");
foreach (DirectoryInfo di in dir.GetDirectories())
{
lable_folders_count.Text = Convert.ToString(dir.GetFileSystemInfos().Length);
}
});
}
Yes, it is possible to pass code as a parameter. But let's solve your problem without using lambdas:
private void Button1_Click()
{
using(GetImpersonationContext())
{
/* code here */
}
}
private WindowsImpersonationContext GetImpersonationContext()
{
IntPtr token = IntPtr.Zero;
LogonUser("Administrator",
"192.168.1.244",
"PassWord",
(int)LogonType.NewCredentials,
(int)LogonProvider.WinNT50,
ref token);
WindowsImpersonationContext context = WindowsIdentity.Impersonate(token);
CloseHandle(token);
return context;
}