Using EventWaitHandles between Windows Service and Windows Application - c#

I have a requirement to send a signal from a Windows Service to a Windows console/Forms application using Named Events. I created a common NamedEvents class library which has the implementation to create events.
The issue is that my windows service is not creating the event. If I create another Windows Forms application (in place of the windows service) to create a event then it works fine. But with Windows Service it doesn't seem to work. Can someone please advise me if I am missing something in my code
public static class NamedEvents
{
public static EventWaitHandle OpenOrCreate(string name, bool initialState, EventResetMode mode)
{
EventWaitHandle ewh = null;
try
{
ewh = EventWaitHandle.OpenExisting(name);
}
catch (WaitHandleCannotBeOpenedException)
{
ewh = new EventWaitHandle(initialState, mode, name);
}
return ewh;
}
public static EventWaitHandle OpenOrWait(string name)
{
EventWaitHandle ewh = null;
while (null == ewh)
{
try
{
ewh = EventWaitHandle.OpenExisting(name);
}
catch (WaitHandleCannotBeOpenedException)
{
Thread.Sleep(50);
}
}
return ewh;
}
}
Windows Service Code:
public void SetSignalToClient()
{
EventWaitHandle completedA = NamedEvents.OpenOrCreate("CompletedA", false, EventResetMode.ManualReset);
completedA.Set();
completedA.Close();
}
Windows Forms Application:
public Form1()
{
InitializeComponent();
new Task((o) => SubscribeToAsyncEvents(),
new System.Threading.CancellationToken()).Start();
}
private void SubscribeToAsyncEvents()
{
while (true)
{
EventWaitHandle completedA = NamedEvents.OpenOrWait("CompletedA");
completedA.WaitOne();
if (textBox1.InvokeRequired)
{
textBox1.Invoke((MethodInvoker)delegate { textBox1.Text = "received"; });
}
completedA.Close();
}
}

This is documented in the MSDN article for CreateEvent. In order to use an event object across multiple Terminal Services sessions (also known as Remote Desktop sessions) you must create a global event object, which you can do by starting the name that with the prefix Global\, e.g., Global\MyEventName.
While the documentation does not mention it, to the best of my knowledge, the .NET EventWaitHandle class will pass the event name to CreateEvent unchanged. So the same mechanism should work.
Windows Services run in session 0, and user applications run in session 1 or higher. So for a service and a user application to communicate, they must use global objects.
Note that if the user application is not running as an administrator, the service will probably also need to explicitly set the permissions on the event object.

Related

How to communicate between a windows service and windows form with a WCF?

I have a Timeout after this timeout I have a send a message between windows service and windows form with a WCF.
I can already send a message from windows form to a windows service :
WCF
public class Interact : IInteract
{
private Func<string, int> callback;
public Interact(Func<string, int> callback)
{
this.callback = callback;
}
public void SendRequest(string name)
{
var output = this.callback(name + " callback");
}
public string AskIfAlive()
{
return "ask";
}
}
Service windows
public partial class LMService : ServiceBase
{
private ServiceHost host;
public LMService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Class1.Init();
Interact instance = new Interact(ReceiveMsg);
host = new ServiceHost(instance);
host.Open();
}
private int ReceiveMsg(string data)
{
// Message from Windows form to windows service
Log.writeEventLog(data);
return 1;
}
}
Timer
public static class Class1
{
static int Timeout = 0;
static Timer tm = new Timer();
public static void Init()
{
tm.Interval = 1000;
tm.Elapsed += Tm_Elapsed;
tm.Start();
}
private static void Tm_Elapsed(object sender, ElapsedEventArgs e)
{
Timeout++;
if(Timeout >= 10)
{
// SEND MESSAGE TO WINDOWS FORM
tm.Stop();
}
}
}
I want to send something to the windows form after the timeout in windows service but I don't know how to do it someone can help me ?
You can use a Duplex Service to allow for the service to also send messages to the Windows Forms app.
See this link for details and a sample.
A duplex service is a WCF service that receives a callback that it can use to send messages to the client. In addition to the service contract, you also need to create a callback contract that is used by the service.
Please note that the callback is tied to the WCF operation context. You might need to change the static Init method of your sample to an instance method and create a new instance of Class1 per call/client.
Whether the service we designed is duplex or request/response model. The initiator of the service is client-side, therefore we should host the service in Windows form application instead of the Windows NT service.
Besides, your Windows NT service seems to send a message to the Windows Form application once. Therefore, I don’t think it is necessary to use the duplex service.
About the duplex service, I have ever made an example. Wish it is useful for you.
TimeOut exception in WCF while implementing duplex
Feel free to let me know if there is anything I can help with.

How to send strings to a WinForms application from another application using methods

I have an EXE that I've created called logger which is a simple WinForms application. It has a richtextbox and that's pretty much it.
I then also have a suite of other applications. What I want to be able to do with these apps is to be able to get them to write output to the logger.exe I have full control over the code of all applications.
I understand I could do a process.start and specify arguments but I want these apps to be able to write to the richtextbox at will based on the methods being called within them.
I was hoping I could create an api in the logger.exe that would expose a method for appending the richtextbox.
Does anyone have any tips on how I might achieve this?
EDIT: This is what I have so far:
namespace ScreenLog
{
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public partial class Logger : Form, IFromClientToServerMessages
{
public Logger()
{
InitializeComponent();
}
public void DisplayTextOnServerAsFromThisClient(string text)
{
LogConsole.AppendText(Environment.NewLine + text);
}
}
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
[OperationContract(IsOneWay = false)]
void DisplayTextOnServerAsFromThisClient(string message);
}
}
As you might have already guessed you would need any of IPC(Inter Process Communication) mechanism to send messages between different processes(Applications). WCF is one of the option, You could implement a simple WCF service module which uses net.pipe binding. This service can be hosted in managed application. In your case this service can be hosted in your logger application.
Note: If you want to host a WCF application in a managed application, Particular managed application(Logger) should have admin privilege.
Implementation of Logger Form
partial class declaration
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public partial class Logger: Form, IFromClientToServerMessages
Introduce Interface for communication
This interface should be added to a assembly which is accessible by both Logger application and any other application which sends message to logger.
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
[OperationContract(IsOneWay = false)]
void DisplayTextOnServerAsFromThisClient(string message);
}
Implementing Interface
Add the following method implementation to your Logger form
public void DisplayTextOnServerAsFromThisClient(string text)
{
//Add proper logic to set value to rich text box control.
richtextbox = text;
}
Hosting the WCF service in logger application
Invoke HostTheNetPipeService() within the constructor of Logger Form
private void HostTheNetPipeService()
{
serverHost = new ServiceHost(this);
serverHost.AddServiceEndpoint((typeof(IFromClientToServerMessages)), new NetNamedPipeBinding(), "net.pipe://127.0.0.1/Server");
serverHost.Open();
}
Call the service from other applications to send message/text
private void SendMessageToLogger()
{
using (ChannelFactory<IFromClientToServerMessages> factory = new ChannelFactory<IFromClientToServerMessages>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/Server")))
{
IFromClientToServerMessages clientToServerChannel = factory.CreateChannel();
try
{
clientToServerChannel.DisplayTextOnServerAsFromThisClient("Message to be displayed");
}
catch (Exception ex)
{
}
finally
{
CloseChannel((ICommunicationObject)clientToServerChannel);
}
}
}
Closing the communication channel
private void CloseChannel(ICommunicationObject channel)
{
try
{
channel.Close();
}
catch (Exception ex)
{
}
finally
{
channel.Abort();
}
}

Share an object instance between multiple session/application instances in ASP.NET MVC

I am developing a project for a log monitor and I am using an ASP.NET application with SignalR.
The main objective of the application is to provide a monitor of error logs in multiple clients across different locations (LCD monitors). Every moment when a log error is created in database, the application should notify all the clients with the new error.
I am wondering to create a static Timer variable in the web application, that will be started by the Application_Start method.
But, knowing the application will have a single thread per session, I think the web server will have a lot of timers running together.
I need to know how to make this Timer instance unique for all the session instances in the web server.
Application_Start is not triggered by a new session, but by the start of the application. If you initialize your timer in Application_Start, you don't need to worry about multiple timer instances.
You can create an instance class that has a timer.
For instance:
public class MyTimerHolder
{
private static Lazy<MyTimerHolder> _instance = new Lazy<MyTimerHolder>(() => new MyTimerHolder());
private readonly TimeSpan _checkPeriod = TimeSpan.FromSeconds(3);
private IHubContext _hubProxy;
// Threaded timer
private Timer _timer;
public MyTimerHolder()
{
_timer = new Timer(CheckDB, null, _checkPeriod, _checkPeriod);
}
public void BroadcastToHub(IHubContext context)
{
_hubProxy = context;
}
public void CheckDB(object state)
{
if (_hubProxy != null)
{
// Logic to check your database
_hubProxy.Clients.All.foo("Whatever data you want to pass");
}
}
public static MyTimerHolder Instance
{
get
{
return _instance.Value;
}
}
}
Then you can change the hubContext at any point from any method. So lets say you want to broadcast to clients connected to hub "MyDBCheckHub". At any point in your application all you have to do is:
MyTimerHolder.Instance.BroadcastToHub(GlobalHost.ConnectionManager.GetHubContext<MyDBCheckHub>());
You could throw this in your application start or wherever you please, there'll only be 1 instance of MyTimerHolder within the app domain.

How to create a Task Scheduler App

I have been task with (ha) creating an application that will allow the users to schedule a command line app we have with a parameter.
So the command line app takes an xml and "runs it"
So bottom line I either need to create a windows service or learn how to interact with the Task Scheduler service already running on the box (version 1 Xp /2003)
At first I though it would be easy have a service run and when a job is submitted, calculate the time between now and run and set up a timer to wait that amount of time. This is better then checking every minute if it's time to run.
Were I hit a wall is I relized I do not know how to communicate with a running windows service. Except maybe create a file with details and have the service with a file watcher to load the file and modify the schedule.
So the underlying questions are how can I execute this psedo code
from client
serviceThatIsRunning.Add(Job)
Or ineracting with the task schedule or creating .job files using c# 3.5
Edit:
To clarify I created a small sample to get my thoughts on "paper"
So I have a Job Class
public class Job
{
#region Properties
public string JobName { get; set; }
public string JobXML { get; set; }
private Timer _JobTimer;
public Timer JobTimer
{
get
{
return _JobTimer;
}
}
#endregion
public void SetJobTimer(TimeSpan time)
{
if (_JobTimer != null)
{
_JobTimer.Dispose();
}
_JobTimer = new Timer(new TimerCallback(RunJob), null, time, time);
}
private void RunJob(Object state)
{
System.Diagnostics.Debug.WriteLine(String.Format("The {0} Job would have ran with file {1}", JobName, JobXML));
}
public override string ToString()
{
return JobName;
}
public void StopTimer()
{
_JobTimer.Dispose();
}
}
Now I need to create an App to house these Jobs that is constantly running, that is why I though of Windows Services, and then a Windows app to allow the user to work with the Job List.
So the question is if I create a Windows Service how do I interact with methods in that service so I can change the JobList, add, delete, change.
Here is a small windows app I created to show that the Job class does run. Interesting point, If I am doing this correctly, I do not add the Job to a listbox and the Add method exits the Job Timer portion still runs and does not get picked up by the Garbage Collector.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnAddJob_Click(object sender, EventArgs e)
{
Job job = new Job();
job.JobName = txtJobName.Text;
job.JobXML = txtJobXML.Text;
job.SetJobTimer(new TimeSpan(0, 0, Convert.ToInt32(JobTime.Value)));
// ??Even If I don't add the Job to a list or ListBox it seems
// ??to stay alive and not picked up by the GC
listBox1.Items.Add(job);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex > -1)
{
Job job = listBox1.Items[listBox1.SelectedIndex] as Job;
txtJobName.Text = job.JobName;
txtJobXML.Text = job.JobXML;
}
}
private void btnRemove_Click(object sender, EventArgs e)
{
Job job = listBox1.Items[listBox1.SelectedIndex] as Job;
job.StopTimer();
listBox1.Items.Remove(job);
}
private void btnCollect_Click(object sender, EventArgs e)
{
GC.Collect();
}
}
If you want to schedule a task using the task scheduler it could be as simple as below. You just need to customize the command line arguments that you pass to schtasks for your needs. See this link for a detailed explanation of command line arguments.
Process p = Process.Start("schtasks", commandArgs);
p.WaitForExit();
If you want to start multiple tasks that run at different time intervals, you can
create for instance a class JobThread that defines a timer that is initialized using the Initialize method:
m_timer = new Timer(new TimerCallback(this.timerHandler), null, this.Interval, this.Interval);
Furthermore, this class defines a List of Job objects. These jobs are executed from the timerHandler.
Finally, you create a singleton JobManager class that defines a Start and Stop method.
In the Start method you do something like this:
foreach (var jobThread in this.m_jobThreads)
{
jobThread.Initialize();
}
This JobManager has also a Initiliaze method that accepts a XmlNode parameter. This method will parse the Xml-job you pass from the command-line.
There was an answer on this thread that is no longer there but, I am going to try to create a listener by keeping a port open
WCF through Windows Services
http://msdn.microsoft.com/en-us/library/ms733069.aspx
Also adding the attribute
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
Helps to keep state of the service.

.Net Remoting to WCF Challenge!

I am trying to migrate my .net remoting code to wcf but I'm finding it difficult. Can someone help me migrate this simple Remoting based program below to use WCF? The program implements a simple publisher/subscriber pattern where we have a single TemperatureProviderProgram that publishers to many TemperatureSubcriberPrograms that subcribe to the TemperatureProvider.
To run the programs:
Copy the TemperatureProviderProgram and TemperatureSubcriberProgram into seperate console application projects.
Copying to remaining classes and interfaces into a common Class Library project then add a reference to System.Runtime.Remoting library
Add a reference to the Class Library project from the console app projects.
Complie and run 1 TemperatureProviderProgram and multiple TemperatureSubcriberProgram.
Please note no IIS or xml should be used. Thanks in advance.
public interface ITemperatureProvider
{
void Subcribe(ObjRef temperatureSubcriber);
}
[Serializable]
public sealed class TemperatureProvider : MarshalByRefObject, ITemperatureProvider
{
private readonly List<ITemperatureSubcriber> _temperatureSubcribers = new List<ITemperatureSubcriber>();
private readonly Random randomTemperature = new Random();
public void Subcribe(ObjRef temperatureSubcriber)
{
ITemperatureSubcriber tempSubcriber = (ITemperatureSubcriber)RemotingServices.Unmarshal(temperatureSubcriber);
lock (_temperatureSubcribers)
{
_temperatureSubcribers.Add(tempSubcriber);
}
}
public void Start()
{
Console.WriteLine("TemperatureProvider started...");
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
TcpServerChannel tcpChannel = new TcpServerChannel("TemperatureProviderChannel", 5001, provider);
ChannelServices.RegisterChannel(tcpChannel, false);
RemotingServices.Marshal(this, "TemperatureProvider", typeof(ITemperatureProvider));
while (true)
{
double nextTemp = randomTemperature.NextDouble();
lock (_temperatureSubcribers)
{
foreach (var item in _temperatureSubcribers)
{
try
{
item.OnTemperature(nextTemp);
}
catch (SocketException)
{}
catch(RemotingException)
{}
}
}
Thread.Sleep(200);
}
}
}
public interface ITemperatureSubcriber
{
void OnTemperature(double temperature);
}
[Serializable]
public sealed class TemperatureSubcriber : MarshalByRefObject, ITemperatureSubcriber
{
private ObjRef _clientRef;
private readonly Random portGen = new Random();
public void OnTemperature(double temperature)
{
Console.WriteLine(temperature);
}
public override object InitializeLifetimeService()
{
return null;
}
public void Start()
{
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
int port = portGen.Next(1, 65535);
TcpServerChannel tcpChannel = new TcpServerChannel(string.Format("TemperatureSubcriber_{0}", Guid.NewGuid()), port, provider);
ChannelServices.RegisterChannel(tcpChannel, false);
ITemperatureProvider p1 = (ITemperatureProvider)RemotingServices.Connect(typeof(ITemperatureProvider), "tcp://localhost:5001/TemperatureProvider");
_clientRef = RemotingServices.Marshal(this, string.Format("TemperatureSubcriber_{0}_{1}.rem", Environment.MachineName, Guid.NewGuid()));
p1.Subcribe(_clientRef);
}
}
public class TemperatureProviderProgram
{
static void Main(string[] args)
{
TemperatureProvider tp = new TemperatureProvider();
tp.Start();
}
}
public class TemperatureSubcriberProgram
{
static void Main(string[] args)
{
Console.WriteLine("Press any key to start TemperatureSubcriber.");
Console.ReadLine();
TemperatureSubcriber ts = new TemperatureSubcriber();
ts.Start();
Console.ReadLine();
}
}
In WCF, with a "push" from the server you're really talking about duplex comms; the MarshalByRefObject is largely redundant here (AFAIK). The page here discusses various scenarios, including duplex/callbacks.
If the issue is xml (for some philosophical reason), then simply using NetDataContractSerializer rather than DataContractSerializer might help.
The other approach is to have the clients "pull" data periodically; this works well if you need to support basic http, etc.
What it sounds like you want to do is use WCF NetTcpBinding with Callbacks.
Take a look at this: http://www.codeproject.com/KB/WCF/publisher_subscriber.aspx
"Learning WCF" by Michele Bustamante is also very good. You can get Chpt1 for VS2008 at her website along with the code for the book. Chpt1 will explain/demo setting up connections and such. She also has downloadable sample code. One of the Samples is a DuplexPublishSubscribe.
You will need to modify your logic a bit. If you want to migrate this app to WCF. You will need to have clients pull data from the service at regular intervals.
You will also need a Windows service or application to host the WCF like the console you are using in the previous code.
Well I build real time systems so polling is not an option - I need to push data.
Also I am finding there is no WCF equivalent of System.Runtime.Remoting.ObjRef! This is an extremely useful type that encapsulates a service endpoint and can be serialise and passed around the network to other remoting service.
Think I’ll be sticking with good old remoting until the ObjRef equivalent is introduced.
Yes it is true, just one correction..
ObjRefs are created automatically when any MarshalByRefObject derived object is going outside the appdomain.
So in this case your ITemperatureProvider interface Subscribe method shoud take ITemperatureSubscriber instead of objref.
And then on client side just call p1.Subscribe(this) and the remoting layer will generate ObjRef from the object that will be serialized and sent. (sending b reference)

Categories