Apologies in advance for rather long post and lot of code.
My application has a timed autosave feature. Users asked that I provide a visual indicator of how much time is left. I did some research on count down timers and eventually wrote the class below:
public class CountDownTimer
{
private Timer timer;
private int remaining;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Count down ticked delegate. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
///
/// <param name="remaining"> The remaining. </param>
/// <param name="maximum"> The maximum. </param>
////////////////////////////////////////////////////////////////////////////////////////////////////
public delegate void CountDownTickedDelegate(int remaining, int maximum);
/// <summary> Event queue for all listeners interested in Ticked event. </summary>
public event CountDownTickedDelegate Ticked;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Count down percent delegate. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
///
/// <param name="percent"> The percent. </param>
////////////////////////////////////////////////////////////////////////////////////////////////////
public delegate void CountDownPercentDelegate(int percent);
/// <summary> Event queue for all listeners interested in Percent events. </summary>
public event CountDownPercentDelegate Percent;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Count down done delegate. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public delegate void CountDownDoneDelegate();
/// <summary> Event queue for all listeners interested in Done events. </summary>
public event CountDownDoneDelegate Done;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Gets or sets the maximum value to count down from </summary>
///
/// <value> The maximum value. </value>
////////////////////////////////////////////////////////////////////////////////////////////////////
public int Maximum
{
get;
set;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Gets or sets a value indicating whether the timer is Paused. </summary>
///
/// <value> true if paused, false if not. </value>
////////////////////////////////////////////////////////////////////////////////////////////////////
public bool Paused
{
get;
set;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Starts this CountDownTimer. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public void Start()
{
timer = new Timer {
Interval = 1000
};
timer.Tick += onTimerTick;
timer.Enabled = true;
remaining = Maximum;
Paused = false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Stops this CountDownTimer. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public void Stop()
{
if (timer == null)
{
return;
}
Paused = true;
timer.Enabled = false;
timer = null;
if (Percent != null)
{
Percent(100);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Resets and restarts this CountDownTimer. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public void Reset()
{
Stop();
Start();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Handles the timer tick event. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
///
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Event information to send to registered event handlers. </param>
////////////////////////////////////////////////////////////////////////////////////////////////////
private void onTimerTick(object sender, EventArgs e)
{
if (remaining > 0)
{
if (Ticked != null)
{
Ticked(remaining, Maximum);
}
if (Percent != null)
{
int percent = remaining * 100 / Maximum;
Percent(percent);
}
if (!Paused)
{
remaining--;
}
else
{
remaining = Maximum;
}
}
else
{
Stop();
if (Done != null)
{
Done();
}
}
}
}
I am using a Timer and each time it 'fires' I decrement a counter. Each decrement kicks off an event so that my Form can present it visually. When the counter reaches zero another event kicks off the autosave.
There are a few other bits included to allow the autosave to be restarted if the user manually saves or if they open a new project.
It seemed to work for me. However a user is reporting that the longer the timer runs the shorter the interval between autosaves. I set the timer to tick every second and my investigations show that it runs at twice the speed. So if the counter is set to 60 (seconds) then it runs down every 30. I cannot replicate the behavior seen by the user but his log certainly shows things running much too fast.
This is in the same thread as the main app - is that likely to be an issue. All my tests so far have not turned anything up other than the tick seems to fire twice in a row every third time or so.
Many thanks in advance for any insight.
One problem I see, is that if CountDownTimer.Start() is called two (or even multiple) times, without the appropriate CountDownTimer.Stop() calls, you end up with two or more activated instances of your Timer object, both invoking your onTimerTick() event handler.
This could cause your described effect, as all running Timer instances decrease the remaining iterations separately.
Is that possible with your calling code?
EDIT:
As a workaround I would suggest, you call Stop() from within Start(). Or even better, you do not recreate the Timer object for every new count down action. Create the Timer object in the constructor and only manipulate its properties.
It is also not a bad idea to remove the onTimerTick() event handler from the timer instance when you dispose the Timer object. Otherwise the GC can not collect the timer instance as it still holds a reference to its CountDownTimer instance.
Timer's Elapsed event keeps firing after every interval. You need to stop first the timer in Elapsed event handler method and start it again when leaving method. Otherwise if your code in event handler method takes about 1 second then Elapsed event will raise again. e.g.
private void onTimerTick(object sender, EventArgs e)
{
try
{
timer.Stop();
//Do your stuff here
}
finally { timer.Start(); }
}
Related
i'm new with xamarin forms. I'm writing an app and i need to create a function that allow call api continuously to check the change of data, if have any change, i will handle something.
I'm looking for the solution but nothing, please help me :(
Thread is an idea?
Okay so first of all you need to poll the API in order to receive the data that you need to check. To do this you can implement my PollingTimer.cs class:
using System;
using System.Threading;
using Xamarin.Forms;
namespace CryptoTracker.Helpers
{
/// <summary>
/// This timer is used to poll the middleware for new information.
/// </summary>
public class PollingTimer
{
private readonly TimeSpan timespan;
private readonly Action callback;
private CancellationTokenSource cancellation;
/// <summary>
/// Initializes a new instance of the <see cref="T:CryptoTracker.Helpers.PollingTimer"/> class.
/// </summary>
/// <param name="timespan">The amount of time between each call</param>
/// <param name="callback">The callback procedure.</param>
public PollingTimer(TimeSpan timespan, Action callback)
{
this.timespan = timespan;
this.callback = callback;
this.cancellation = new CancellationTokenSource();
}
/// <summary>
/// Starts the timer.
/// </summary>
public void Start()
{
CancellationTokenSource cts = this.cancellation; // safe copy
Device.StartTimer(this.timespan,
() => {
if (cts.IsCancellationRequested) return false;
this.callback.Invoke();
return true; // or true for periodic behavior
});
}
/// <summary>
/// Stops the timer.
/// </summary>
public void Stop()
{
Interlocked.Exchange(ref this.cancellation, new CancellationTokenSource()).Cancel();
}
}
}
Now that you have added a polling timer to your project, you must now go to the content page that you wish to poll from. Here is the pseudo code for what your content page should look like:
namespace YourApp.Views
{
public class MainPage : ContentPage
{
PollingTimer timer;
public MainPage ()
{
//PUT UI CODE HERE
Content = layout;
//Instantiate Polling timer to call handleaction every 5 seconds
timer = new PollingTimer(TimeSpan.FromSeconds(5), HandleAction);
}
/// <summary>
/// When the page enters the users view, this procedure is called.
/// </summary>
protected override void OnAppearing()
{
base.OnAppearing();
//Handle action and start your timer
HandleAction();
timer.Start();
}
/// <summary>
/// When the page disappears from the users view this procedure is called.
/// </summary>
protected override void OnDisappearing()
{
base.OnDisappearing();
//Stop your timer
timer.Stop(); //Stop the timer
}
/// <summary>
/// Callback for the timer.
/// </summary>
void HandleAction()
{
//Make call to your api to get data
//Compare data with data you currently have
// Do whatever you want.
}
I hope this helps you. Let me know if you need any more help :)
You can use Timer Class for you issue.
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
When trying to run the following program code, this error occurred;
"Input string was not in a correct format."
The project breaks at the point below.
int bytesSentSpeed = (int)(interfaceStats.BytesSent - double.Parse(lblBytesSent.Text)) / 1024;
Code;
using System;
using System.Net.NetworkInformation;
using System.Windows.Forms;
namespace InterfaceTrafficWatch
{
/// <summary>
/// Network Interface Traffic Watch
/// by Mohamed Mansour
///
/// Free to use under GPL open source license!
/// </summary>
public partial class MainForm : Form
{
/// <summary>
/// Timer Update (every 1 sec)
/// </summary>
private const double timerUpdate = 1000;
/// <summary>
/// Interface Storage
/// </summary>
private NetworkInterface[] nicArr;
/// <summary>
/// Main Timer Object
/// (we could use something more efficient such
/// as interop calls to HighPerformanceTimers)
/// </summary>
private Timer timer;
/// <summary>
/// Constructor
/// </summary>
public MainForm()
{
InitializeComponent();
InitializeNetworkInterface();
InitializeTimer();
}
/// <summary>
/// Initialize all network interfaces on this computer
/// </summary>
private void InitializeNetworkInterface()
{
// Grab all local interfaces to this computer
nicArr = NetworkInterface.GetAllNetworkInterfaces();
// Add each interface name to the combo box
for (int i = 0; i < nicArr.Length; i++)
cmbInterface.Items.Add(nicArr[i].Name);
// Change the initial selection to the first interface
cmbInterface.SelectedIndex = 0;
}
/// <summary>
/// Initialize the Timer
/// </summary>
private void InitializeTimer()
{
timer = new Timer();
timer.Interval = (int)timerUpdate;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
/// <summary>
/// Update GUI components for the network interfaces
/// </summary>
private void UpdateNetworkInterface()
{
// Grab NetworkInterface object that describes the current interface
NetworkInterface nic = nicArr[cmbInterface.SelectedIndex];
// Grab the stats for that interface
IPv4InterfaceStatistics interfaceStats = nic.GetIPv4Statistics();
// Calculate the speed of bytes going in and out
// NOTE: we could use something faster and more reliable than Windows Forms Tiemr
// such as HighPerformanceTimer http://www.m0interactive.com/archives/2006/12/21/high_resolution_timer_in_net_2_0.html
int bytesSentSpeed = (int)(interfaceStats.BytesSent - double.Parse(lblBytesSent.Text)) / 1024;
int bytesReceivedSpeed = (int)(interfaceStats.BytesReceived - double.Parse(lblBytesReceived.Text)) / 1024;
// Update the labels
lblSpeed.Text = nic.Speed.ToString();
lblInterfaceType.Text = nic.NetworkInterfaceType.ToString();
lblSpeed.Text = nic.Speed.ToString();
lblBytesReceived.Text = interfaceStats.BytesReceived.ToString();
lblBytesSent.Text = interfaceStats.BytesSent.ToString();
lblUpload.Text = bytesSentSpeed.ToString() + " KB/s";
lblDownload.Text = bytesReceivedSpeed.ToString() + " KB/s";
}
/// <summary>
/// The Timer event for each Tick (second) to update the UI
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void timer_Tick(object sender, EventArgs e)
{
UpdateNetworkInterface();
}
}
}
The exception means the string you are trying to parse does not have a valid double value.
Just do
double bytesSent = 0.0;
if(double.TryParse(lblBytesSent.Text, out bytesSent))
{
int bytesSentSpeed = (int)(interfaceStats.BytesSent - bytesSent) / 1024;
}
Debug and check what the value of lblBytesSent is on that line - it's probably not a number for one reason or another.
I have attempted to create a derived class of Timer that allows for a 'Pause' latch to be set to keep the worker thread from reactivating the timer. However, Elapsed events are continued to be raised when AutoReset is set to false and the Enabled accessor appears to be doing it's job in preventing the Enabled property of the base class from being modified once the Paused variable is set.
Why is this happening or what strategies should I use to further understand what interactions are actually happening here?
I have attached the implementation of the derived class below.
using System.Timers
class PauseableTimer : Timer
{
public bool Paused;
new public bool Enabled
{
get
{
return base.Enabled;
}
set
{
if (Paused)
{
if (!value) base.Enabled = false;
}
else
{
base.Enabled = value;
}
}
}
}
Example illustrating problem.
class Program
{
private static PauseableTimer _pauseableTimer;
private static int _elapsedCount;
static void Main(string[] args)
{
_pauseableTimer = new PauseableTimer(){AutoReset = false,Enabled = false,Paused = false};
_pauseableTimer.Elapsed += pauseableTimer_Elapsed;
_pauseableTimer.Interval = 1;
_pauseableTimer.Enabled = true;
while(_elapsedCount<100)
{
if (_elapsedCount > 50) _pauseableTimer.Paused = true;
}
}
static void pauseableTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine(String.Format("this.Enabled:'{0}',Paused:'{1}',AutoReset:'{2}",_pauseableTimer.Enabled,_pauseableTimer.Paused,_pauseableTimer.AutoReset));
_elapsedCount++;
_pauseableTimer.Interval = _pauseableTimer.Interval == 1 ? 2 : 1; //This line breaks it.
_pauseableTimer.Enabled = true;
}
}
Relevant document, System.Timers.Timer.Interval
Note
If Enabled and AutoReset are both set to false, and the timer has previously been enabled, setting the Interval property causes the Elapsed event to be raised once, as if the Enabled property had been set to true. To set the interval without raising the event, you can temporarily set the AutoReset property to true.
The recommended solution of setting AutoReset to true does not solve the problem because there is an undocumented behavior of setting AutoReset to true during an event handler also allowing for an event to be fired.
The solution seems to be to build out the derived object to the point where you can keep any of the apparently many ways that an event can be fired again from happening.
Below is the implementation that I ended with.
public class PauseableTimer : Timer
{
private bool _paused;
public bool Paused
{
get { return _paused; }
set
{
Interval = _interval;
_paused = value;
}
}
new public bool Enabled
{
get
{
return base.Enabled;
}
set
{
if (Paused)
{
if (!value) base.Enabled = false;
}
else
{
base.Enabled = value;
}
}
}
private double _interval;
new public double Interval
{
get { return base.Interval; }
set
{
_interval = value;
if (Paused){return;}
if (value>0){base.Interval = _interval;}
}
}
public PauseableTimer():base(1){}
public PauseableTimer(double interval):base(interval){}
}
Everything is more complex in multithreading, I'm afraid. Assuming your code is working as you wish, there is a window where in-flight events can get raised after you reset the Enabled property. See this quote from the MSDN docs.
The signal to raise the Elapsed event
is always queued for execution on a
ThreadPool thread. This might result
in the Elapsed event being raised
after the Enabled property is set to
false. The code example for the Stop
method shows one way to work around
this race condition.
Another option is to suppress the event??? I can't explain what is going but the theory presented below should allow you to circumvent this little problem you have discussed. As Steve mentioned put a 'Watch and break point on the enabled property' that you are try set and make sure it is actually being set.
How would I tackle this:
Catch and check for the 'Enabled' property and remove '-=' the subscribing method (handler) as of when needed and then re-add '+=' it again when you do need handle the 'Elapsed' event.
I have used this style quite a few times on a few different WinForms project. If you don't want the 'Elapsed' event to be handled programmatically create a check for and remove it when a certain condition is met and then add it when the opposite condition is met.
if (paused) // determine pause logic to be true in here
{
timer.Elapsed -= ... // remove the handling method.
}
else
{
timer.Elapsed += ... // re-add it in again
}
The above code logic will allow you code to ignore the 'Elapsed' event ever time it is raised whilst the 'Paused' flag is true. I hope the above helps
In System.Timers.Timer the Elapsed event is added to the ThreadPool when the class is created. After that it is fired. The Enabled property can be false at that time. You can't do anything about that, but what you can do is test if the Enabled property is true when the Elapsed event fires. I do override the Enabled property to make this magic happening, as extra I also put an IsDisposed property in it:
public class DisposableTimer : System.Timers.Timer {
/// <summary>
/// override the Timer base class Enabled property
/// </summary>
/// <remarks>
/// the code in the Elapsed event should only be executed when the Enabled property is set to "true".
/// we cannot prevent that the Elapsed event is fired at the start, because its automatically put in the ThreadPool,
/// but we can prevent that the code in it can be executed when the Enabled property is "false".
/// </remarks>
private bool enabled;
public new bool Enabled
{
get
{
return enabled;
}
set
{
enabled = base.Enabled = value;
}
}
/// <summary>
/// count the heartbeats
/// </summary>
public int HeartbeatCounter { get; set; }
/// <summary>
/// name of timer
/// </summary>
public string TimerName { get; set; }
/// <summary>
/// show heartbeat on console
/// </summary>
public bool ShowHeartBeat { get; set; }
// type of entry in eventlog
public EventLogEntryType EventLogEntryType { get; set; }
// updated interval to process messages
public Func<double> UpdatebleInterval { get; set; }
/// <summary>
/// used eventlog
/// </summary>
public EventLog EventLog { get; set; }
/// <summary>
/// message logging
/// </summary>
/// <remarks>
/// this property needs to be dynamic because in
/// pda service a different class is used but luckily :-)
/// with the same method for adding loggings.
/// </remarks>
public dynamic MessageLogging { get; set; }
/// <summary>
/// make sure there are no overlapping set of timer callbacks
/// </summary>
private object locker;
/// <summary>
/// initialize timer class
/// </summary>
/// <param name="actions">action to perform</param>
/// <param name="timerName">name of timer</param>
public DisposableTimer(List<Action> actions, string timerName) : base()
{
// used to make sure there are no overlapping executing sets of timer callbacks
locker = new object();
// wrapper for the actions that need to be performed.
base.Elapsed += (s, a) => Callback(actions);
// set name of timer
this.TimerName = timerName;
/*
* only once a callback is executed after elapsed time,
* because there is only one callback executed there can be
* no overlap, because the "reset" is done after the set of
* callbacks are executed.
*/
AutoReset = false;
// timer is not started yet
Enabled = false;
}
/// <summary>
/// check if verwijder bericht timer is disposed
/// </summary>
public bool IsDisposed
{
get
{
var timerType = typeof(System.Timers.Timer);
var timerDisposedField = timerType.GetField("disposed", BindingFlags.NonPublic | BindingFlags.Instance);
return (bool)timerDisposedField.GetValue(this);
}
}
/// <summary>
/// after a callback a timer needs to be reset to continue running if AutoReset=false.
/// </summary>
/// <param name="interval">new interval of timer</param>
private void Reset(double interval)
{
// stop the timer
Stop();
// only do when not disposed yet.
if (!IsDisposed)
{
// adjust interval if needed
if (interval != 0)
Interval = interval;
// release exclusive lock
Monitor.Exit(locker);
}
// start the timer again
Start();
}
/// <summary>
/// only start if not disposed and started yet
/// </summary>
public new void Start()
{
if (!IsDisposed && !Enabled)
Enabled = true;
}
/// <summary>
/// only stop if not disposed and stopped yet
/// </summary>
public new void Stop()
{
if (!IsDisposed && Enabled)
Enabled = false;
}
/// <summary>
/// set of callbacks to perform after timer elapse interval
/// </summary>
/// <param name="callBackActions">list of callbacks inside this wrapper to execute</param>
public void Callback(List<Action> callBackActions)
{
// only execute callbacks if timer is enabled.
if (Enabled)
{
/*
* AutoReset = false, so a callback is only executed once,
* because of this overlapping callbacks should not occur,
* but to be sure exclusive locking is also used.
*/
var hasLock = false;
// show heartbeat at output window
if (ShowHeartBeat)
Debug.WriteLine(string.Format("HeartBeat interval: {0}...{1}/thread: 0x{2:X4}", TimerName, ++HeartbeatCounter, AppDomain.GetCurrentThreadId() ));
// execute callback action.
try
{
// only perform set of actions if not executing already on this thread.
Monitor.TryEnter(locker, ref hasLock);
if (hasLock)
{
// show heartbeat at output window
if (ShowHeartBeat)
Debug.WriteLine(string.Format(" action: {0}...{1}/thread: 0x{2:X4}", TimerName, HeartbeatCounter, AppDomain.GetCurrentThreadId()));
// execute the set of callback actions
foreach (Action callBackAction in callBackActions)
{
// execute callback
try
{
callBackAction();
}
// log error, but keep the action loop going.
catch (Exception ex)
{
EventLog.WriteEntry(ex.Message, EventLogEntryType.Warning);
MessageLogging.Insert(ex.Message);
}
}
}
// show that action is busy
else if (ShowHeartBeat)
Debug.WriteLine(string.Format(" busy: {0}...{1}/thread: 0x{2:X4}", TimerName, HeartbeatCounter, AppDomain.GetCurrentThreadId()));
}
// adjust interval when needed and release exclusive lock when done.
finally
{
// after the complete action is finished the lock should be released.
if (hasLock)
{
// timer interval can be changed when timer is active, callback function is needed for this.
double newInterval = 0;
if (UpdatebleInterval != null)
{
// calculate new interval for timer
double updatedInterval = UpdatebleInterval();
if (Interval != updatedInterval)
{
// based on Dutch
var dutchCultureInfo = new CultureInfo("nl-NL", false).TextInfo;
// write interval change to loggings
string intervalMessage = dutchCultureInfo.ToTitleCase(string.Format(#"{0} interval veranderd van {1} naar {2} seconden", TimerName, Interval / 1000, updatedInterval / 1000));
EventLog.WriteEntry(intervalMessage, EventLogEntryType.Information);
MessageLogging.Insert(intervalMessage);
// set for new interval
newInterval = updatedInterval;
}
}
// make ready for new callback after elapsed time, lock can be released by now.
Reset(newInterval);
}
}
}
// show heartbeat at output window
else if (ShowHeartBeat)
Debug.WriteLine(string.Format("HeartBeat thread: {0}...{1}/thread: 0x{2:X4}", TimerName, ++HeartbeatCounter, AppDomain.GetCurrentThreadId()));
}
}
I would reformat your code:
// from this
if (!value) base.Enabled = false;
// to this
if (!value)
base.Enabled = false;
Not only does it read better, you can put a break point on the key line and see if it's being executed
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();
}