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
Related
I am a electronic student, I am developing a GUI with Visual Studio; so I found examples and information in the next page.
http://www.voip-sip-sdk.com/
my principal class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Ozeki.Media;
using Ozeki.VoIP;
using Ozeki.Common;
namespace Consola2
{
/// <summary>
/// Basic softphone logic.
/// </summary>
/// <remarks>
/// This class is used to introduce how to declare, define and initialize a softphone,
/// how to handle some of it's events, and use some of it's functions.
/// The Program.cs uses this class to create a softphone,
/// uses the functions and events declared here as public.
/// </remarks>
class Softphone
{
ISoftPhone _softphone; // softphone object
IPhoneLine _phoneLine; // phone line object
IPhoneCall _call; // the call object
Microphone _microphone;
Speaker _speaker;
MediaConnector _connector; // connects the devices to each other (eg. microphone, speaker, mediaSender, mediaReceiver)
PhoneCallAudioSender _mediaSender; // after connected with the microphone, this will be attached to the call
PhoneCallAudioReceiver _mediaReceiver; // after connected with the speaker, this will be attached to the call
bool _incomingCall; // indicates wheter we have an incoming call (so, the phone is ringing)
#region Events
/// <summary>
/// Occurs when an incoming call received.
/// </summary>
public event EventHandler IncomingCall;
/// <summary>
/// Occurs when the registration state of the phone line has changed.
/// </summary>
public event EventHandler<RegistrationStateChangedArgs> PhoneLineStateChanged;
/// <summary>
/// Occurs when the state of the call has changed.
/// </summary>
public event EventHandler<CallStateChangedArgs> CallStateChanged;
#endregion
/// <summary>
/// Default constructor, initalizes the softphone with deafult parameters.
/// </summary>
public Softphone()
{
_softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);
_microphone = Microphone.GetDefaultDevice();
_speaker = Speaker.GetDefaultDevice();
_connector = new MediaConnector();
_mediaSender = new PhoneCallAudioSender();
_mediaReceiver = new PhoneCallAudioReceiver();
_incomingCall = false;
}
/// <summary>
/// Registers the SIP account to the PBX.
/// Calls cannot be made while the SIP account is not registered.
/// If the SIP account requires no registration, the RegisterPhoneLine() must be called too to register the SIP account to the ISoftPhone.
/// </summary>
public void Register(bool registrationRequired, string displayName, string userName, string authenticationId, string registerPassword, string domainHost, int domainPort)
{
try
{
// We need to handle the event, when we have an incoming call.
_softphone.IncomingCall += softphone_IncomingCall;
// To register to a PBX, we need to create a SIP account
var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost, domainPort);
//Console.WriteLine("\nCreating SIP account {0}", account);
// With the SIP account and the NAT configuration, we can create a phoneline.
_phoneLine = _softphone.CreatePhoneLine(account);
//Console.WriteLine("Phoneline created.");
// The phoneline has states, we need to handle the event, when it is being changed.
_phoneLine.RegistrationStateChanged += phoneLine_PhoneLineStateChanged;
// If our phoneline is created, we can register that.
_softphone.RegisterPhoneLine(_phoneLine);
// For further information about the calling of the ConnectMedia(), please check the implementation of this method.
ConnectMedia();
}
catch (Exception ex)
{
Console.WriteLine("Error during SIP registration: " + ex);
}
}
/// <summary>
/// This will be called when the registration state of the phone line has changed.
/// </summary>
private void phoneLine_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
{
DispatchAsync(() =>
{
var handler = PhoneLineStateChanged;
if (handler != null)
handler(this, e);
});
}
/// <summary>
/// Starts the capturing and playing audio/video devices.
/// Other devices can be used (and started), for example: WebCamera or WaveStreamPlayback.
/// </summary>
private void StartDevices()
{
if (_microphone != null)
{
_microphone.Start();
}
if (_speaker != null)
{
_speaker.Start();
}
}
/// <summary>
/// Stops the capturing and playing audio/video devices.
/// Other devices can be stopped, for example: WebCamera.
/// </summary>
private void StopDevices()
{
if (_microphone != null)
{
_microphone.Stop();
}
if (_speaker != null)
{
_speaker.Stop();
}
}
#region Media handling guide
/*
To send our voice through the microphone to the other client's speaker, we need to connect them.
We send our voice through the mediaSender, and we get the other client's voice through the mediaSender to our speaker object.
To disconnect these handlers, we will use the DisconnectMedia() method.
It is possible to use other mediahandlers with the connector, for example we can connect a WaveStreamPlayback or an MP3StreamPlayback object to the MediaSender, so we can play music/voice
during the call. For exmaple: when can create an IVR (Interactive Voice Response), we can create voice recorder etc.
For example:
We can connect an .mp3 file player (which plays an mp3 file into the voice call) by the "connector.Connect(Mp3StreamPlayback, mediaSender); " line.
(We should also create an MP3StreamPlayback object: "MP3StreamPlayback Mp3StreamPlayback; "
and we need to tell to this object the details (what to play into the speaker, etc.))
*/
#endregion
/// <summary>
/// Connects the audio handling devices to each other.
/// The audio data will flow from the source to the destination.
/// </summary>
private void ConnectMedia()
{
if (_microphone != null)
{
_connector.Connect(_microphone, _mediaSender);
}
if (_speaker != null)
{
_connector.Connect(_mediaReceiver, _speaker);
}
}
/// <summary>
/// Disconnects the audio handling devices from each other.
/// </summary>
private void DisconnectMedia()
{
if (_microphone != null)
{
_connector.Disconnect(_microphone, _mediaSender);
}
if (_speaker != null)
{
_connector.Disconnect(_mediaReceiver, _speaker);
}
// You can close all of the connections by using: connector.Dispose();
}
/// <summary>
/// Subscribes to the events of a call to receive notifications such as the state of the call has changed.
/// In this sample subscribes only to the state changed and error occurred events.
/// </summary>
private void WireUpCallEvents()
{
_call.CallStateChanged += (call_CallStateChanged);
}
/// <summary>
/// Unsubscribes from the events of a call.
/// </summary>
private void WireDownCallEvents()
{
_call.CallStateChanged -= (call_CallStateChanged);
}
/// <summary>
/// This will be called when an incoming call received.
/// To receive notifications from the call (eg. ringing), the program need to subscribe to the events of the call.
/// </summary>
private void softphone_IncomingCall(object sender, VoIPEventArgs<IPhoneCall> e)
{
_call = e.Item;
WireUpCallEvents();
_incomingCall = true;
DispatchAsync(() =>
{
var handler = IncomingCall;
if (handler != null)
handler(this, EventArgs.Empty);
});
}
/// <summary>
/// This will be called when the state of the call call has changed.
/// </summary>
/// <remarks>
/// In this sample only three states will be handled: Answered, InCall, Ended
///
/// Answered: when the call has been answered, the audio devices will be started and attached to the call.
/// It is required to comminicate with the other party and hear them.
/// The devices are connected at softphone initialization time,
/// so no need to connect them every time when a call is being answered.
///
/// InCall: when the call is in an active state, the audio deveices will be started.
///
/// Ended: when the call ends, the audio devices will be stopped and detached from the call.
/// </remarks>
private void call_CallStateChanged(object sender, CallStateChangedArgs e)
{
// the call has been answered
if (e.State == CallState.Answered)
{
StartDevices();
_mediaReceiver.AttachToCall(_call);
_mediaSender.AttachToCall(_call);
}
// the call is in active communication state
// IMPORTANT: this state can occur multiple times. for example when answering the call or the call has been taken off hold.
if (e.State == CallState.InCall)
{
StartDevices();
}
// the call has ended
if (e.State.IsCallEnded())
{
if (_call != null)
{
CallFinished();
}
}
DispatchAsync(() =>
{
var handler = CallStateChanged;
if (handler != null)
handler(this, e);
});
}
/// <summary>
/// Starts calling the specified number.
/// In this sample an outgoing call can be made if there is no current call (outgoing or incoming) on the phone line.
/// </summary>
public void StartCall(string numberToDial)
{
if (_call == null)
{
_call = _softphone.CreateCallObject(_phoneLine, numberToDial);
WireUpCallEvents();
// To make a call simply call the Start() method of the call object.
_call.Start();
}
}
/// <summary>
/// Answers the current incoming call.
/// </summary>
public void AcceptCall()
{
// when the value of the incomingCall member is true, there is an incoming call
if (_incomingCall == true)
{
_incomingCall = false;
***_call.Answer();***
}
}
/// <summary>
/// Hangs up the current call.
/// </summary>
public void HangUp()
{
if (_call != null)
{
_call.HangUp();
_call = null;
}
}
/// <summary>
/// If the call ends, we won't need our speaker and microphone anymore to communicate,
/// until we enter into a call again, so we are calling the StopDevices() method.
/// The mediaHandlers are getting detached from the call object
/// (since we are not using our microphone and speaker, we have no media to send).
/// We won't need the call's events anymore, becouse our call is about to be ended,
/// and with setting the call to null, we are ending it.
/// </summary>
public void CallFinished()
{
StopDevices();
_mediaReceiver.Detach();
_mediaSender.Detach();
WireDownCallEvents();
_call = null;
}
/// <summary>
/// This method is used to solve the task blockings.
/// </summary>
private void DispatchAsync(Action action)
{
var task = new WaitCallback(o => action.Invoke());
ThreadPool.QueueUserWorkItem(task);
}
}
}
My GUI code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using Ozeki.VoIP;
namespace Consola2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private static Softphone _mySoftphone; // softphone object
/// <summary>
/// The entry point of the program.
/// - initializes the softphone
/// - shows a greeting message
/// - registers the SIP account
/// </summary>
///
private static void InitSoftphone()
{
_mySoftphone = new Softphone();
_mySoftphone.PhoneLineStateChanged += mySoftphone_PhoneLineStateChanged;
_mySoftphone.CallStateChanged += mySoftphone_CallStateChanged;
_mySoftphone.IncomingCall += mySoftphone_IncomingCall;
}
/// <summary>
/// This will be called when the registration state of the phone line has changed.
/// </summary>
static void mySoftphone_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
{
Console.WriteLine("Phone line state changed: {0}", e.State);
if (e.State == RegState.Error || e.State == RegState.NotRegistered)
{
MessageBox.Show("Datos Invalidos", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
else if (e.State == RegState.RegistrationSucceeded)
{
MessageBox.Show("¡Registro Exitoso!");
//StartToDial();
}
}
/// <summary>
/// This will be called when an incoming call received.
/// In this sample when an incoming call receveived, it will be answered automatically.
/// </summary>
static void mySoftphone_IncomingCall(object sender, EventArgs e)
{
MessageBox.Show("Llamada entrante");
_mySoftphone.AcceptCall();
MessageBox.Show("Llamada aceptada:");
}
/// <summary>
/// This will be called when the state of the call has changed. (eg. ringing, answered, rejected)
/// </summary>
private static void mySoftphone_CallStateChanged(object sender, CallStateChangedArgs e)
{
Console.WriteLine("Call state changed: {0}", e.State);
if (e.State.IsCallEnded())
{
MessageBox.Show("Llamada terminada:");
}
if (e.State == CallState.Error)
{
Console.WriteLine("Call error occured. {0}", e.Reason);
}
}
public MainWindow()
{
InitializeComponent();
InitSoftphone();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
string id = textBox1.Text;
string user = textBox2.Text;
string displayed = textBox3.Text;
string pass = textBox4.Text;
string domain = textBox5.Text;
int port = Convert.ToInt32(textBox6.Text);
bool check = false;
if (checkBox1.IsChecked == true)
{
check = true;
}
_mySoftphone.Register(check, id, user, displayed, pass, domain, port);
}
private void Llamar_Click(object sender, RoutedEventArgs e)
{
string numero = textBox7.Text;
_mySoftphone.StartCall(numero);
}
private void button2_Click(object sender, RoutedEventArgs e)
{
_mySoftphone.AcceptCall();
}
}
}
With that, I can register into a PBX and make calls , the problem is that when you receive calls an error message appears "NullReferenceException was unhandled" In my class principal "AcceptCall()" method in the line "_call,Answer();".
Where is the problem? i'm not sure about de references with the "Ozeki" library.
I appreciate your help, or an example to make and receive calls.
Thanks.
Your call button (Llamar) instantiates _call inside StartCall. However, when AcceptCall is run, _call is still null, and cannot be used.
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.
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;
}
}
}
I am relatively new to C#/.Net. I'm developing a desktop application that requires multi threading. I came up with the following pattern below as a base. I was wondering if anyone could point out how to make it better in terms of coding, being thread safe, and being efficient.
Hopefully this makes some sense.
public abstract class ThreadManagerBase
{
// static class variables
private static ThreadManagerBase instance = null;
private static BackgroundWorker thread = null;
private static ProgressBarUIForm progress = null;
/// <summary>
/// Create a new instance of this class. The internals are left to the derived class to figure out.
/// Only one instance of this can run at any time. There should only be the main thread and this thread.
/// </summary>
public abstract static ThreadManagerBase NewInstance();
/// <summary>
/// Clears the instance.
/// </summary>
public static void ClearInstance()
{
instance = null;
}
/// <summary>
/// Initializes the background worker with some presets.
/// Displays progress bar.
/// </summary>
private abstract static void InitializeThread()
{
thread = new BackgroundWorker();
thread.WorkerReportsProgress = true;
thread.WorkerSupportsCancellation = true;
thread.DoWork += new DoWorkEventHandler(thread_DoWork);
thread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(thread_RunWorkerCompleted);
thread.ProgressChanged += new ProgressChangedEventHandler(thread_ProgressChanged);
thread.RunWorkerAsync();
progress = new ProgressBarUIForm();
progress.EnableCancelButton = true;
progress.UserCanceled += new EventHandlerCancelClicked(progress_UserCanceled);
progress.ShowDialog();
thread.Dispose();
thread = null;
}
private static void progress_UserCanceled(bool userCanceled)
{
thread.CancelAsync();
}
private static void thread_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progress.SetProgressLevel = e.ProgressPercentage;
progress.SetProgressMessage = e.UserState.ToString();
}
private static void thread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progress.Close();
progress = null;
}
private static void thread_DoWork(object sender, DoWorkEventArgs e)
{
ProcessWork();
}
private abstract static void ProcessWork()
{
// do actuall stuff here.
// the derived classes will take care of the plumbing.
}
}
Have you looked into the Microsoft Parallel Extensions to .NET Framework 3.5? It's a pretty good library that takes a lot of the work out of threading.
There are also a lot of articles on MSDN about threading patterns that you should research too. Threading can get really complicated really fast. It's nice to have someone else to have though of all the important stuff that can go wrong, and simplify it down to a library or a pattern. Of course, there's danger in that too if you don't understand the gotchas of any particular solution. So, make sure you research well whatever solution you choose.
I don't see a good reason to create this abstraction over BackgroundWorker.
If you insist, just a warning: I'm not sure if it changed in later releases, but in NET 2.0, it wasn't possible to really cancel the DoWork handler (unless it checked once in a while if it was asked to stop). Read here for a solution.
I have done something similar to this. There is a good reason if you do have multiple tasks that you want to perform, but you dont want to have BackgroundWorker code replicated through the entire project. I dont have the progressbar tied to the actual base class, I just have that in the main form. Here is the solution I came up with:
The following is the base class:
public abstract class Operation
{
#region public Event Handlers
///
/// The event that updates the progress of the operation
///
public event OperationProgressChangedEventHandler OperationProgressChanged;
///
/// The event that notifies that the operation is complete (and results)
///
public event OperationCompletedEventHandler OperationCompleted;
#endregion
#region Members
// Whether or not we can cancel the operation
private bool mWorkerSupportsCancellation = false;
// The task worker that handles running the operation
private BackgroundWorker mOperationWorker;
// The operation parameters
private object[] mOperationParameters;
#endregion
///
/// Base class for all operations
///
public Operation(params object[] workerParameters)
{
mOperationParameters = workerParameters;
// Setup the worker
SetupOperationWorker();
}
#region Setup Functions
///
/// Setup the background worker to run our Operations
///
private void SetupOperationWorker()
{
mOperationWorker = new BackgroundWorker();
mOperationWorker.WorkerSupportsCancellation = mWorkerSupportsCancellation;
mOperationWorker.WorkerReportsProgress = true;
mOperationWorker.WorkerSupportsCancellation = true;
mOperationWorker.DoWork += new DoWorkEventHandler(OperationWorkerDoWork);
mOperationWorker.ProgressChanged += new ProgressChangedEventHandler(OperationWorkerProgressChanged);
mOperationWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OperationWorkerRunWorkerCompleted);
}
#endregion
#region Properties
///
/// Whether or not to allow the user to cancel the operation
///
public bool CanCancel
{
set
{
mWorkerSupportsCancellation = value;
}
}
#endregion
#region Operation Start/Stop Details
///
/// Start the operation with the given parameters
///
/// The parameters for the worker
public void StartOperation()
{
// Run the worker
mOperationWorker.RunWorkerAsync(mOperationParameters);
}
///
/// Stop the operation
///
public void StopOperation()
{
// Signal the cancel first, then call cancel to stop the test
if (IsRunning())
{
// Sets the backgroundworker CancelPending to true, so we can break
// in the sub classes operation
mOperationWorker.CancelAsync();
// This allows us to trigger an event or "Set" if WaitOne'ing
Cancel();
// Wait for it to actually stop before returning
while (IsRunning())
{
Application.DoEvents();
}
}
}
///
/// Whether or not the operation is currently running
///
///
public bool IsRunning()
{
return mOperationWorker.IsBusy;
}
#endregion
#region BackgroundWorker Events
///
/// Fires when the operation has completed
///
///
///
private void OperationWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Allow the sub class to clean up anything that might need to be updated
Clean();
// Notify whoever is register that the operation is complete
if (OperationCompleted != null)
{
OperationCompleted(e);
}
}
///
/// Fires when the progress needs to be updated for a given test (we might not care)
///
///
///
private void OperationWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Notify whoever is register of what the current percentage is
if (OperationProgressChanged != null)
{
OperationProgressChanged(e);
}
}
///
/// Fires when we start the operation (this does the work)
///
///
///
private void OperationWorkerDoWork(object sender, DoWorkEventArgs e)
{
// Run the operation
Run(sender, e);
}
#endregion
#region Abstract methods
///
/// Abstract, implemented in the sub class to do the work
///
///
///
protected abstract void Run(object sender, DoWorkEventArgs e);
///
/// Called at the end of the test to clean up anything (ex: Disconnected events, etc)
///
protected abstract void Clean();
///
/// If we are waiting on something in the operation, this will allow us to
/// stop waiting (ex: WaitOne).
///
protected abstract void Cancel();
#endregion
}
The following is an example test class for the example I posted:
class TestOperation : Operation
{
AutoResetEvent mMsgRec;
public TestOperation(params object[] workerParameters)
: base(workerParameters)
{
CanCancel = true;
mMsgRec = new AutoResetEvent(false);
//mSomeEvent += DoSomething();
}
protected override void Cancel()
{
mMsgRec.Set();
}
protected override void Clean()
{
//mSomeEvent -= DoSomething();
}
protected override void Run(object sender, DoWorkEventArgs e)
{
BackgroundWorker bg = (BackgroundWorker)sender;
for (int i = 0; !bg.CancellationPending && (i < 90); i++)
{
bg.ReportProgress(i);
Thread.Sleep(100);
}
for (int i = 90; !bg.CancellationPending && (i < 100); i++)
{
mMsgRec.WaitOne(2000, false);
bg.ReportProgress(i);
}
if (bg.CancellationPending)
{
e.Cancel = true;
}
else
{
e.Result = "Complete"; // Or desired result
}
}
}
And here is what the main form would look like (very basic example):
public partial class Form1 : Form
{
TestOperation t;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
t = new TestOperation();
t.CanCancel = true;
t.OperationProgressChanged += new OperationProgressChangedEventHandler(t_OperationProgressChanged);
t.OperationCompleted += new OperationCompletedEventHandler(t_OperationCompleted);
t.StartOperation();
}
void t_OperationCompleted(RunWorkerCompletedEventArgs e)
{
progressBar1.Value = 0;
}
void t_OperationProgressChanged(ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void button2_Click(object sender, EventArgs e)
{
t.StopOperation();
}
}
I'm currently investigation Threadmare over at http://sklobovsky.nstemp.com/community/threadmare/threadmare.htm for a C# project. It looks very, very useful. It's in Delphi, but the principles apply to any language that can handle events.
You don't need a BackgroundWorker unless you want to be spoonfed, normal threads are perfectly acceptable, as long as you follow the rules.