I am writing a C# Script control (WinForms). This is based on Dockpanelsuite, Scintilla, CSharpScriptingLibrary and a lot of code I wrote/collected in the last year. I want to include a prebuild "Logging" and a "ProgressBar" feature. So far I got everything to work as expected, except the fact it wont work correctly when using Timers.
The code written in the editor is compiled at runtime, the "Programm"-type is loaded and the "Main"-method is invoked. If the created assembly includes a "Log"-type the "LogMessage"-event will be connected to a method in the hosting script control, which forwards the messages to the Output-window.
As you can see the "Hello World" message is written to the Output-window, but each time the timer fires the LogMessage event in the static Log-Class is null.
Any idea what I have to change to share the LogMessage event from the UI thread with the Timer thread?
Programm.cs
using System;
using System.Timers;
namespace ScriptControl
{
public class Programm
{
Timer timer = null;
int step = 0, steps = 10;
public void Main()
{
Log.Write("Hello World");
try
{
timer = new Timer();
timer.Interval = 1000;
timer.Elapsed += TimerHandler;
timer.Enabled = true;
}
catch(Exception ex)
{
Log.Write(ex);
}
}
private void TimerHandler(object sender, object args)
{
if(this.step == 1) timer.Enabled = false;
Log.Write(step++.ToString());
Progress.Set(this.step, steps);
}
}
}
Log.cs
public delegate void LogHandler(object message);
public static class Log
{
public static event LogHandler LogMessage;
public static void Write(object message)
{
if(LogMessage != null)
LogMessage(message);
else
System.Windows.Forms.MessageBox.Show("LogMessage is null");
}
}
Related
I have developed my first C# Service in VS 2015, but I cannot get my ElapsedEventHandler method to fire. I have the following code:
using System;
using System.ServiceProcess;
using System.Timers;
namespace UpdateEnvironmentService
{
public partial class Scheduler : ServiceBase
{
private Timer timer = null;
public Scheduler()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer = new Timer();
this.timer.Interval = Convert.ToDouble(1000); //timer intraval in milliseconds
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.UpdateData);
timer.Enabled = true;
Library.WriteLog("Data Updater Started ");
}
private void UpdateData(object sender, EventArgs e)
{
Library.WriteLog("Got to update Data ");
}
protected override void OnStop()
{
timer.Enabled = false;
timer = null;
Library.WriteLog("Data Updater Stopped ");
}
}
}
The line Data Updater Started gets printed to my log file, but I never end up seeing Got to update Data or even Data Updater Stopped. It seems my ElapsedEventHandler is never firing. Anybody have any idea why?
I would refer you to the documentation on MSDN for the System.Timers.Timer class.
Most examples and usage of the timer, tends to avoid setting Enabled directly and instead relies on the Start and Stop methods.
On a side-note, I would recommend that in the age of Task, that you approach the problem in a different way:
namespace UpdateEnvironmentService
{
public partial class Scheduler : ServiceBase
{
private readonly CancellationTokenSource _tcs;
public Scheduler()
{
InitializeComponent();
_tcs = new CancellationTokenSource();
}
protected override void OnStart(string[] args)
{
Library.WriteLog("Data Updater Started ");
Task.Factory.StartNew(Runner, _tcs.Token);
}
private async void Runner()
{
Library.WriteLog("In runner");
var delay = TimeSpan.FromSeconds(1);
while(!_tcs.IsCancellationRequested)
{
Library.WriteLog("Waiting...");
await Task.Delay(delay, _tcs.Token);
UpdateData();
}
Library.WriteLog("Cancellation requested; exiting runner");
}
private void UpdateData()
{
Library.WriteLog("Got to update Data ");
}
protected override void OnStop()
{
_tcs.Cancel();
Library.WriteLog("Data Updater Stopped ");
}
}
}
This approach removes the need for a timer, and instead introduces asynchrony from the task, allowing the threadpool to manage the delay; it also has the benefit of introducing nicer cancellation control, which means it can even be cancelled while it's waiting for the delay!
My timer elapsed event does not fire in my windows service, why? I search in the forum but nothing of the solutions work for me.
In the main of program.cs:
static class program
{
static void Main()
{
#if DEBUG
servicioCR cr = new servicioCR();
cr.beginProcess();
#else
#endif
//ServiceBase[] ServicesToRun;
//ServicesToRun = new ServiceBase[]
//{
// new servicioCR()
//};
//ServiceBase.Run(ServicesToRun);
}
}
In my service class
public static System.Timers.Timer timer;
public servicioCR()
{
timer = new System.Timers.Timer(5000);
timer.AutoReset = false;
timer.Elapsed += timer_Elapsed;
InitializeComponent();
}
The elapsed event
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
timer.Stop();
//Do stuff
}
catch (System.Exception ex)
{
EventLog.WriteEntry(ex.Message);
}
finally
{
timer.Start();
}
}
And the beginProcess()
internal void beginProcess()
{
timer.Start();
}
I am using .NET Framework 4.5 and VS 2013... I don't understand why it doesn't work, I copied this from another solution that works fine.
If I put a breakpoint in one line of the Do stuff on the elapsed event, it never breaks.
Why? Thanks
Your timer is running, but the program closes itself before the timer ever fires. You need to put a pause of some kind in so your program does not close itself.
static void Main()
{
#if DEBUG
servicioCR cr = new servicioCR();
cr.beginProcess();
Console.WriteLine("Program Running");
Console.ReadLine();
#else
#endif
//ServiceBase[] ServicesToRun;
//ServicesToRun = new ServiceBase[]
//{
// new servicioCR()
//};
//ServiceBase.Run(ServicesToRun);
}
Here is a snippit of code from one of my older projects, this lets you run your program as both a service and a console app. In Visual studio just set the debugger to pass in the arguments --console in the setup project screen.
class Program
{
public static void Main(string[] args)
{
var example = new MyExampleApp();
if (args.Contains("--console"))
{
example.ConsoleRun(args);
}
else
{
ServiceBase.Run(example);
}
}
}
class MyExampleApp : ServiceBase
{
public void ConsoleRun(string[] args)
{
Console.WriteLine(string.Format("{0}::starting...", GetType().FullName));
OnStart(args);
Console.WriteLine(string.Format("{0}::ready (ENTER to exit)", GetType().FullName));
Console.ReadLine();
OnStop();
Console.WriteLine(string.Format("{0}::stopped", GetType().FullName));
}
//... the rest of the code from your service class.
}
I want to have a timer in my windows phone 8 app, that´s counting/running independent of current shown page.
It should connect to server - when possible in a UI independet task/thread - and store data in a global object/list.
The Independence from current shown page is my point.
I tried following in App.xaml.cs:
public partial class App : Application
{
// creating timer instance
DispatcherTimer gAppTimer = new DispatcherTimer();
// timer interval specified as 1 minute
gAppTimer.Interval = TimeSpan.FromSeconds(60);
// Sub-routine OnTimerTick that will be called at specified intervall
gAppTimer.Tick += OnTimerTick;
// starting the timer
gAppTimer.Start();
public void OnTimerTick(Object sender, EventArgs args)
{
// text box property is set to current system date.
// ToString() converts the datetime value into text
MessageBox.Show("TIMER fired");
}
:
:
But this doesn´t work. Than I tried just declaring the object in App.xaml.cs:
public partial class App : Application
{
// creating timer instance
DispatcherTimer gAppTimer = new DispatcherTimer();
public void OnTimerTick(Object sender, EventArgs args)
{
// text box property is set to current system date.
// ToString() converts the datetime value into text
MessageBox.Show("TIMER fired");
}
:
:
And on my startpage.xaml.cs:
// timer interval specified as 1 minute
App.gAppTimer.Interval = TimeSpan.FromSeconds(60);
// Sub-routine OnTimerTick that will be called at specified intervall
App.gAppTimer.Tick += OnTimerTick;
// starting the timer
App.gAppTimer.Start();
But this doesn´t work, too.
Any ideas how to handle my Problem? What I don´t want to use is a Background Task, because it runs only every 30 minutes. My solution should only run, if the app is "active" (in foreground).
That's normally done using a static or singleton class. Both will be global and you'll have access to them from every page.
Also, the DispatcherTimer invokes it's TimerTick method on the UI thread. If you don't need to be in the UI thread, you should use a System.Threading.Timer, which invokes a method in a background thread.
Here's an example:
public static class SomeManager {
private static Timer gAppTimer;
private static object lockObject = new object();
public static void StartTimer() {
if (gAppTimer == null) {
lock (lockObject) {
if (gAppTimer == null) {
gAppTimer = new Timer(OnTimerTick, null, 60 * 1000, 60 * 1000);
}
}
}
}
public static void StopTimer() {
if (gAppTimer != null) {
lock (lockObject) {
if (gAppTimer != null) {
gAppTimer.Change(Timeout.Infinite, Timeout.Infinite);
gAppTimer = null;
}
}
}
}
private static void OnTimerTick(object state) {
Action();
}
public static void Action() {
// Do what you need to do
}
}
Just call SomeManager.StartTimer() from your first page or from App.xaml.cs and the timer will start.
Update
I updated the code a little:
Renamed the Initialize method to StartTimer.
Added StopTimer method which stops the timer. You can then start it again by calling SomeManager.StartTimer.
Added Action method which is the one actually donig the work. You can invoke it from anywhere, anytime.
Note: the the timer will call this method in a background thread and you should do the same using something like Task.Run(() => SomeManager.Action());
Added a lock to ensure that the Start/Stop methods will not throw exceptions if invoked from multiple threads at the same time.
I'm not sure how you have arranged your code, but as I've tried:
public partial class App : Application
{
public static PhoneApplicationFrame RootFrame { get; private set; }
public DispatcherTimer gAppTimer = new DispatcherTimer();
public void OnTimerTick(Object sender, EventArgs args)
{
MessageBox.Show("TIMER fired");
}
public App()
{
gAppTimer.Interval = TimeSpan.FromSeconds(2);
// Sub-routine OnTimerTick that will be called at specified intervall
gAppTimer.Tick += OnTimerTick;
// starting the timer
gAppTimer.Start();
// rest of the code
the above code works. MessageBox shows every 2 seconds, if you had declared your DispatcherTimer as public, then you will be able to access it like this:
(App.Current as App).gAppTimer.Stop();
Note also that depending on what you want to achieve you may also use System.Threading.Timer.
On the other hand you may also think of using public static DispatcherTimer somewhere.
There's plenty of examples of people saying to use a Timer instead of Thread.Sleep(...) in an Azure Worker Role. No probs with that.
What I'm struggling to understand is how to code this.
Currently, I have the following (pseduo code)
_timer.Elapsed += (sender, args) => DoWork();
public override void Run()
{
while(true)
{
DoWork();
}
}
public void DoWork()
{
try
{
_timer.Stop();
// Now - do stuff ....
}
catch(....) { ... }
_timer.Start()
}
And what happens, is that the code enters the DoWork() method once and DoesStuff(tm).. fine .. starts the timer (say .. with a 30 second interval) and then exits that method.
Then, it returns back to the main Run() method .. which is in that loop. So it immediately comes back around and enters the DoWork() method again .. instead of waiting for the timer to fire it off.
So I'm not sure how to replace any Thread.Sleep(...) with Timers.
Any clues?
Clarification
I do not want to exit the Run() method :) I'm very happy to keep looping forever. What I'm stuck with, is replacing the standard Thread.Sleep(...) call (which blocks the thread) and replace that with a Timer, which most people suggest.
Update
Please do not link or suggest that I should use cancelSource.Token.WaitHandle.WaitOne(); as a solution. That is not what I'm trying to achieve here. Please note the post title!
I figure that if you want to solve this situation the way you outline here you will need a WaitHandle AND a Timer.
The short answer is here below. The long answer became a blog post: HowTo wait in a WorkerRole using Timer and EventWaitHandle over Thread.Sleep
I used an EventWaitHandle along with the Timer and came up with this solution:
public class WorkerRole : RoleEntryPoint
{
Waiter waiter;
public override bool OnStart()
{
waiter = new Waiter(WorkerConfiguration.WaitInterval);
return base.OnStart();
}
public override void Run()
{
while (true)
{
DoWork();
waiter.Wait();
}
}
public void DoWork()
{
// [...]
}
}
And here is the waiter class:
public class Waiter
{
private readonly Timer timer;
private readonly EventWaitHandle waitHandle;
public Waiter(TimeSpan? interval = null)
{
waitHandle = new AutoResetEvent(false);
timer = new Timer();
timer.Elapsed += (sender, args) => waitHandle.Set();
SetInterval(interval);
}
public TimeSpan Interval
{
set { timer.Interval = value.TotalMilliseconds; }
}
public void Wait(TimeSpan? newInterval = null)
{
SetInterval(newInterval);
timer.Start();
waitHandle.WaitOne();
timer.Close();
waitHandle.Reset();
}
private void SetInterval(TimeSpan? newInterval)
{
if (newInterval.HasValue)
{
Interval = newInterval.Value;
}
}
}
I'm having the hardest time trying to get this to work, hoping one of you has done this before.
I have a C# console app that is running a child process which inherits its console. I want a ctrl-c caught by the outer app to be passed along to the inner app so that it can have a chance to shut down nicely.
I have some very simple code. I start a Process, then poll it with WaitForExit(10). I also have a CancelKeyPress handler registered, which sets a bool to true when it fires. The polling loop also checks this, and when it's true, it calls GenerateConsoleCtrlEvent() (which I have mapped through pinvoke).
I've tried a lot of combinations of params to GenerateConsoleCtrlEvent(). 0 or 1 for the first param, and either 0 or the child process's ID for the second param. Nothing seems to work. Sometimes I get a false back and Marshal.GetLastWin32Error() returns 0, and sometimes I get true back. But none cause the child app to receive a ctrl-c.
To be absolutely sure, I wrote a test C# app to be the child app which prints out what's going on with it and verified that manually typing ctrl-c when it runs does properly cause it to quit.
I've been banging my head against this for a couple hours. Can anyone give me some pointers on where to go with this?
Not so sure this is a good approach. This only works if the child process is created with the CREATE_NEW_PROCESS_GROUP flag for CreateProcess(). The System.Diagnostics.Process class however does not support this.
Consider using the return value from the Main() method. There is already a unique value defined in the Windows SDK for Ctrl+C aborts, STATUS_CONTROL_C_EXIT or 0xC000013A. The parent process can get that return code from the Process.ExitCode property.
Did you have any luck with this? My understanding is that when you press CTRL+C in a console, by default all the processes attached to the console receive it, not just the parent one. Here's an example:
Child.cs:
using System;
public class MyClass
{
public static void CtrlCHandler(object sender, ConsoleCancelEventArgs args)
{
Console.WriteLine("Child killed by CTRL+C.");
}
public static void Main()
{
Console.WriteLine("Child start.");
Console.CancelKeyPress += CtrlCHandler;
System.Threading.Thread.Sleep(4000);
Console.WriteLine("Child finish.");
}
}
Parent.cs:
using System;
public class MyClass
{
public static void CtrlCHandler(object sender, ConsoleCancelEventArgs args)
{
Console.WriteLine("Parent killed by CTRL+C.");
}
public static void Main()
{
Console.CancelKeyPress += CtrlCHandler;
Console.WriteLine("Parent start.");
System.Diagnostics.Process child = new System.Diagnostics.Process();
child.StartInfo.UseShellExecute = false;
child.StartInfo.FileName = "child.exe";
child.Start();
child.WaitForExit();
Console.WriteLine("Parent finish.");
}
}
Output:
Y:\>parent
Parent start.
Child start.
Parent killed by CTRL+C.
Child killed by CTRL+C.
^C
Y:\>parent
Parent start.
Child start.
Child finish.
Parent finish.
So I wouldn't have thought you'd need to do anything special. However, if you really need to generate CTRL+C events yourself, things might not be so easy. I'm not sure about the problems you describe, but as far as I can tell you can only send CTRL+C events to all the processes attached to a console window. If you detach a process, you can't send it CTRL+C events. If you want to be selective in which processes to send the CTRL+C events, you seem to need to create new console windows for every one. I've no idea if there's some way to do it without visible windows or when you want to redirect I/O using pipes.
Here is my solution for sending ctrl-c to a process. FYI, I never got GenerateConsoleCtrlEvent to work.
Rather than using GenerateConsoleCtrlEvent, here is how I have found to send CTRL-C to a process. FYI, in this case, I didn't ever need to find the group process ID.
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class ConsoleAppManager
{
private readonly string appName;
private readonly Process process = new Process();
private readonly object theLock = new object();
private SynchronizationContext context;
private string pendingWriteData;
public ConsoleAppManager(string appName)
{
this.appName = appName;
this.process.StartInfo.FileName = this.appName;
this.process.StartInfo.RedirectStandardError = true;
this.process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
this.process.StartInfo.RedirectStandardInput = true;
this.process.StartInfo.RedirectStandardOutput = true;
this.process.EnableRaisingEvents = true;
this.process.StartInfo.CreateNoWindow = true;
this.process.StartInfo.UseShellExecute = false;
this.process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
this.process.Exited += this.ProcessOnExited;
}
public event EventHandler<string> ErrorTextReceived;
public event EventHandler ProcessExited;
public event EventHandler<string> StandartTextReceived;
public int ExitCode
{
get { return this.process.ExitCode; }
}
public bool Running
{
get; private set;
}
public void ExecuteAsync(params string[] args)
{
if (this.Running)
{
throw new InvalidOperationException(
"Process is still Running. Please wait for the process to complete.");
}
string arguments = string.Join(" ", args);
this.process.StartInfo.Arguments = arguments;
this.context = SynchronizationContext.Current;
this.process.Start();
this.Running = true;
new Task(this.ReadOutputAsync).Start();
new Task(this.WriteInputTask).Start();
new Task(this.ReadOutputErrorAsync).Start();
}
public void Write(string data)
{
if (data == null)
{
return;
}
lock (this.theLock)
{
this.pendingWriteData = data;
}
}
public void WriteLine(string data)
{
this.Write(data + Environment.NewLine);
}
protected virtual void OnErrorTextReceived(string e)
{
EventHandler<string> handler = this.ErrorTextReceived;
if (handler != null)
{
if (this.context != null)
{
this.context.Post(delegate { handler(this, e); }, null);
}
else
{
handler(this, e);
}
}
}
protected virtual void OnProcessExited()
{
EventHandler handler = this.ProcessExited;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
protected virtual void OnStandartTextReceived(string e)
{
EventHandler<string> handler = this.StandartTextReceived;
if (handler != null)
{
if (this.context != null)
{
this.context.Post(delegate { handler(this, e); }, null);
}
else
{
handler(this, e);
}
}
}
private void ProcessOnExited(object sender, EventArgs eventArgs)
{
this.OnProcessExited();
}
private async void ReadOutputAsync()
{
var standart = new StringBuilder();
var buff = new char[1024];
int length;
while (this.process.HasExited == false)
{
standart.Clear();
length = await this.process.StandardOutput.ReadAsync(buff, 0, buff.Length);
standart.Append(buff.SubArray(0, length));
this.OnStandartTextReceived(standart.ToString());
Thread.Sleep(1);
}
this.Running = false;
}
private async void ReadOutputErrorAsync()
{
var sb = new StringBuilder();
do
{
sb.Clear();
var buff = new char[1024];
int length = await this.process.StandardError.ReadAsync(buff, 0, buff.Length);
sb.Append(buff.SubArray(0, length));
this.OnErrorTextReceived(sb.ToString());
Thread.Sleep(1);
}
while (this.process.HasExited == false);
}
private async void WriteInputTask()
{
while (this.process.HasExited == false)
{
Thread.Sleep(1);
if (this.pendingWriteData != null)
{
await this.process.StandardInput.WriteLineAsync(this.pendingWriteData);
await this.process.StandardInput.FlushAsync();
lock (this.theLock)
{
this.pendingWriteData = null;
}
}
}
}
}
Then, in actually running the process and sending the CTRL-C in my main app:
DateTime maxStartDateTime = //... some date time;
DateTime maxEndDateTime = //... some later date time
var duration = maxEndDateTime.Subtract(maxStartDateTime);
ConsoleAppManager appManager = new ConsoleAppManager("myapp.exe");
string[] args = new string[] { "args here" };
appManager.ExecuteAsync(args);
await Task.Delay(Convert.ToInt32(duration.TotalSeconds * 1000) + 20000);
if (appManager.Running)
{
// If stilll running, send CTRL-C
appManager.Write("\x3");
}
For details, please see Redirecting standard input of console application and Windows how to get the process group of a process that is already running?