Events fired when the display power is switched On/Off - c#

I search for an event or if doesn't exist, a method to know if the screen off (Power Options – Control Panel - Turn off the display setting).
None of these solutions work for me.
So either I was wrong somewhere, or it's just not suitable.
How to get the events when the screen/display goes to power OFF or ON?
I expect some track or solution.
The problem is that I don't know what I'm doing, if you could help me a little more it would be cool.
I made this, but it doesn't work:
internal static class NativeMethods
{
public static Guid GUID_MONITOR_POWER_ON = new Guid(0x02731015, 0x4510, 0x4526, 0x99, 0xE6, 0xE5, 0xA1, 0x7E, 0xBD, 0x1A, 0xEA);
public const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
public const int WM_POWERBROADCAST = 0x0218;
public const int PBT_POWERSETTINGCHANGE = 0x8013;
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct POWERBROADCAST_SETTING
{
public Guid PowerSetting;
public uint DataLength;
public byte Data;
}
[DllImport(#"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid, Int32 Flags);
[DllImport(#"User32", SetLastError = true, EntryPoint = "UnregisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
public static extern bool UnregisterPowerSettingNotification(IntPtr handle);
}
private void WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
Debug.WriteLine("EVENT", "DEBUG");
}
public form1()
{
NativeMethods.RegisterPowerSettingNotification(this.Handle, ref NativeMethods.GUID_MONITOR_POWER_ON, NativeMethods.DEVICE_NOTIFY_WINDOW_HANDLE);
}

The declarations are mostly correct, you just need to handle the messages when you're notified.
Override OnHandleCreated, to be sure that the Window handle is valid when you pass it to the function.
Override WndProc, to receive and process the WM_POWERBROADCAST event.
Note that the Guid used in Windows 8+ is different from the one used in Window 7.
Not much, in Windows 8+ is also available a POWERBROADCAST_SETTING.Data value of 0x02, including the Monitor Dimmed status; anyway, it's recommended that you use this Guid instead.
You can check the OSVersion before calling RegisterPowerSettingNotification.
This function returns a handle (IntPtr), which is used to call UnregisterPowerSettingNotification after.
The first notification is sent as soon as your application begins to process the messages (you should receive a message informing you that the Monitor is On :).
Note that these events are notified when the System turns On/Off or dims the Display power, not if you switch the Monitor's Power button On/Off.
public partial class Form1 : Form
{
private IntPtr unRegPowerNotify = IntPtr.Zero;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
var settingGuid = new NativeMethods.PowerSettingGuid();
Guid powerGuid = IsWindows8Plus()
? settingGuid.ConsoleDisplayState
: settingGuid.MonitorPowerGuid;
unRegPowerNotify = NativeMethods.RegisterPowerSettingNotification(
this.Handle, powerGuid, NativeMethods.DEVICE_NOTIFY_WINDOW_HANDLE);
}
private bool IsWindows8Plus()
{
var version = Environment.OSVersion.Version;
if (version.Major > 6) return true; // Windows 10+
if (version.Major == 6 && version.Minor > 1) return true; // Windows 8+
return false; // Windows 7 or less
}
protected override void WndProc(ref Message m)
{
switch (m.Msg) {
case NativeMethods.WM_POWERBROADCAST:
if (m.WParam == (IntPtr)NativeMethods.PBT_POWERSETTINGCHANGE)
{
var settings = (NativeMethods.POWERBROADCAST_SETTING)m.GetLParam(
typeof(NativeMethods.POWERBROADCAST_SETTING));
switch (settings.Data) {
case 0:
Console.WriteLine("Monitor Power Off");
break;
case 1:
Console.WriteLine("Monitor Power On");
break;
case 2:
Console.WriteLine("Monitor Dimmed");
break;
}
}
m.Result = (IntPtr)1;
break;
}
base.WndProc(ref m);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
NativeMethods.UnregisterPowerSettingNotification(unRegPowerNotify);
base.OnFormClosing(e);
}
}
NativeMethods declarations:
using System.Runtime.InteropServices;
public class NativeMethods
{
internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x0;
internal const uint DEVICE_NOTIFY_SERVICE_HANDLE = 0x1;
internal const int WM_POWERBROADCAST = 0x0218;
internal const int PBT_POWERSETTINGCHANGE = 0x8013;
[DllImport("User32.dll", SetLastError = true)]
internal static extern IntPtr RegisterPowerSettingNotification(IntPtr hWnd, [In] Guid PowerSettingGuid, uint Flags);
[DllImport("User32.dll", SetLastError = true)]
internal static extern bool UnregisterPowerSettingNotification(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct POWERBROADCAST_SETTING
{
public Guid PowerSetting;
public uint DataLength;
public byte Data;
}
// https://learn.microsoft.com/en-us/windows/win32/power/power-setting-guids
public class PowerSettingGuid
{
// 0=Powered by AC, 1=Powered by Battery, 2=Powered by short-term source (UPC)
public Guid AcdcPowerSource { get; } = new Guid("5d3e9a59-e9D5-4b00-a6bd-ff34ff516548");
// POWERBROADCAST_SETTING.Data = 1-100
public Guid BatteryPercentageRemaining { get; } = new Guid("a7ad8041-b45a-4cae-87a3-eecbb468a9e1");
// Windows 8+: 0=Monitor Off, 1=Monitor On, 2=Monitor Dimmed
public Guid ConsoleDisplayState { get; } = new Guid("6fe69556-704a-47a0-8f24-c28d936fda47");
// Windows 8+, Session 0 enabled: 0=User providing Input, 2=User Idle
public Guid GlobalUserPresence { get; } = new Guid("786E8A1D-B427-4344-9207-09E70BDCBEA9");
// 0=Monitor Off, 1=Monitor On.
public Guid MonitorPowerGuid { get; } = new Guid("02731015-4510-4526-99e6-e5a17ebd1aea");
// 0=Battery Saver Off, 1=Battery Saver On.
public Guid PowerSavingStatus { get; } = new Guid("E00958C0-C213-4ACE-AC77-FECCED2EEEA5");
// Windows 8+: 0=Off, 1=On, 2=Dimmed
public Guid SessionDisplayStatus { get; } = new Guid("2B84C20E-AD23-4ddf-93DB-05FFBD7EFCA5");
// Windows 8+, no Session 0: 0=User providing Input, 2=User Idle
public Guid SessionUserPresence { get; } = new Guid("3C0F4548-C03F-4c4d-B9F2-237EDE686376");
// 0=Exiting away mode 1=Entering away mode
public Guid SystemAwaymode { get; } = new Guid("98a7f580-01f7-48aa-9c0f-44352c29e5C0");
/* Windows 8+ */
// POWERBROADCAST_SETTING.Data not used
public Guid IdleBackgroundTask { get; } = new Guid(0x515C31D8, 0xF734, 0x163D, 0xA0, 0xFD, 0x11, 0xA0, 0x8C, 0x91, 0xE8, 0xF1);
public Guid PowerSchemePersonality { get; } = new Guid(0x245D8541, 0x3943, 0x4422, 0xB0, 0x25, 0x13, 0xA7, 0x84, 0xF6, 0x79, 0xB7);
// The Following 3 Guids are the POWERBROADCAST_SETTING.Data result of PowerSchemePersonality
public Guid MinPowerSavings { get; } = new Guid("8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c");
public Guid MaxPowerSavings { get; } = new Guid("a1841308-3541-4fab-bc81-f71556f20b4a");
public Guid TypicalPowerSavings { get; } = new Guid("381b4222-f694-41f0-9685-ff5bb260df2e");
}
}

You must call RegisterPowerSettingNotification first
and you will receive WM_POWERBROADCAST message

Related

c# Balloon Tip Text: Remove Project text from notification

I am using Balloon tip text in C# and everything is working as expected. However whenever my notifications appear, there will be subtext that correlates to the project name.
How do I remove this text?
Notification image:
notifyIcon1.Icon = new Icon(SystemIcons.Application, 40, 40);
notifyIcon1.Visible = true;
notifyIcon1.Text = "Application Installation";
notifyIcon1.BalloonTipText = "The App installaion has started.";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.BalloonTipTitle = "Test App Installation";
notifyIcon1.ShowBalloonTip(10000);
Okey so i think i have a start for you atleast. As you can see in the image below i managed to remove the name of the exectuable file that it was running from.
I found a example here how to show Balloon tip like Windows 10 Balloon tip without stretching icon and the example from cokeman19 where you can add your own custom icon. Although this does not seem to work when you dont want the exectuable name at the bottom.
If you want to do some changes i recommend you to look at https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx since it provied some nice hints on what to do.
Here is the code that i made a few changes too so we get what we want.
public class NotifyIconLarge : IDisposable
{
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
public static extern int Shell_NotifyIcon(int message, NOTIFYICONDATA pnid);
[DllImport("Comctl32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr LoadIconWithScaleDown(IntPtr hinst, string pszName, int cx, int cy, out IntPtr phico);
[DllImport("user32.dll", SetLastError = true)]
static extern bool DestroyIcon(IntPtr hIcon);
private const int NIF_LARGE_ICON = 0x00000040;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class NOTIFYICONDATA
{
public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
public IntPtr hWnd;
public int uID;
public int uFlags;
public int uCallbackMessage;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szTip;
public int dwState;
public int dwStateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szInfo;
public int uTimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szInfoTitle;
public int dwInfoFlags;
Guid guidItem;
public IntPtr hBalloonIcon;
}
private IntPtr _windowHandle;
private IntPtr _hIcon;
private bool _added;
private int _id = 1;
private string _tipText;
public NotifyIconLarge(IntPtr windowHandle, string tipText)
{
_windowHandle = windowHandle;
_tipText = tipText;
IntPtr result = LoadIconWithScaleDown(IntPtr.Zero, "", 0, 0, out _hIcon);
UpdateIcon(true);
}
private void UpdateIcon(bool showIconInTray)
{
NOTIFYICONDATA nOTIFYICONDATA = new NOTIFYICONDATA();
nOTIFYICONDATA.uCallbackMessage = 2048;
nOTIFYICONDATA.uFlags = 1;
nOTIFYICONDATA.hWnd = _windowHandle;
nOTIFYICONDATA.uID = _id;
nOTIFYICONDATA.hIcon = IntPtr.Zero;
nOTIFYICONDATA.szTip = null;
if (_hIcon != IntPtr.Zero)
{
nOTIFYICONDATA.uFlags |= 1;
nOTIFYICONDATA.hIcon = _hIcon;
}
nOTIFYICONDATA.uFlags |= 4;
nOTIFYICONDATA.szTip = _tipText;
nOTIFYICONDATA.hBalloonIcon = _hIcon;
if (showIconInTray)
{
if (!_added)
{
Shell_NotifyIcon(0, nOTIFYICONDATA);
_added = true;
}
else
{
Shell_NotifyIcon(1, nOTIFYICONDATA);
}
}
else
{
if (_added)
{
Shell_NotifyIcon(2, nOTIFYICONDATA);
_added = false;
}
}
}
public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
{
NOTIFYICONDATA nOTIFYICONDATA = new NOTIFYICONDATA();
nOTIFYICONDATA.hWnd = _windowHandle;
nOTIFYICONDATA.uID = _id;
nOTIFYICONDATA.uFlags = 16;
nOTIFYICONDATA.uTimeoutOrVersion = timeout;
nOTIFYICONDATA.szInfoTitle = tipTitle;
nOTIFYICONDATA.szInfo = tipText;
switch (tipIcon)
{
case ToolTipIcon.None:
nOTIFYICONDATA.dwInfoFlags = NIF_LARGE_ICON;
break;
case ToolTipIcon.Info:
nOTIFYICONDATA.dwInfoFlags = 1;
break;
case ToolTipIcon.Warning:
nOTIFYICONDATA.dwInfoFlags = 2;
break;
case ToolTipIcon.Error:
nOTIFYICONDATA.dwInfoFlags = 3;
break;
}
int ret = Shell_NotifyIcon(1, nOTIFYICONDATA);
}
public void RemoveFromTray()
{
UpdateIcon(false);
if (_hIcon != IntPtr.Zero)
DestroyIcon(_hIcon);
}
~NotifyIconLarge()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
RemoveFromTray();
}
}
So first you declare the NotifyIconLarge class somewhere.
private NotifyIconLarge _nil;
Then to create a notify popup you use the following code:
_nil = new NotifyIconLarge(Handle, "Icon Tip");
_nil.ShowBalloonTip(10000, "Balloon Title", "Balloon Text", ToolTipIcon.None);
When you are finished with the tray remove it:
_nil.RemoveFromTray();
Credits to cokeman19 for most of the code

Can't register for power notification settings in WinForms C#

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();
}

How to retrieve readable USB VID/PID using dbcc_name from DEV_BROADCAST_DEVICEINTERFACE

I'm trying to catch VIP and PID from USB device:
public const int WM_DEVICECHANGE = 0x219;
public const int DBT_DEVTYP_VOLUME = 0x00000002;
public const int DBT_DEVICEARRIVAL = 0x8000;
[StructLayout(LayoutKind.Sequential)]
internal class DEV_BROADCAST_HDR
{
public int dbch_size;
public int dbch_devicetype;
public int dbch_reserved;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
public int dbcc_size;
public int dbcc_devicetype;
public int dbcc_reserved;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)]
public byte[] dbcc_classguid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] dbcc_name;
}
public void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE) //Device state has changed
{
switch (m.WParam.ToInt32())
{
case DBT_DEVICEARRIVAL: //New device arrives
DEV_BROADCAST_HDR hdr;
hdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
if (hdr.dbch_devicetype == DBT_DEVTYP_VOLUME) //If it is a USB Mass Storage or Hard Drive
{
//Save Device name
DEV_BROADCAST_DEVICEINTERFACE deviceInterface;
string deviceName = "";
deviceInterface = (DEV_BROADCAST_DEVICEINTERFACE)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_DEVICEINTERFACE));
deviceName = new string(deviceInterface.dbcc_name).Trim();
}
}
}
}
But deviceName always returns a string with non sense characters. I have change CharSet in DEV_BROADCAST_DEVICEINTERFACE structure and declare dbcc.name as string but the result is the same.
I would like to avoid reading from registry, and among all I have read, I have seen that it is possible to cast a DEV_BROADCAST_HEADER to a DEV_BROADCAST_DEVICEINTERFACE only if dbch_devicetype==DBT_DEVTYP_DEVICEINTERFACE. In my case, dbch_devicetype is 2, not 5, and I am using some common USB Mass Storage devices. What am I doing wrong? Thanks in advance!
Maybe, there is a more elegant way to resolve this question, but at least, and after a long search, this seems to be working.
On one hand, I register the app in order to receive the correct info to cast it as DEV_BROADCAST_DEVICEINTERFACE structure (DEV_BROADCAST_HEADER.dbch_devicetype is 5 in this case). So, I am able to retrieve VID and PID info. On the other hand, I keep first Windows message WndProc receives to retrieve the volume that Windows assings when I connect USB devices (DEV_BROADCAST_HEADER.dbch_devicetype is 2). Then, I receive two messages.
In code:
public const int WM_DEVICECHANGE = 0x219;
public const int DBT_DEVTYP_VOLUME = 0x00000002;
public const int DBT_DEVICEARRIVAL = 0x8000;
public const int DBT_DEVTYP_DEVICEINTERFACE = 0x00000005;
private IntPtr notificationHandle;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);
[StructLayout(LayoutKind.Sequential)]
internal class DEV_BROADCAST_HDR
{
public int dbch_size;
public int dbch_devicetype;
public int dbch_reserved;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
public int dbcc_size;
public int dbcc_devicetype;
public int dbcc_reserved;
public Guid dbcc_classguid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] dbcc_name;
}
public void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE) //Device state has changed
{
switch (m.WParam.ToInt32())
{
case DBT_DEVICEARRIVAL: //New device arrives
DEV_BROADCAST_HDR hdr;
hdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
if (hdr.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) //If it is a USB Mass Storage or Hard Drive
{
//Save Device name
DEV_BROADCAST_DEVICEINTERFACE deviceInterface = (DEV_BROADCAST_DEVICEINTERFACE)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_DEVICEINTERFACE));
deviceName = new string(deviceInterface.dbcc_name);
deviceNameFiltered = deviceName.Substring(0, deviceName.IndexOf('{'));
vid = GetVid(deviceName);
pid = GetPid(deviceName);
}
if (hdr.dbch_devicetype == DBT_DEVTYP_VOLUME)
{
DEV_BROADCAST_VOLUME volume;
volume = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
//Translate mask to device letter
driveLetter = DriveMaskToLetter(volume.dbcv_unitmask);
}
}
}
To register app in order to receive the correct info to cast it as DEV_BROADCAST_DEVICEINTERFACE structure, it is necessary to call to this last RegisterUsbDeviceNotification method from Form class with its window handler as argument.
public void RegisterUsbDeviceNotification(IntPtr windowHandle)
{
DEV_BROADCAST_DEVICEINTERFACE deviceInterface = new DEV_BROADCAST_DEVICEINTERFACE
{
dbcc_classguid = GuidDevinterfaceUSBDevice,
dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE,
dbcc_reserved = 0,
};
deviceInterface.dbcc_size = Marshal.SizeOf(deviceInterface);
IntPtr buffer = Marshal.AllocHGlobal(deviceInterface.dbcc_size);
Marshal.StructureToPtr(deviceInterface, buffer, true);
notificationHandle = RegisterDeviceNotification(windowHandle, buffer, 0);
}

How to log actions in compact framework?

I'm working on a windows mobile project using compact framework.
One thing I have to do is log when users perform actions, this can mean any action from pressing a button to using the barcode scanner. The time it happened also needs to be logged.
My plan is to override all controls to include logging functionality built into them but this might not be the right way to go about it, seems like a very tedious thing to do..
Is there a better way?
I would go with IL Weaving. Here is a library that I would recommend: http://www.sharpcrafters.com/aop.net/msil-injection What it does is that you mark your class with an attribute and you can intercept all function calls. In this interception you would put in your logging logic.
I'd say it depends greatly on the definition of "action". I'd be highly inclined to see if the (undocumented) QASetWindowsJournalHook API would work. It's probably going to grab most of what you want, with not a lot of code required. A native example of usage can be found on Codeproject here.
SetWindowsHook with WH_JOURNALRECORD might also be worth a look. Yeah, I know it's "unsupported" but it works just fine, and it's unlikely to be removed from a device you've got fielded (plus it's been in the OS for at least 10 years).
Some P/Invoke declarations, all derived from pwinuser.h, for them both are as follows:
[StructLayout(LayoutKind.Sequential)]
public struct JournalHookStruct
{
public int message { get; set; }
public int paramL { get; set; }
public int paramH { get; set; }
public int time { get; set; }
public IntPtr hwnd { get; set; }
}
internal enum HookType
{
JournalRecord = 0,
JournalPlayback = 1,
KeyboardLowLevel = 20
}
internal enum HookCode
{
Action = 0,
GetNext = 1,
Skip = 2,
NoRemove = 3,
SystemModalOn = 4,
SystemModalOff = 5
}
public const int HC_ACTION = 0;
public const int LLKHF_EXTENDED = 0x1;
public const int LLKHF_INJECTED = 0x10;
public const int LLKHF_ALTDOWN = 0x20;
public const int LLKHF_UP = 0x80;
public const int VK_TAB = 0x9;
public const int VK_CONTROL = 0x11;
public const int VK_ESCAPE = 0x1B;
public const int VK_DELETE = 0x2E;
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(HookType idHook, HookProc lpfn, IntPtr hMod, int
[DllImport("coredll.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("coredll.dll", SetLastError = true)]
public static extern int CallNextHookEx(IntPtr hhk, HookCode nCode, IntPtr wParam, IntPtr
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr QASetWindowsJournalHook(HookType nFilterType, HookProc pfnFilterProc, ref JournalHookStruct pfnEventMsg);
Would writing these messages to a log file not solve your problem?
#if PocketPC
private static string _appPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
#else
private static string _appPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Application.CompanyName);
#endif
public const int KILOBYTE = 1024;
public static string ErrorFile { get { return _appPath + #"\error.log"; } }
public static void Log(string message)
{
if (String.IsNullOrEmpty(message)) return;
using (FileStream stream = File.Open(ErrorFile, FileMode.Append, FileAccess.Write))
{
using (StreamWriter sw = new StreamWriter(stream, Encoding.UTF8, KILOBYTE))
{
sw.WriteLine(string.Format("{0:MM/dd/yyyy HH:mm:ss} - {1}", DateTime.Now, message));
}
}
}
You could have issues though if you have threading going on and multiple routines try to write at the same time. In that case, you could add additional logic to lock the routine while it is in use.
That's how I do it, anyway.
By the #if regions, you can see this is also used by my Windows PC applications.

.NET: How to place my window near the notification area (systray)?

I'd like to display a little popup window next to the notification area. It's similar to what Outlook/Skype/Live! Messenger/etc does when it displays the notification about a new message. In my case it will have some input controls (textbox, datetimepicker, buttons...) so a simple bubble won't do.
The trick is doing this correctly when the user has multiple monitors and/or the taskbar is not located at the bottom of the screen. I could not find any functions that would let me determine the position and orientation of the taskbar/notification area.
Use WinAPI calls to find the TaskBar position, and position your window according to it
C# Example
class Program
{
static void Main(string[] args)
{
Taskbar taskbar = new Taskbar();
Console.WriteLine("Position: {0}, AlwaysOnTop: {1}; AutoHide: {2}; Bounds: {3}", taskbar.Position, taskbar.AlwaysOnTop, taskbar.AutoHide, taskbar.Bounds);
Console.ReadLine();
}
}
public enum TaskbarPosition
{
Unknown = -1,
Left,
Top,
Right,
Bottom,
}
public sealed class Taskbar
{
private const string ClassName = "Shell_TrayWnd";
public Rectangle Bounds
{
get;
private set;
}
public TaskbarPosition Position
{
get;
private set;
}
public Point Location
{
get
{
return this.Bounds.Location;
}
}
public Size Size
{
get
{
return this.Bounds.Size;
}
}
//Always returns false under Windows 7
public bool AlwaysOnTop
{
get;
private set;
}
public bool AutoHide
{
get;
private set;
}
public Taskbar()
{
IntPtr taskbarHandle = User32.FindWindow(Taskbar.ClassName, null);
APPBARDATA data = new APPBARDATA();
data.cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA));
data.hWnd = taskbarHandle;
IntPtr result = Shell32.SHAppBarMessage(ABM.GetTaskbarPos, ref data);
if (result == IntPtr.Zero)
throw new InvalidOperationException();
this.Position = (TaskbarPosition) data.uEdge;
this.Bounds = Rectangle.FromLTRB(data.rc.left, data.rc.top, data.rc.right, data.rc.bottom);
data.cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA));
result = Shell32.SHAppBarMessage(ABM.GetState, ref data);
int state = result.ToInt32();
this.AlwaysOnTop = (state & ABS.AlwaysOnTop) == ABS.AlwaysOnTop;
this.AutoHide = (state & ABS.Autohide) == ABS.Autohide;
}
}
public enum ABM : uint
{
New = 0x00000000,
Remove = 0x00000001,
QueryPos = 0x00000002,
SetPos = 0x00000003,
GetState = 0x00000004,
GetTaskbarPos = 0x00000005,
Activate = 0x00000006,
GetAutoHideBar = 0x00000007,
SetAutoHideBar = 0x00000008,
WindowPosChanged = 0x00000009,
SetState = 0x0000000A,
}
public enum ABE : uint
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3
}
public static class ABS
{
public const int Autohide = 0x0000001;
public const int AlwaysOnTop = 0x0000002;
}
public static class Shell32
{
[DllImport("shell32.dll", SetLastError = true)]
public static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData);
}
public static class User32
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
[StructLayout(LayoutKind.Sequential)]
public struct APPBARDATA
{
public uint cbSize;
public IntPtr hWnd;
public uint uCallbackMessage;
public ABE uEdge;
public RECT rc;
public int lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
You need to get the actual location of your notification icon, and place your pop-up window near that (or wherever you like).
You need to translate your XY locations relative to desktop(s). AFAIK, there are no direct function, even in Win32 API which can directly give you the answer.
These sites will help you-
1. http://forum.codecall.net/managed-c/262-dual-monitors-window-position.html
2. http://msdn.microsoft.com/en-us/magazine/cc188759.aspx

Categories

Resources