My Windows service runs briefly, and then stops (no err msg).
I have this code in Program.cs of my Windows service:
static class Program
{
[STAThread]
static void Main()
{
#if(!DEBUG)
var ServicesToRun = new ServiceBase[]
{
new RoboRprtrService()
};
ServiceBase.Run(ServicesToRun);
#else
var rrs = new RoboRprtrService();
rrs.ConfigureService();
Console.ReadLine();
#endif
}
}
The breakpoint here:
var rrs = new RoboRprtrService();
...is hit (the DEBUG constant is currently defined), and so is the call to "InitializeComponent()" in my ServiceBase class, which is as follows:
public partial class RoboRprtrService : ServiceBase
{
private Timer timer;
private bool operationIsRunning;
public RoboRprtrService()
{
InitializeComponent(); // <= Breakpoint here; is reached
}
protected override void OnStart(string[] args)
{
ConfigureService(); // <= Breakpoint here, not hit
}
public void ConfigureService()
{
const int ONE_HOUR = 3600000;
timer = new Timer { Interval = 50000 };
timer.Elapsed += timer_Tick;
timer.Enabled = true;
RoboRprtrLib.WriteToLog("RoboRprtrService has started");
}
private void timer_Tick(object sender, ElapsedEventArgs eeargs)
{
if (operationIsRunning) return; // <= Breakpoint here, not hit
operationIsRunning = true;
timer.Elapsed -= timer_Tick;
try
{
RoboRprtrLib.WriteToLog("Timer tick event has occurred");
RoboRprtrLib.GenerateAndSaveDueReports();
operationIsRunning = false;
}
finally
{
timer.Elapsed += timer_Tick;
}
}
protected override void OnStop()
{
timer.Enabled = false;
RoboRprtrLib.WriteToLog("RoboRprtrService has stopped");
}
}
When I step (F11) into the InitializeComponent(); in the constructor, it takes me to this machine-generated code:
namespace RoboReporterService
{
partial class RoboRprtrService
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.ServiceName = "Service1";
}
#endregion
}
}
...(into the InitializeComponent() method) and then the Service stops.
Why does my service fail to "stay alive"?
As a side note, this project's properties, on the Application tab, show the output type of this as "Windows Application"; shouldn't it be "Class Library"?
UPDATE
Note: If I call code from the ConfigureService() method in the Service class, like so:
public void ConfigureService()
{
const int ONE_HOUR = 3600000;
timer = new Timer { Interval = 50000 };
timer.Elapsed += timer_Tick;
timer.Enabled = true;
RoboRprtrLib.WriteToLog("RoboRprtrService has started");
operationIsRunning = true;
RoboRprtrLib.GenerateAndSaveDueReports();
operationIsRunning = false;
}
...it runs; but then the service stops, and the timer_Tick() handler is never reached, even though it is hooked up in ConfigureService()
Related
I have this code for a windows service program that should clear the clipboard every now and then. Testing it in a console app proved it is a working code (then turned the console app into service via Topshelf nuget package). But running it as a service it just won't do the job. After some basic logging, I found that the Clipboard.Hastext() (and all the others) return false after installing the service (directly installed from the debug folder). What did I miss or what is the difference between debug and deployed mode that should be considered?
public class Service
{
readonly Timer timer;
object clipboardData;
public Service()
{
timer = new Timer(TimeSpan.FromMinutes(1).TotalMilliseconds)
{
AutoReset = true
};
timer.Elapsed += Timer_Elapsed;
}
/// <summary>
/// Checks every minute if there is any data on the clipboard.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
CurrentThreadParse(DoWork);
}
public void Start()
{
timer.Start();
}
public void Stop()
{
timer.Stop();
}
private void ChangeClipboardData()
{
if (Clipboard.ContainsText())
{
clipboardData = Clipboard.GetText();
WriteChanged();
}
else if (Clipboard.ContainsImage())
{
clipboardData = Clipboard.GetImage();
WriteChanged();
}
else if (Clipboard.ContainsAudio())
{
clipboardData = Clipboard.GetAudioStream();
WriteChanged();
}
else if (Clipboard.ContainsFileDropList())
{
clipboardData = Clipboard.GetFileDropList();
WriteChanged();
}
#if DEBUG
else
Console.WriteLine("No change!");
#endif
}
private void WriteChanged()
{
#if DEBUG
Console.WriteLine("Clipboard data has changed!");
#endif
}
private object GetClipboardObject()
{
if (Clipboard.ContainsText())
return Clipboard.GetText();
else if (Clipboard.ContainsImage())
return Clipboard.GetImage();
else if (Clipboard.ContainsAudio())
return Clipboard.GetAudioStream();
else if (Clipboard.ContainsFileDropList())
return Clipboard.GetFileDropList();
return null;
}
private void ClearClipboard()
{
Clipboard.Clear();
clipboardData = null;
#if DEBUG
Console.WriteLine("Clipboard data removed!");
#endif
}
/// <summary>
/// Sets the current thread as STA.
/// </summary>
/// <param name="threadStart">The code to run.</param>
private void CurrentThreadParse(ThreadStart threadStart)
{
Thread thread = new Thread(threadStart);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
private void DoWork()
{
object clipboardObject = GetClipboardObject();
if (clipboardData == null)
{
ChangeClipboardData();
}
else if (clipboardData.Equals(clipboardObject))
{
ClearClipboard();
}
else
{
//The last check returned data from the clipboard, but was changed,
//so the clipboard should not be cleared.
ChangeClipboardData();
}
}
}
Full project can be found here: https://github.com/profgyuri/ClipboardService
It's not possible with a service.
Services are running in a separate session on Windows (session 0). Users are in different sessions always (session 1, 2, etc). User's clipboards cannot be accessed by services from another session.
Instead you may create a Windows Schedule task to be executed in user session, a Run only when user is logged on task ( link ). Schedule may have any repeat interval.
i'm building a quick and dirty program to basically turn on a light in my room from a website as a code kata. during the programming i decided i would temporarily use a winform to test instead of hooking the physical light up (and having all sorts of possible problems there). but when i run my program the winform doesn't show, I've tried to run the executable but still nothing. when debugging i can see all the code works fine it's just that the winform doesn't show up. here is all the code:
form1.cs:
using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;
namespace alarm_light_test
{
public partial class Form1 : Form
{
WebClient client = new WebClient();
public Form1()
{
InitializeComponent();
while (true)
{
if (CheckData())
{
TurnSirenOn();
client.DownloadString("**link to .php file to reset the .txt file**");
Thread.Sleep(5000);
TurnSirenOff();
}
else
{
Thread.Sleep(1000);
}
}
}
public bool CheckData()
{
bool retval = false;
Stream stream = client.OpenRead("**link to online .txt file**");
StreamReader reader = new StreamReader(stream);
String content = reader.ReadToEnd();
if(content == "1")
{
retval = true;
}
return retval;
}
public void TurnSirenOn()
{
pictureBox1.BackColor = Color.Green;
}
public void TurnSirenOff()
{
pictureBox1.BackColor = Color.Red;
}
}
}
program.cs:
using System;
using System.Windows.Forms;
namespace alarm_light_test
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Form1.Designer.cs
namespace alarm_light_test
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.pictureBox1 = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(13, 13);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(235, 235);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(260, 260);
this.Controls.Add(this.pictureBox1);
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.PictureBox pictureBox1;
}
}
In your constructor public Form1(), you have a loop for-ever (a.k.a an infinite loop).
The while(true) will prevent the constructor from finishing the construction of the form object, and therefore will not be able to display anything.
public Form1()
{
InitializeComponent();
while (true) // This loop will never exit, and will run forever
{
...
}
}
Edit: Sample of how to run your code Asynchronously, therefore allowing the form to fully initialise and display as expected.
public Form1()
{
InitializeComponent();
DoWorkAsynchronously();
}
private async Task DoWorkAsynchronously()
{
await Task.Run(() =>
{
while (true)
{
if (CheckData())
{
TurnSirenOn();
client.DownloadString("**link to .php file to reset the .txt file**");
Thread.Sleep(5000);
TurnSirenOff();
}
else
{
Thread.Sleep(1000);
}
}
});
}
So I need to have a timer to count down from 60 seconds. I am new to Xamarin and have no idea what it accepts. It will be used in Android.
Any suggestions on how to start?
Can you use System.Timers.Timer?
You can use the System.Threading.Timer class, which is documented in the Xamarin docs: https://developer.xamarin.com/api/type/System.Threading.Timer/
Alternatively, for Xamarin.Forms, you have access to a cross-platform Timer via the Device class:
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
// called every 1 second
// do stuff here
return true; // return true to repeat counting, false to stop timer
});
If you just need Android you can use
System.Threading.Timer
In shared code with Xamarin Forms you can use
Device.StartTimer(...)
Or you can implement one yourselfe with advanced features like:
public sealed class Timer : CancellationTokenSource {
private readonly Action _callback;
private int _millisecondsDueTime;
private readonly int _millisecondsPeriod;
public Timer(Action callback, int millisecondsDueTime) {
_callback = callback;
_millisecondsDueTime = millisecondsDueTime;
_millisecondsPeriod = -1;
Start();
}
public Timer(Action callback, int millisecondsDueTime, int millisecondsPeriod) {
_callback = callback;
_millisecondsDueTime = millisecondsDueTime;
_millisecondsPeriod = millisecondsPeriod;
Start();
}
private void Start() {
Task.Run(() => {
if (_millisecondsDueTime <= 0) {
_millisecondsDueTime = 1;
}
Task.Delay(_millisecondsDueTime, Token).Wait();
while (!IsCancellationRequested) {
//TODO handle Errors - Actually the Callback should handle the Error but if not we could do it for the callback here
_callback();
if(_millisecondsPeriod <= 0) break;
if (_millisecondsPeriod > 0 && !IsCancellationRequested) {
Task.Delay(_millisecondsPeriod, Token).Wait();
}
}
});
}
public void Stop() {
Cancel();
}
protected override void Dispose(bool disposing) {
if (disposing) {
Cancel();
}
base.Dispose(disposing);
}
}
Yes, you can use System.Timers.Timer.
In Android we can use java.util.Timer like this:
private int i;
final Timer timer=new Timer();
TimerTask task=new TimerTask() {
#Override
public void run() {
if (i < 60) {
i++;
} else {
timer.cancel();
}
Log.e("time=",i+"");
}
};
timer.schedule(task, 0,1000);
In Xamarin.Android we can also use java.util.Timer like this:
[Activity(Label = "Tim", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
Timer timer = new Timer();
timer.Schedule(new MyTask(timer),0,1000);
}
}
class MyTask : TimerTask
{
Timer mTimer;
public MyTask(Timer timer) {
this.mTimer = timer;
}
int i;
public override void Run()
{
if (i < 60)
{
i++;
}
else {
mTimer.Cancel();
}
Android.Util.Log.Error("time",i+"");
}
}
You can always use
Task.Factory.StartNewTaskContinuously(YourMethod, new CancellationToken(), TimeSpan.FromMinutes(10));
Be sure to add the following class to your project
static class Extensions
{
/// <summary>
/// Start a new task that will run continuously for a given amount of time.
/// </summary>
/// <param name="taskFactory">Provides access to factory methods for creating System.Threading.Tasks.Task and System.Threading.Tasks.Task`1 instances.</param>
/// <param name="action">The action delegate to execute asynchronously.</param>
/// <param name="cancellationToken">The System.Threading.Tasks.TaskFactory.CancellationToken that will be assigned to the new task.</param>
/// <param name="timeSpan">The interval between invocations of the callback.</param>
/// <returns>The started System.Threading.Tasks.Task.</returns>
public static Task StartNewTaskContinuously(this TaskFactory taskFactory, Action action, CancellationToken cancellationToken, TimeSpan timeSpan
, string taskName = "")
{
return taskFactory.StartNew(async () =>
{
if (!string.IsNullOrEmpty(taskName))
{
Debug.WriteLine("Started task " + taskName);
}
while (!cancellationToken.IsCancellationRequested)
{
action();
try
{
await Task.Delay(timeSpan, cancellationToken);
}
catch (Exception e)
{
break;
}
}
if (!string.IsNullOrEmpty(taskName))
{
Debug.WriteLine("Finished task " + taskName);
}
});
}
}
I am creating a pos app that cannot communicate with my fiscal printer. So I have decided to store a receipt in a text file as Json object and make Windows Service app with FileSystemWatch to check for file updates and forward it to printer. I am using third party library to communicate with the printer. Here is a code of the service:
Program.cs
static void Main(string[] args)
{
var program = new Watcher();
if (Environment.UserInteractive)
{
program.Start();
}
else
{
ServiceBase.Run(new ServiceBase[]
{
program
});
}
//ServiceBase[] ServicesToRun;
//ServicesToRun = new ServiceBase[]
//{
// new Watcher()
//};
//ServiceBase.Run(ServicesToRun);
}
Watcher.cs
public partial class Watcher : ServiceBase
{
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);
public static OICFiscalPrinter printer { get; set; }
[StructLayout(LayoutKind.Sequential)]
public struct ServiceStatus
{
public long dwServiceType;
public ServiceState dwCurrentState;
public long dwControlsAccepted;
public long dwWin32ExitCode;
public long dwServiceSpecificExitCode;
public long dwCheckPoint;
public long dwWaitHint;
};
public enum ServiceState
{
SERVICE_STOPPED = 0x00000001,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_RUNNING = 0x00000004,
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007,
}
public Watcher()
{
InitializeComponent();
}
public void CheckReceipt(object e, FileSystemEventArgs args)
{
printer = new OICFiscalPrinter();
var name = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string text = null;
try
{
text = System.IO.File.ReadAllText(name + "\\Pictures\\test.txt");
var BasketList = JsonConvert.DeserializeObject<List<ItemsOnFacture>>(text);
printer.PortConfigString = "PortName=COM4;DataBits=8;Speed=9600;" +
"Parity = N; StopBits = 1; FlowControl = X;" +
"ReadTimeout = 6000;" +
"WriteTimeout = 500; UseReadBuffer = 1";
printer.Active = true;
var t = printer.Open();
if (!t) return;
printer.OpenReceipt();
foreach (var item in BasketList)
{
printer.ReceiptItem(item.ItemName, item.VatFee == 5 ? "B" : item.VatFee == 8 ? "A" : "D",
(decimal)item.PriceBrutto,
item.Amount, "unit", (decimal)item.PriceBruttoSum);
}
printer.CloseReceipt((decimal)BasketList.Sum(w => w.PriceBruttoSum),
(decimal)BasketList.Sum(w => w.PriceBruttoSum));
printer.Close();
File.Delete(name + "\\Pictures\\test.txt");
}
catch
{
}
}
public void Start()
{
//Start Logic here
var serviceStatus = new ServiceStatus
{
dwCurrentState = ServiceState.SERVICE_START_PENDING,
dwWaitHint = 100000
};
this.fileSystemWatcher1 = new System.IO.FileSystemWatcher();
((System.ComponentModel.ISupportInitialize)(this.fileSystemWatcher1)).BeginInit();
//
// fileSystemWatcher1
//
this.fileSystemWatcher1.EnableRaisingEvents = true;
var name = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
fileSystemWatcher1 = new FileSystemWatcher(name + "\\Pictures", "test.txt")
{
EnableRaisingEvents = true,
IncludeSubdirectories = false,
NotifyFilter = NotifyFilters.DirectoryName
};
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
this.fileSystemWatcher1.Changed += new System.IO.FileSystemEventHandler(this.CheckReceipt);
((System.ComponentModel.ISupportInitialize)(this.fileSystemWatcher1)).EndInit();
// Update the service state to Running.
serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
}
protected override void OnStart(string[] args)
{
Start();
}
protected override void OnContinue()
{
}
protected override void OnStop()
{
}
private FileSystemWatcher fileSystemWatcher1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// Watcher
//
components = new System.ComponentModel.Container();
this.ServiceName = "WATTOFP";
}
#endregion
}
ProjectInstaller.cs
[RunInstaller(true)]
public class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
//
// serviceInstaller1
//
this.serviceInstaller1.Description = "WATTO Fiscal Printer";
this.serviceInstaller1.DisplayName = "WATTO Fiscal Printer";
this.serviceInstaller1.ServiceName = "WATTOFP";
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// ProjectInstaller
//
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.serviceInstaller1});
}
#endregion
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
}
The problem is the fact that after installation when I try to run the service it starts and immediately stops as the warning appears. How can I make the service run and the watch the file for the changes?
The problem might be that service by default is running under system account. Thus user folder is not what you're expecting and there is no file there.
Usually when service cannot be started, there should be a error with exception in event log. Please post it here for further assistance.
I have implemented a SqlListener class that uses SqlDependency to wait for changes in SQL database. At one point in my business workflow I need to wait for a record turning up in the database. The SqlListener triggers an event when requested record is found. This works fine. I can make it work by entering a While-loop and wait until I detect the event being returned. But this is not ideal design. It makes the processor spin a lot in vain.
I would like to wait for the event in a more intelligent manner. I read a lot of suggestions on using Task, NotificationDelegate, ManualResetEvent, etc. .... but I was not able to get it all together.
A simplified example will probably make it easier to understand. This is my current setup that works. But if possible I would like to get rid of the ugly while loop.
private const int MaxWaitTime = 5;
private SqlListener<RecordType> _recordListener;
private RecordType _record;
/// <summary>
/// Request a record and wait until it is found.
/// </summary>
public RecordType GetRecordAwait(int requestedId)
{
// Initiate listening for record
_recordListener = new SqlListener<RecordType>();
_recordListener.SqlModified += SqlListener_SqlModified;
_recordListener.StartListening(requestedId);
// Wait until record is found
var startTime = DateTime.Now;
while (_record == null &&
DateTime.Now.Subtract(startTime).TotalSeconds < MaxWaitTime)
{
Thread.Sleep(1);
}
// Stop listening
_recordListener.SqlModified -= SqlListener_SqlModified;
_recordListener.Dispose();
_recordListener = null;
// Return record
return _record;
}
private void SqlListener_SqlModified(object sender, SqlModifiedArgs args)
{
_record = (RecordType)args.Record;
}
Instead of using While, you could go with Timer and events. Something like:
public class ListenerWaiting
{
public ListenerWaiting(int waitingTimeSeconds)
{
_waitSeconds = waitingTimeSeconds;
}
private int _waitSeconds;
private System.Timers.Timer _timer;
private Listener _listener;
public event EventHandler<string> ListenerDone;
public void Listen(int listeningPeriodSeconds)
{
_listener = new Listener(listeningPeriodSeconds * 1000);
_listener.ListenerCompleted += ListenerListenerCompleted;
_timer = new System.Timers.Timer(_waitSeconds * 1000) {Enabled = true};
_timer.Elapsed += TimerElapsed;
}
void ListenerListenerCompleted(object sender, string e)
{
StopTimer();
StopListener();
if (ListenerDone != null)
ListenerDone(this, "Waiting success! Message was: " + e);
}
void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
StopTimer();
StopListener();
if (ListenerDone != null)
ListenerDone(this, "Waited longer than set, aborted waiting...");
}
private void StopTimer()
{
_timer.Stop();
_timer.Elapsed -= TimerElapsed;
_timer = null;
}
private void StopListener()
{
_listener.ListenerCompleted -= ListenerListenerCompleted;
_listener = null;
}
}
public class Listener
{
private System.Timers.Timer _timer;
private string _listeningPeriodSeconds;
public event EventHandler<string> ListenerCompleted;
public Listener(int listeningPeriodSeconds)
{
_listeningPeriodSeconds = listeningPeriodSeconds.ToString();
_timer = new System.Timers.Timer(listeningPeriodSeconds) { Enabled = true };
_timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Elapsed -= TimerElapsed;
_timer = null;
if (ListenerCompleted != null)
ListenerCompleted(this, _listeningPeriodSeconds);
}
}
...and then consume it with:
static void Main(string[] args)
{
var wait = new ListenerWaiting(5);
wait.ListenerDone += WaitListenerDone;
wait.Listen(3);
Console.ReadLine();
}
static void WaitListenerDone(object sender, string e)
{
Console.WriteLine(e);
}
I guess I could find better names for classes, but you'll get the idea ;)
In fact the solution was more simple than I first thought. When I rephrased my question and searched again I found it. The ManualResetEvent as mentioned already in my question turned out to be the simplest way to solve it.
All I had to do was to add a ManualResetEvent and set it to wait ;-)
private const int MaxWaitTime = 5000;
private SqlListener<RecordType> _recordListener;
private RecordType _record;
private readonly ManualResetEvent _recordWaiter = new ManualResetEvent(false);
/// <summary>
/// Request a record and wait until it is found.
/// </summary>
public RecordType GetRecordAwait(int requestedId)
{
// Initiate listening for record
_recordListener = new SqlListener<RecordType>();
_recordListener.SqlModified += SqlListener_SqlModified;
_recordListener.StartListening(requestedId);
// Wait synchronously until record is found
_recordWaiter.WaitOne(MaxWaitTime);
// Stop listening
_recordListener.SqlModified -= SqlListener_SqlModified;
_recordListener.Dispose();
_recordListener = null;
// Return record
return _record;
}
private void SqlListener_SqlModified(object sender, SqlModifiedArgs args)
{
_record = (RecordType)args.Record;
_recordWaiter.Set();
}