I'm registering a hotkey with "RegisterHotKey" from user32.dll
Pressing the hotkey will trigger an event which does Console.WriteLine("HOTKEY");
Works great normally, but I am having a problem where after about 5 seconds it stops working. event is no longer triggered.
By commenting things out, I have narrowed this down to 1 line of code:
Process[] p = Process.GetProcessesByName("notepad");
i.e. getting process names (name doesn't matter, notepad or anything)
This GetProcessesByName is called on a System.Timers timer once per second. and like i said, after about 5 seconds (sometimes 3 or 4, its random) the hotkey stops working.
How can I fix this?
Below is the code i'm using (from this website)
Could the problem have something to do with the 1st argument of RegisterHotKey?? (a HWND)
Must I have am active form to register hotkeys?
public class HotkeyController {
public HotkeyController() {
KeyboardHook k = new KeyboardHook();
k.RegisterHotKey(0, Keys.Subtract);
k.KeyPressed += new EventHandler<KeyPressedEventArgs>(k_KeyPressed);
}
void k_KeyPressed(object sender, KeyPressedEventArgs e) {
Console.WriteLine("HOTKEY");
}
}
public sealed class KeyboardHook : IDisposable {
// Registers a hot key with Windows.
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
// Unregisters the hot key with Windows.
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
/// <summary>
/// Represents the window that is used internally to get the messages.
/// </summary>
private class Window : NativeWindow, IDisposable {
private static int WM_HOTKEY = 0x0312;
public Window() {
// create the handle for the window.
this.CreateHandle(new CreateParams());
}
/// <summary>
/// Overridden to get the notifications.
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
// check if we got a hot key pressed.
if (m.Msg == WM_HOTKEY) {
// get the keys.
Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
ModifierKeys modifier = (ModifierKeys)((int)m.LParam & 0xFFFF);
// invoke the event to notify the parent.
if (KeyPressed != null)
KeyPressed(this, new KeyPressedEventArgs(modifier, key));
}
}
public event EventHandler<KeyPressedEventArgs> KeyPressed;
#region IDisposable Members
public void Dispose() {
this.DestroyHandle();
}
#endregion
}
private Window _window = new Window();
private int _currentId;
public KeyboardHook() {
// register the event of the inner native window.
_window.KeyPressed += delegate(object sender, KeyPressedEventArgs args) {
if (KeyPressed != null)
KeyPressed(this, args);
};
}
/// <summary>
/// Registers a hot key in the system.
/// </summary>
/// <param name="modifier">The modifiers that are associated with the hot key.</param>
/// <param name="key">The key itself that is associated with the hot key.</param>
public void RegisterHotKey(ModifierKeys modifier, Keys key) {
// increment the counter.
_currentId = _currentId + 1;
// register the hot key.
if (!RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key))
throw new InvalidOperationException("Couldn’t register the hot key.");
}
/// <summary>
/// A hot key has been pressed.
/// </summary>
public event EventHandler<KeyPressedEventArgs> KeyPressed;
#region IDisposable Members
public void Dispose() {
// unregister all the registered hot keys.
for (int i = _currentId; i > 0; i--) {
UnregisterHotKey(_window.Handle, i);
}
// dispose the inner native window.
_window.Dispose();
}
#endregion
}
/// <summary>
/// Event Args for the event that is fired after the hot key has been pressed.
/// </summary>
public class KeyPressedEventArgs : EventArgs {
private ModifierKeys _modifier;
private Keys _key;
internal KeyPressedEventArgs(ModifierKeys modifier, Keys key) {
_modifier = modifier;
_key = key;
}
public ModifierKeys Modifier {
get { return _modifier; }
}
public Keys Key {
get { return _key; }
}
}
/// <summary>
/// The enumeration of possible modifiers.
/// </summary>
[Flags]
public enum ModifierKeys : uint {
Alt = 1,
Control = 2,
Shift = 4,
Win = 8
}
thanks Alex, you gave me an idea to try this using a form (as i was using in the past) and its working fine now.
in the code i posted, it makes a Window like so (what is this and how is it different from a form?)
private Window _window = new Window();
perhaps Process.GetProcesses finds this "Window" and kills it? thus stopping the hotkeys from working?
who knows?? i am no guru...
anyway, i can just make an invisible form, which is good enough.
this is the "hotkey form" code i'm using:
public partial class HotkeyForm : Form {
public HotkeyForm() {
InitializeComponent();
RegisterHotKey(this.Handle, 0, 0, (int)Keys.Subtract);
}
protected override void WndProc(ref Message m) {
if (m.Msg == 0x0312) {
switch (m.WParam.ToInt32()) {
case 0: //numpad minus.
//Environment.Exit(0);
Console.WriteLine("FORM HOTKEY");
break;
}
}
base.WndProc(ref m);
}
}
Related
I have a windowless winforms application that uses an ApplicationContext to setup a NotifyIcon (TrayIcon) with which the user can control. But I also want to use hotkeys.
I found some good approaches using RegisterHotkey (eg. Global hotkeys in windowless .NET app), but they all need a form or a native window, which I don't want to use because of side-effects.
But I already use a NotifyIcon (TrayIcon) and I guess that has some kind of message pipe to trigger clicks etc. already. How can I use that one to register global hotkeys?
I required the same and finally I found this answer on another question Set global hotkeys using C# It is working perfectly with tray (NotifyIcon) application.
I am aligning the code for tray application, just follow the below steps:
1. Create new class "KeyboardHook"
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyTrayApp
{
public sealed class KeyboardHook : IDisposable
{
// Registers a hot key with Windows.
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
// Unregisters the hot key with Windows.
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
/// <summary>
/// Represents the window that is used internally to get the messages.
/// </summary>
private class Window : NativeWindow, IDisposable
{
private static int WM_HOTKEY = 0x0312;
public Window()
{
// create the handle for the window.
this.CreateHandle(new CreateParams());
}
/// <summary>
/// Overridden to get the notifications.
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
// check if we got a hot key pressed.
if (m.Msg == WM_HOTKEY)
{
// get the keys.
Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
ModifierKeys modifier = (ModifierKeys)((int)m.LParam & 0xFFFF);
// invoke the event to notify the parent.
if (KeyPressed != null)
KeyPressed(this, new KeyPressedEventArgs(modifier, key));
}
}
public event EventHandler<KeyPressedEventArgs> KeyPressed;
#region IDisposable Members
public void Dispose()
{
this.DestroyHandle();
}
#endregion
}
private Window _window = new Window();
private int _currentId;
public KeyboardHook()
{
// register the event of the inner native window.
_window.KeyPressed += delegate (object sender, KeyPressedEventArgs args)
{
if (KeyPressed != null)
KeyPressed(this, args);
};
}
/// <summary>
/// Registers a hot key in the system.
/// </summary>
/// <param name="modifier">The modifiers that are associated with the hot key.</param>
/// <param name="key">The key itself that is associated with the hot key.</param>
public void RegisterHotKey(ModifierKeys modifier, Keys key)
{
// increment the counter.
_currentId = _currentId + 1;
// register the hot key.
if (!RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key))
{
string message = "The hotkey \"Ctrl+Alt+K\" could not be registered. This problem is probably causedby another tool claiming usage of the same hotkey!\r\n\r\nAll KeepOn features still work directly from the tray icon context menu without hotkey.";
string title = "KeepOn";
MessageBoxButtons buttons = MessageBoxButtons.OK;
MessageBoxIcon icon = MessageBoxIcon.Exclamation;
DialogResult result = MessageBox.Show(message, title, buttons,icon);
}
}
/// <summary>
/// A hot key has been pressed.
/// </summary>
public event EventHandler<KeyPressedEventArgs> KeyPressed;
#region IDisposable Members
public void Dispose()
{
// unregister all the registered hot keys.
for (int i = _currentId; i > 0; i--)
{
UnregisterHotKey(_window.Handle, i);
}
// dispose the inner native window.
_window.Dispose();
}
#endregion
}
/// <summary>
/// Event Args for the event that is fired after the hot key has been pressed.
/// </summary>
public class KeyPressedEventArgs : EventArgs
{
private ModifierKeys _modifier;
private Keys _key;
internal KeyPressedEventArgs(ModifierKeys modifier, Keys key)
{
_modifier = modifier;
_key = key;
}
public ModifierKeys Modifier
{
get { return _modifier; }
}
public Keys Key
{
get { return _key; }
}
}
/// <summary>
/// The enumeration of possible modifiers.
/// </summary>
[Flags]
public enum ModifierKeys : uint
{
Alt = 1,
Control = 2,
Shift = 4,
Win = 8
}
}
2. Add code to your main class (app startup class)
Check your startup class in Main() method of Program.cs file
Application.Run(new TrayApplicationContext());
public class TrayApplicationContext : ApplicationContext
{
KeyboardHook hook = new KeyboardHook();
public TaskTrayApplicationContext()
{
// register the event that is fired after the key press.
hook.KeyPressed += new EventHandler<KeyPressedEventArgs>(Close_App);
// register the control + alt+ F12 combination as hot key.
hook.RegisterHotKey(ModifierKeys.Control | ModifierKeys.Alt, Keys.F12);
}
void Close_App(object sender, KeyPressedEventArgs e)
{
Application.Exit();
}
}
Say thanks to stackoverflow community to make it possible!!
Do WPF have Touch-and-Hold gesture? I cannot find event for that, so I tried to implement one for myself. I know that there is Stylus class but in WPF it does not help me. If there aren't one there is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace WebControlTouch
{
/// <summary>
/// Due to lack of Touch-and-Hold gesture, here is implementation of it. Stupid M$.
/// </summary>
public static class Touch_and_Hold
{
#region Constructor + methods
/// <summary>
/// Static constructor which creates timer object with 1000ms interval, also sets parameters of Timer.
/// </summary>
static Touch_and_Hold()
{
gestureTimer = new Timer(1000);
gestureTimer.AutoReset = false;
gestureTimer.Elapsed += gestureTimer_Elapsed;
}
/// <summary>
/// On elasped (time ofc)
/// </summary>
/// <seealso cref="gestureTimer"/>
static void gestureTimer_Elapsed(object sender, ElapsedEventArgs e)
{
occured = true;
}
/// <summary>
/// Call it on OnTouchDown event.
/// It will start timer and will count time of touch
/// </summary>
/// <returns>Returns that gesture occured</returns>
public static void onTouch()
{
gestureTimer.Start();
}
/// <summary>
/// Call it on touch up mainwindow event (or somewhere else)
/// It stops gesture timer
/// </summary>
public static void onTouchUp()
{
occured = false;
}
#endregion
#region Members + properties
/// <summary>
/// Timer for measuring touchTime
/// </summary>
private static Timer gestureTimer;
/// <summary>
/// Do tap-and-hold occured
/// </summary>
private static bool occured = false;
/// <summary>
/// Property for getting occured flag
/// </summary>
public static bool occuredGesture
{
get { return occured; }
}
#endregion
}
}
If yes, please tell me name of the event. If not - try to steer me to solution.
Any help will be very appreciated.
It is possible to do that in an awaitable fashion. Create a timer with specific interval. Start it when user tapped and return the method when timer elapsed. If user release the hand, return the method with false flag.
public static Task<bool> TouchHold(this FrameworkElement element, TimeSpan duration)
{
DispatcherTimer timer = new DispatcherTimer();
TaskCompletionSource<bool> task = new TaskCompletionSource<bool>();
timer.Interval = duration;
MouseButtonEventHandler touchUpHandler = delegate
{
timer.Stop();
if (task.Task.Status == TaskStatus.Running)
{
task.SetResult(false);
}
};
element.PreviewMouseUp += touchUpHandler;
timer.Tick += delegate
{
element.PreviewMouseUp -= touchUpHandler;
timer.Stop();
task.SetResult(true);
};
timer.Start();
return task.Task;
}
For more information, read this post.
I've previously achieved this by create a custom control that extends button to delay the trigger of a button command after a delay on press-and-hold.
public class DelayedActionCommandButton : Button
First dependency properties:
public static readonly DependencyProperty DelayElapsedProperty =
DependencyProperty.Register("DelayElapsed", typeof(double), typeof(DelayedActionCommandButton), new PropertyMetadata(0d));
public static readonly DependencyProperty DelayMillisecondsProperty =
DependencyProperty.Register("DelayMilliseconds", typeof(int), typeof(DelayedActionCommandButton), new PropertyMetadata(1000));
public double DelayElapsed
{
get { return (double)this.GetValue(DelayElapsedProperty); }
set { this.SetValue(DelayElapsedProperty, value); }
}
public int DelayMilliseconds
{
get { return (int)this.GetValue(DelayMillisecondsProperty); }
set { this.SetValue(DelayMillisecondsProperty, value); }
}
These give us a control on how the delay should be and an output of how long is left.
Next I create an animation, to control the elapsed amount which when complete fires the command. There is also a cancel delay method:
private void BeginDelay()
{
this._animation = new DoubleAnimationUsingKeyFrames() { FillBehavior = FillBehavior.Stop };
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(this.DelayMilliseconds)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.Completed += (o, e) =>
{
this.DelayElapsed = 0d;
this.Command.Execute(this.CommandParameter); // Replace with whatever action you want to perform
};
this.BeginAnimation(DelayElapsedProperty, this._animation);
}
private void CancelDelay()
{
// Cancel animation
this.BeginAnimation(DelayElapsedProperty, null);
}
Finally, we wire up the event handlers:
private void DelayedActionCommandButton_TouchDown(object sender, System.Windows.Input.TouchEventArgs e)
{
this.BeginDelay();
}
private void DelayedActionCommandButton_TouchUp(object sender, System.Windows.Input.TouchEventArgs e)
{
this.CancelDelay();
}
When used in XAML, you can optionally create a template that can animate based on the value of DelayElapsed to provide a countdown, or visual cue such as an expanding border, whatever takes your fancy.
How can I create a loop that will be continuously executed whenever the message loop is idle in WPF?
The goal here is to perform some long running graphical update, such as refreshing a PicktureBox, that is capable of consuming whatever free resources are available but shouldn't freeze the UI or otherwise take priority over any other operations in the message queue.
I noticed this blog post which provides the code to do this in a winforms application, but I don't know how to translate it to a WPF application. Below is the code of a WinForms render loop class that I made based on the other article:
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace Utilities.UI
{
/// <summary>
/// WinFormsAppIdleHandler implements a WinForms Render Loop (max FPS possible).
/// Reference: http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
/// </summary>
public sealed class WinFormsAppIdleHandler
{
private readonly object _completedEventLock = new object();
private event EventHandler _applicationLoopDoWork;
//PRIVATE Constructor
private WinFormsAppIdleHandler()
{
Enabled = false;
SleepTime = 10;
Application.Idle += Application_Idle;
}
/// <summary>
/// Singleton from:
/// http://csharpindepth.com/Articles/General/Singleton.aspx
/// </summary>
private static readonly Lazy<WinFormsAppIdleHandler> lazy = new Lazy<WinFormsAppIdleHandler>(() => new WinFormsAppIdleHandler());
public static WinFormsAppIdleHandler Instance { get { return lazy.Value; } }
/// <summary>
/// Gets or sets if must fire ApplicationLoopDoWork event.
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// Gets or sets the minimum time betwen ApplicationLoopDoWork fires.
/// </summary>
public int SleepTime { get; set; }
/// <summary>
/// Fires while the UI is free to work. Sleeps for "SleepTime" ms.
/// </summary>
public event EventHandler ApplicationLoopDoWork
{
//Reason of using locks:
//http://stackoverflow.com/questions/1037811/c-thread-safe-events
add
{
lock (_completedEventLock)
_applicationLoopDoWork += value;
}
remove
{
lock (_completedEventLock)
_applicationLoopDoWork -= value;
}
}
/// <summary>
/// FINALMENTE! Imagem ao vivo sem travar! Muito bom!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Application_Idle(object sender, EventArgs e)
{
//Try to update interface
while (Enabled && IsAppStillIdle())
{
OnApplicationIdleDoWork(EventArgs.Empty);
//Give a break to the processor... :)
//8 ms -> 125 Hz
//10 ms -> 100 Hz
Thread.Sleep(SleepTime);
}
}
private void OnApplicationIdleDoWork(EventArgs e)
{
var handler = _applicationLoopDoWork;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Gets if the app still idle.
/// </summary>
/// <returns></returns>
private static bool IsAppStillIdle()
{
bool stillIdle = false;
try
{
Message msg;
stillIdle = !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
catch (Exception e)
{
//Should never get here... I hope...
MessageBox.Show("IsAppStillIdle() Exception. Message: " + e.Message);
}
return stillIdle;
}
#region Unmanaged Get PeekMessage
// http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
#endregion
}
}
The best way to do this is to use the per-frame callbacks provided by the static CompositionTarget.Rendering event.
To elaborate a bit on the answer of Oren, you can attach a method to the Rendering event like this:
CompositionTarget.Rendering += Loop;
The Loop function can then update the properties of an element, in this example an element positioned on a Canvas:
private void Loop(object sender, EventArgs e)
{
LeftPos++;
Canvas.SetLeft(ball, LeftPos);
}
https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.compositiontarget.rendering?view=net-5.0
I have a Windows Form that starts some console application in background(CreateNoWindow = rue,WindowStyle = ProcessWindowStyle.Hidden).
Windows form gives me opportunity to stop the console application at any time. But I'd like to handle somehow the close message inside the console application. I tried to use hooking like:
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add);
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate bool HandlerRoutine(CtrlTypes ctrlType);
// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
StaticLogger.Instance.DebugFormat("Main: ConsoleCtrlCheck: Got event {0}.", ctrlType);
if (ctrlType == CtrlTypes.CTRL_CLOSE_EVENT)
{
// Handle close stuff
}
return true;
}
static int Main(string[] args)
{
// Subscribing
HandlerRoutine hr = new HandlerRoutine(ConsoleCtrlCheck);
SetConsoleCtrlHandler(hr, true);
// Doing stuff
}
but I get the message inside ConsoleCtrlCheck only if the console window is created. But if window is hidden - I don't get any message.
In my windows Form to close console application process I use
proc.CloseMainWindow();
to send message to the console window.
P.S. AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; - also does not help
Do you now other way to handle this situation?
Thanks.
This might work. I used it in NUnit testes to clean up environment. Unfortunately it is not garantieed to be called. To make it working you need to create an instance of it and pass callback function that should be called on shutdown.
/// <summary>
/// Detects the moment when environment is about to be shutdown.
/// <remarks>
/// For usage just create single instance of it.
/// Each time when GC calles Finilize a '~ShutdownDetector' will be called.
/// </remarks>
/// </summary>
public sealed class ShutdownDetector
{
/// <summary>
/// Initializes a new instance of the <see cref="T:ShutdownDetector"/> class.
/// </summary>
/// <param name="notifier">The notifier</param>
public ShutdownDetector(Notifier notifier)
{
if (notifier == null) throw new ArgumentNullException("notifier");
_notifier = notifier;
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="T:CQG.PDTools.Common.ShutdownDetector"/> is reclaimed by garbage collection.
/// </summary>
~ShutdownDetector()
{
if (Environment.HasShutdownStarted)
{
onShutdown();
}
else
{
new ShutdownDetector(_notifier);
}
}
/// <summary>
/// Called when component needs to signal about shutdown.
/// </summary>
private void onShutdown()
{
if (_notifier != null)
{
_notifier();
}
}
Notifier _notifier;
public delegate void Notifier();
}
I am trying to write a class library that can catch the windows messages to notify me if a device has been attached or removed. Normally, in a windows forms app I would just override the WndProc method but there is not WndProc method in this case. Is there another way I can get the messages?
You'll need a window, there's no way around that. Here's a sample implementation. Implement an event handler for the DeviceChangeNotifier.DeviceNotify event to get notifications. Call the DeviceChangeNotifier.Start() method at the start of your program. Call DeviceChangeNotifier.Stop() at the end of your program. Beware that the DeviceNotify event is raised on a background thread, be sure to lock as needed to keep your code thread-safe.
using System;
using System.Windows.Forms;
using System.Threading;
class DeviceChangeNotifier : Form {
public delegate void DeviceNotifyDelegate(Message msg);
public static event DeviceNotifyDelegate DeviceNotify;
private static DeviceChangeNotifier mInstance;
public static void Start() {
Thread t = new Thread(runForm);
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
}
public static void Stop() {
if (mInstance == null) throw new InvalidOperationException("Notifier not started");
DeviceNotify = null;
mInstance.Invoke(new MethodInvoker(mInstance.endForm));
}
private static void runForm() {
Application.Run(new DeviceChangeNotifier());
}
private void endForm() {
this.Close();
}
protected override void SetVisibleCore(bool value) {
// Prevent window getting visible
if (mInstance == null) CreateHandle();
mInstance = this;
value = false;
base.SetVisibleCore(value);
}
protected override void WndProc(ref Message m) {
// Trap WM_DEVICECHANGE
if (m.Msg == 0x219) {
DeviceNotifyDelegate handler = DeviceNotify;
if (handler != null) handler(m);
}
base.WndProc(ref m);
}
}
I have a working USB communication class that implements device change notification in a slightly different way if anyone is interested. It's pretty compact (w/o the comments) and doesn't rely on Threading or the OnSourceInitialized and HwndHandler stuff in the client. Also, you do not need a Form or Window as mentioned. Any type where you can override WndProc() can be used. I use a Control.
The sample contains only code needed for notification and nothing else. The sample code is C++/CLI and although I don't subscribe to the practice of putting executable code in header files, for the sake of brevity, I do so here.
#pragma once
#include <Windows.h> // Declares required datatypes.
#include <Dbt.h> // Required for WM_DEVICECHANGE messages.
#include <initguid.h> // Required for DEFINE_GUID definition (see below).
namespace USBComms
{
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Windows;
using namespace System::Windows::Forms;
// This function is required for receieving WM_DEVICECHANGE messages.
// Note: name is remapped "RegisterDeviceNotificationUM"
[DllImport("user32.dll" , CharSet = CharSet::Unicode, EntryPoint="RegisterDeviceNotification")]
extern "C" HDEVNOTIFY WINAPI RegisterDeviceNotificationUM(
HANDLE hRecipient,
LPVOID NotificationFilter,
DWORD Flags);
// Generic guid for usb devices (see e.g. http://msdn.microsoft.com/en-us/library/windows/hardware/ff545972%28v=vs.85%29.aspx).
// Note: GUIDs are device and OS specific and may require modification. Using the wrong guid will cause notification to fail.
// You may have to tinker with your device to find the appropriate GUID. "hid.dll" has a function `HidD_GetHidGuid' that returns
// "the device interfaceGUID for HIDClass devices" (see http://msdn.microsoft.com/en-us/library/windows/hardware/ff538924%28v=vs.85%29.aspx).
// However, testing revealed it does not always return a useful value. The GUID_DEVINTERFACE_USB_DEVICE value, defined as
// {A5DCBF10-6530-11D2-901F-00C04FB951ED}, has worked with cell phones, thumb drives, etc. For more info, see e.g.
// http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx.
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
/// <summary>
/// Declare a delegate for the notification event handler.
/// </summary>
/// <param name="sender">The object where the event handler is attached.</param>
/// <param name="e">The event data.</param>
public delegate void NotificationEventHandler(Object^ sender, EventArgs^ e);
/// <summary>
/// Class that generetaes USB Device Change notification events.
/// </summary>
/// <remarks>
/// A Form is not necessary. Any type wherein you can override WndProc() can be used.
/// </remarks>
public ref class EventNotifier : public Control
{
private:
/// <summary>
/// Raises the NotificationEvent.
/// </summary>
/// <param name="e">The event data.</param>
void RaiseNotificationEvent(EventArgs^ e) {
NotificationEvent(this, e);
}
protected:
/// <summary>
/// Overrides the base class WndProc method.
/// </summary>
/// <param name="message">The Windows Message to process. </param>
/// <remarks>
/// This method receives Windows Messages (WM_xxxxxxxxxx) and
/// raises our NotificationEvent as appropriate. Here you should
/// add any message filtering (e.g. for the WM_DEVICECHANGE) and
/// preprocessing before raising the event (or not).
/// </remarks>
virtual void WndProc(Message% message) override {
if(message.Msg == WM_DEVICECHANGE)
{
RaiseNotificationEvent(EventArgs::Empty);
}
__super::WndProc(message);
}
public:
/// <summary>
/// Creates a new instance of the EventNotifier class.
/// </summary>
EventNotifier(void) {
RequestNotifications(this->Handle); // Register ourselves as the Windows Message processor.
}
/// <summary>
/// Registers an object, identified by the handle, for
/// Windows WM_DEVICECHANGE messages.
/// </summary>
/// <param name="handle">The object's handle.</param>
bool RequestNotifications(IntPtr handle) {
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_reserved = 0;
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
return RegisterDeviceNotificationUM((HANDLE)handle, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE) != NULL;
}
/// <summary>
/// Defines the notification event.
/// </summary>
virtual event NotificationEventHandler^ NotificationEvent;
};
}
Then, in the 'receiver' (the object that subscribes to and consumes our NotificationEvent), all you have to do is:
void Receiver::SomeFunction(void)
{
USBComms::EventNotifier usb = gcnew USBComms::EventNotifier();
usb->NotificationEvent += gcnew USBComms::NotificationEventHandler(this, &Receiver::USBEvent);
}
void Receiver::USBEvent(Object^ sender, EventArgs^ e)
{
// Handle the event notification as appropriate.
}
In Windows CE / Windows Mobile / SmartDevice projects, the standard Form does not provide an override to the WndProc method, but this can be accomplished by making a class based on Microsoft.WindowsCE.Forms.MessageWindow, creating a constructor that takes a form, hold that form in a local variable so that a method on that form can be called whenever the message is detected. Here's a scaled down sample to illustrate. Hope this is helpful to someone in the CE / Windows Mobile world.
public class MsgWindow : Microsoft.WindowsCE.Forms.MessageWindow {
public const int WM_SER = 0x500;
public const int WM_SER_SCANDONE = WM_SER + 0;
frmMain msgform { get; set; }
public MsgWindow(frmMain msgform) {
this.msgform = msgform;
}
protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message m) {
switch (m.Msg) {
case WM_SER_SCANDONE:
this.msgform.RespondToMessage(WM_SER_SCANDONE);
break;
default:
break;
}
base.WndProc(ref m);
}
}
public partial class frmMain : Form {
public frmMain() {
InitializeComponent();
}
public void RespondToMessage(int nMsg) {
try {
switch (nMsg) {
case MsgWindow.WM_SER_SCANDONE:
// do something here based on the message
break;
default:
break;
}
} catch (Exception ex) {
MessageBox.Show(string.Format("{0} - {1}", ex.Message, ex.ToString()), "RespondToMessage() Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
// throw;
}
}
}