.NET Framework Bookmark and Share   
 index > .NET Base Class Library > EventWaitHandle synchronization between windows forms app and windows service
 

EventWaitHandle synchronization between windows forms app and windows service

Hi everyone, i'm developing a web crawler solution that reads specific stock infomation from mutiple websites. This solution includes a windows serviceproject that the web crawler runs on, and amanagementUI project using Windows Forms. User canspecify stock symbol ("MSFT" for example )from themanagement UI and then this valueispassed to the service, so the service can gather user specified stock information once per minute from the Internet. The stock symbol is stored in the app.config file under the windows service project. I try to use EventWaitHandle object to make communication between the management UI and thewindows service. I'm not familiar with event synchronizationso please have a look at why it doesn't work. Thanks!

1. This is the code running inside the windows service:

public class InfoServer
{
private System.Timers.Timer serviceTimer; //Interval

private EventWaitHandle resetSettingsEvent;

private Thread listenerThread; //anew threadlisten for event notification

private bool created;

public InfoServer()
{
this.resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", out created);
this.serviceTimer = new System.Timers.Timer();
this.serviceTimer.Elapsed += new ElapsedEventHandler(Run);

this.serviceTimer.Interval = 60000;

this.serviceTimer.Start();
this.serviceTimer.Enabled = true;
this.listenerThread = new Thread(new ThreadStart(this.Listen));
this.listenerThread.Name = "Listener";
this.listenerThread.Start();
}

public void Run(object source, ElapsedEventArgs e)
{
YahooAccess yahoo = new YahooAccess(Properties.Settings.Default.StockCode);
yahoo.Connect();
yahoo.WrapData();
yahoo.InsertData();
}

public void Listen() //

{
while (true)
{
if (this.resetSettingsEvent.WaitOne(1000, false))

{
Properties.Settings.Default.Reload(); //Load new settings in app.config

}

}

}

public void Stop()
{
this.serviceTimer.Enabled = false;

}

}

2. Here issome codefrom the management Form class definition:

...

public Form_Admin()
{
this.sc = new ServiceController("service_name");
this.resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", outcreated);
InitializeComponent();
}

private void ChangeSettings() //Modify thestock code setting

{
string configPath = "D:\\Test\\StockInfoFile.exe.config";
XmlDocument configXML = new XmlDocument();
configXML.Load(configPath);
XmlNode configNode = configXML.SelectSingleNode("/configuration/applicationSettings/StockInfoCollector.Properties.Settings/setting[@name='StockCode']/value");
configNode.InnerText = this.textBox_StockCode.Text;
configXML.Save(configPath);
this.resetSettingsEvent.Set(); //notify the windows service

}

...

zeyansoft
David M Morton wrote:

In both of your methods, you'll want to declare your ManualResetEvent as follows:

Code Snippet

EventWaitHandle resetSettingsEvent = null;

try

{

resetSettingsEvent = EventWaitHandle.OpenExisting("resetStockSettings");

}

catch (WaitHandleCannotBeOpenedException ex)

{

resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings");

}

If the EventWaitHandle already exists, then you'll want to open the existing wait handle. If it doesn't, then you'll want to create it. Your two calls to the EventWaitHandle constructor are actually creating two
EventWaitHandle objects, with two handles in the system. Using this method above will ensure you pick up the one that has been created first.
Apart from using exceptions for normal logic flow, this introduces a race condition between OpenExisting and new EventWaitHandle--there's the possibility that another application could run and create that event with that name between OpenExisting and new EventWaitHandle. The original code that used ("new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", out created);" is the correct way of avoiding this race condition).
Peter Ritchie

It looks like you most likely have an error with code access security.

Try adding the following lines where you create the EventWaitHandle in the service:

Code Snippet

EventWaitHandleSecurity evhSec = new EventWaitHandleSecurity();

EventWaitHandleAccessRule evhAccessRule = new EventWaitHandleAccessRule("Everyone", EventWaitHandleRights.FullControl, AccessControlType.Allow);

evhSec.AddAccessRule(evhAccessRule);

EventWaitHandle mynewevent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", out value, evhSec);

You may need to modify the code for a particular user or group of users in the EventWaitHandleAccessRule constructor ("Everyone" is pretty broad.)

This may work. If not, let us know under which account the service is running under.

David M Morton
Try using the EventWaitHandle in EventResetMode.AutoReset instead of ManualReset. The Service is probably blowing through the WaitOne statements, because a manual reset event isn't automatically turned off once another thread has the event. AutoReset will ensure that the event is reset at the time the other thread receives the signal.

David M Morton
zeyansoft wrote:

Hi everyone, i'm developing a web crawler solution that reads specific stock infomation from mutiple websites. This solution includes a windows serviceproject that the web crawler runs on, and amanagementUI project using Windows Forms. User canspecify stock symbol ("MSFT" for example )from themanagement UI and then this valueispassed to the service, so the service can gather user specified stock information once per minute from the Internet. The stock symbol is stored in the app.config file under the windows service project. I try to use EventWaitHandle object to make communication between the management UI and thewindows service. I'm not familiar with event synchronizationso please have a look at why it doesn't work. Thanks!

1. This is the code running inside the windows service:

public class InfoServer
{
private System.Timers.Timer serviceTimer; //Interval

private EventWaitHandle resetSettingsEvent;

private Thread listenerThread; //anew threadlisten for event notification

private bool created;

public InfoServer()
{
this.resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", out created);
this.serviceTimer = new System.Timers.Timer();
this.serviceTimer.Elapsed += new ElapsedEventHandler(Run);

this.serviceTimer.Interval = 60000;

this.serviceTimer.Start();
this.serviceTimer.Enabled = true;
this.listenerThread = new Thread(new ThreadStart(this.Listen));
this.listenerThread.Name = "Listener";
this.listenerThread.Start();
}

public void Run(object source, ElapsedEventArgs e)
{
YahooAccess yahoo = new YahooAccess(Properties.Settings.Default.StockCode);
yahoo.Connect();
yahoo.WrapData();
yahoo.InsertData();
}

public void Listen() //

{
while (true)
{
if (this.resetSettingsEvent.WaitOne(1000, false))

{
Properties.Settings.Default.Reload(); //Load new settings in app.config

}

}

}

public void Stop()
{
this.serviceTimer.Enabled = false;

}

}

2. Here issome codefrom the management Form class definition:

...

public Form_Admin()
{
this.sc = new ServiceController("service_name");
this.resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", outcreated);
InitializeComponent();
}

private void ChangeSettings() //Modify thestock code setting

{
string configPath = "D:\\Test\\StockInfoFile.exe.config";
XmlDocument configXML = new XmlDocument();
configXML.Load(configPath);
XmlNode configNode = configXML.SelectSingleNode("/configuration/applicationSettings/StockInfoCollector.Properties.Settings/setting[@name='StockCode']/value");
configNode.InnerText = this.textBox_StockCode.Text;
configXML.Save(configPath);
this.resetSettingsEvent.Set(); //notify the windows service

}

...

Be sure to add a resetSettingsEvent.Reset() before or after the Reload call.
Peter Ritchie

In both of your methods, you'll want to declare your ManualResetEvent as follows:

Code Snippet

EventWaitHandle resetSettingsEvent = null;

try

{

resetSettingsEvent = EventWaitHandle.OpenExisting("resetStockSettings");

}

catch (WaitHandleCannotBeOpenedException ex)

{

resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings");

}

If the EventWaitHandle already exists, then you'll want to open the existing wait handle. If it doesn't, then you'll want to create it. Your two calls to the EventWaitHandle constructor are actually creating two
EventWaitHandle objects, with two handles in the system. Using this method above will ensure you pick up the one that has been created first.
David M Morton

If that doesn't work, try calling just resetSettingsEvent.WaitOne() without the parameters. It seems your code will continue to run after 1 second regardless of whether the event is signaled or not.

David M Morton
David M Morton wrote:

In both of your methods, you'll want to declare your ManualResetEvent as follows:

Code Snippet

EventWaitHandle resetSettingsEvent = null;

try

{

resetSettingsEvent = EventWaitHandle.OpenExisting("resetStockSettings");

}

catch (WaitHandleCannotBeOpenedException ex)

{

resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings");

}

If the EventWaitHandle already exists, then you'll want to open the existing wait handle. If it doesn't, then you'll want to create it. Your two calls to the EventWaitHandle constructor are actually creating two
EventWaitHandle objects, with two handles in the system. Using this method above will ensure you pick up the one that has been created first.
Apart from using exceptions for normal logic flow, this introduces a race condition between OpenExisting and new EventWaitHandle--there's the possibility that another application could run and create that event with that name between OpenExisting and new EventWaitHandle. The original code that used ("new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", out created);" is the correct way of avoiding this race condition).
Peter Ritchie
What part isn't working? Have you confirmed that Properties.Settings.Default.Reload() is not being called?

Peter Ritchie

Hi everybody, using my original code, if I start the windows service first and then run the WinForms app, the 2nd line of following code will throw "UnauthorizedAccessException"

public Form_Admin()
{
this.sc = new ServiceController("service_name");
this.resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", outcreated);
InitializeComponent();
}

UnhandledSystem.UnauthorizedAccessException
Message="ā€œresetStockSettingsā€Ā?Access Denied"
Source="mscorlib"
StackTrace:
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Threading.EventWaitHandle..ctor(Boolean initialState, EventResetMode mode, String name, Boolean& createdNew, EventWaitHandleSecurity eventSecurity)
at System.Threading.EventWaitHandle..ctor(Boolean initialState, EventResetMode mode, String name, Boolean& createdNew)
at ServiceAdmin.Form_Admin..ctor() position D:\StockInfoCollector\ServiceAdmin\Form_Admin.cs:line 24
at ServiceAdmin.Program.Main()position D:\StockInfoCollector\ServiceAdmin\Program.cs:line 17
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

If I run the management app first and use the "Start" button on the form to start the service, no exception occurs but the service doesn't read stock information at all.However, new settings can be saved into the StockInfoFile.exe.config file.

Thanks for your patienceļ¼?br>

zeyansoft

It looks like you most likely have an error with code access security.

Try adding the following lines where you create the EventWaitHandle in the service:

Code Snippet

EventWaitHandleSecurity evhSec = new EventWaitHandleSecurity();

EventWaitHandleAccessRule evhAccessRule = new EventWaitHandleAccessRule("Everyone", EventWaitHandleRights.FullControl, AccessControlType.Allow);

evhSec.AddAccessRule(evhAccessRule);

EventWaitHandle mynewevent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", out value, evhSec);

You may need to modify the code for a particular user or group of users in the EventWaitHandleAccessRule constructor ("Everyone" is pretty broad.)

This may work. If not, let us know under which account the service is running under.

David M Morton

Thank you very much, David and Peter! There is still one issue. The windows service can now run normally after being started. But if I change the stock code from the Windows Forms app, the CPU usage increases to99%-100%. From the Process Explorer tool, Ifind my windows service contributes to this full CPU usage.From then on, the windows service doesn'tdownload any new data from the internet. However, the new stock code i input from windows formshas been saved into StockInfoFile.exe.config file.Is there something wrong with my listener thread? Does it cause an infinite loop? Please have a look. Ifeel we will solve this problem very soon.

zeyansoft
Try using the EventWaitHandle in EventResetMode.AutoReset instead of ManualReset. The Service is probably blowing through the WaitOne statements, because a manual reset event isn't automatically turned off once another thread has the event. AutoReset will ensure that the event is reset at the time the other thread receives the signal.

David M Morton
zeyansoft wrote:

Hi everyone, i'm developing a web crawler solution that reads specific stock infomation from mutiple websites. This solution includes a windows serviceproject that the web crawler runs on, and amanagementUI project using Windows Forms. User canspecify stock symbol ("MSFT" for example )from themanagement UI and then this valueispassed to the service, so the service can gather user specified stock information once per minute from the Internet. The stock symbol is stored in the app.config file under the windows service project. I try to use EventWaitHandle object to make communication between the management UI and thewindows service. I'm not familiar with event synchronizationso please have a look at why it doesn't work. Thanks!

1. This is the code running inside the windows service:

public class InfoServer
{
private System.Timers.Timer serviceTimer; //Interval

private EventWaitHandle resetSettingsEvent;

private Thread listenerThread; //anew threadlisten for event notification

private bool created;

public InfoServer()
{
this.resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", out created);
this.serviceTimer = new System.Timers.Timer();
this.serviceTimer.Elapsed += new ElapsedEventHandler(Run);

this.serviceTimer.Interval = 60000;

this.serviceTimer.Start();
this.serviceTimer.Enabled = true;
this.listenerThread = new Thread(new ThreadStart(this.Listen));
this.listenerThread.Name = "Listener";
this.listenerThread.Start();
}

public void Run(object source, ElapsedEventArgs e)
{
YahooAccess yahoo = new YahooAccess(Properties.Settings.Default.StockCode);
yahoo.Connect();
yahoo.WrapData();
yahoo.InsertData();
}

public void Listen() //

{
while (true)
{
if (this.resetSettingsEvent.WaitOne(1000, false))

{
Properties.Settings.Default.Reload(); //Load new settings in app.config

}

}

}

public void Stop()
{
this.serviceTimer.Enabled = false;

}

}

2. Here issome codefrom the management Form class definition:

...

public Form_Admin()
{
this.sc = new ServiceController("service_name");
this.resetSettingsEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "resetStockSettings", outcreated);
InitializeComponent();
}

private void ChangeSettings() //Modify thestock code setting

{
string configPath = "D:\\Test\\StockInfoFile.exe.config";
XmlDocument configXML = new XmlDocument();
configXML.Load(configPath);
XmlNode configNode = configXML.SelectSingleNode("/configuration/applicationSettings/StockInfoCollector.Properties.Settings/setting[@name='StockCode']/value");
configNode.InnerText = this.textBox_StockCode.Text;
configXML.Save(configPath);
this.resetSettingsEvent.Set(); //notify the windows service

}

...

Be sure to add a resetSettingsEvent.Reset() before or after the Reload call.
Peter Ritchie

Thanks a lot, Peter and David. Both apps are running normally now. I really learned a lot in the past couple of days from you.Both of you are really high-level experts

Kind

Regards

Zeyan

zeyansoft

You can use google to search for other answers

Custom Search

More Threads

• How to Use FileSystemWatcher in Network Directory
• How to open a zip file using C# ... Please Help
• Framework Culture Info not changing after chaning windows regional settings
• When should one use multiple Lock objects?
• Question about BinaryReader?
• Xp/Vista style scrollbar
• Help transmitting files asynchronously
• How to get the MSI file startup location in my installcalss project?
• Application blocks.
• Getting all Roles of a user using WindowsTokenRoleProvider