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 |
|