I have a project that wants me to communicate with a server. I need to send data processed from a standalone application on client side to the server. For this I am using a wcf service that interacts with the server. This service is hosted in a windows service. Now my problem begins. I need to monitor a folder, write/read some files and delete them. For this I am using the same windows service as the one hosting the wcf service. How can I pass data between the two services? For example I would like to read a file using the windows service and pass the data to wcf service which then passes it to the server and back.
If you just want to be able to communicate between two services hosted in a windows service, one solution I have used is to store a static session state in the windows service itself. In Program.cs, I declare a static field which stores the session state, and I modify the constructors of my two services to take a reference to this object:
static class Program
{
private static SessionState sessionState = new SessionState() { SessionID = "100" };
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] servicesToRun;
servicesToRun = new ServiceBase[]
{
new Service1(sessionState),
new Service2(sessionState)
};
ServiceBase.Run(servicesToRun);
}
}
I have a class called SessionState which I use to store any data which I want to transfer between my hosted services. In this instance I am just giving the session an ID property:
public class SessionState
{
public string SessionID { get; set; }
}
Service1 and Service2 store a reference to the static session state, and in my example I just modify the sessionID in one of the two threads:
public partial class Service1 : ServiceBase
{
private SessionState sessionState;
public Service1(SessionState sessionState)
{
this.sessionState = sessionState;
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Console.WriteLine("Service 1 started.");
Task tsk = new Task(() => this.DoStuff());
tsk.Start();
}
protected override void OnStop()
{
}
private void DoStuff()
{
Console.WriteLine("Session state for service 1 is " + this.sessionState.SessionID);
Thread.Sleep(2000);
Console.WriteLine("Session state for service 1 is " + this.sessionState.SessionID);
}
}
public partial class Service2 : ServiceBase
{
private SessionState sessionState;
public Service2(SessionState sessionState)
{
this.sessionState = sessionState;
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Console.WriteLine("Service 2 started.");
Task tsk = new Task(() => this.DoStuff());
tsk.Start();
}
protected override void OnStop()
{
}
private void DoStuff()
{
Console.WriteLine("Session state for service 2 is " + this.sessionState.SessionID);
Thread.Sleep(1000);
this.sessionState.SessionID = "200";
Console.WriteLine("Session state for service 2 is " + this.sessionState.SessionID);
}
}
Now when I run the Windows Service (with a Console window attached) I get the following:
Hope this helps!
Alex
Related
The windows service i coded is not working:
Intended funcionality: The service is supposed to receive messages from Windows Message Queue (MSMQ) and write the messages on .txt files.
It works when I run it not-as-a-service (directly from visual studio)
When I installed it as a service i can start it, but it doesn't do anything, not creating/writing .txt files anywhere
(I know it isn't writing the files elsewhere because when I run the program from VS the messages are still in the queue, so they weren't taken out by the service)
The difference between running it as a service and running it from visual studio is the next:
namespace ComponentAtrapador
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
#if DEBUG
Service1 myService = new Service1();
myService.startMethod();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
#endif
}
}
}
(so if i run it as DEBUG it will run without me having to install it as a service)
Here's the service's code:
namespace ComponentAtrapador
{
public partial class Service1 : ServiceBase
{
private System.Timers.Timer _timer;
private System.ComponentModel.IContainer components1;
private System.Diagnostics.EventLog eventLog1;
public void startMethod()
{
OnStart(null);
}
public Service1()
{
InitializeComponent();
eventLog1 = new System.Diagnostics.EventLog();
if (!System.Diagnostics.EventLog.SourceExists("MySource"))
{
System.Diagnostics.EventLog.CreateEventSource(
"MySource", "MyNewLog");
}
eventLog1.Source = "MySource";
eventLog1.Log = "MyNewLog";
}
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("In OnStart");
_timer = new System.Timers.Timer();
_timer.Interval = 5000; // 5 seconds
_timer.Elapsed += new System.Timers.ElapsedEventHandler(this.OnTimer);
_timer.Start();
}
protected override void OnStop()
{
_timer.Stop();
}
public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)
{
string nombreArchivo = "archivoMensaje";
MessageQueue messageQueue = new MessageQueue(#".\Private$\SomeTestName");
System.Messaging.Message[] messages = messageQueue.GetAllMessages();
System.Messaging.Message m = new System.Messaging.Message();
foreach (System.Messaging.Message message in messages)
{
message.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
string text = message.Body.ToString();
System.IO.File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + nombreArchivo + Properties.Settings.Default.SettingNumero + ".txt", text);
Properties.Settings.Default.SettingNumero++;
Properties.Settings.Default.Save();
//Do something with the message.
}
// after all processing, delete all the messages
messageQueue.Purge();
//}
}
}
}
That is where Logs are so helpful, put couple of eventLog1.WriteEntry() inside your OnTimer method and you will see where is it failing.
I would check
how many messages I am getting.
I will put one eventLog1.WriteEntry() inside loop to see what is happening with each message etc...
Turns out the service didn't have enough permissions, fixed it by setting the serviceProcessInstaller of the service on visual studio to User, so that when i installed it it'd ask for credentials. Just had to type "./[username]" when it asked for my username for it to work.
Another way of fixing it would be going into the task manager > services > right click service > properties > security. And change the permissions there.
I have developed a Windows Service whose task is actually to start a host with particular url and port. Below is what I have now.
Program.cs
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new WindowsDxService()
};
ServiceBase.Run(ServicesToRun);
}
ProjectInstaller.cs
[RunInstaller(true)]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
}
WindowsDxService.cs
public partial class WindowsDxService : ServiceBase
{
public WindowsDxService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
var url = "http://127.0.0.1:9000";
using (var host = new NancyHost(new Uri(url)))
{
host.Start();
}
}
}
Configuration on serviceProcessInstaller1 and serviceInstaller1 in ProjectInstaller.cs [Design] file.
serviceProcessInstaller1
Account=LocalSystem
serviceInstaller1
StartType=Automatic
Library.cs
public class Library : NancyModule
{
public Library()
{
Get["/"] = parameters =>
{
return "Hello world";
};
Get["jsontest"] = parameters =>
{
var test = new
{
Name = "Guruprasad Rao",
Twitter="#kshkrao3",
Occupation="Software Developer"
};
return Response.AsJson(test);
};
}
}
Basically I followed this tutorial which actually shows how to do it with Console application which I succeeded though, but I wanted to have this as Windows Service which actually starts a host with specified port whenever the system starts. The service is started successfully and running but whenever I browse the url in the same system its not showing up the page, which means our basic This webpage is not available message. What else configuration I have to do so as to start the host? Hoping for a help.
You are disposing the host when you start your service. I would suggest something like this:
public partial class WindowsDxService : ServiceBase
{
private Host host;
public WindowsDxService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
this.host = new NancyHost(...)
this.host.Start();
}
protected override void OnStop()
{
this.host.Stop();
this.host.Dispose();
}
}
You'd probably find it a lot easier to write the service if you used TopShelf library.
I have an application that schedules jobs using Quartz.Net. It works on my development laptop perfectly both as a winforms application (with start and stop buttons) and as a Windows Services whose OnStart() and OnStop() event code matches the start and stop button code of the winforms application. They're both in the same solution using the same "model" code (in its own project).
If I run the winforms application on the production computer it works perfectly, the jobs are executed according to their schedule as expected. However if I install and run it as a Windows Service on the production PC nothing happens! The jobs do not run.
I have no idea how to debug this. Please let me know if you have any suggestions as to what might be wrong.
Also please let me know what other information I should be providing.
Oh - dev PC is running Windows 7, production PC is running Windows 8.1! Could that be the problem? I built the service by following this tutorial: http://msdn.microsoft.com/en-us/library/zt39148a(v=vs.110).aspx which does not indicate that anything special needs to be done for deploying to Windows 8?
Could this have something to do with environment variables (which I know nothing about)?
Here is some code which may be relevant:
The service:
namespace DataPump
{
public partial class DataPumpService : ServiceBase
{
private TaskManager _taskManager;
public DataPumpService()
{
InitializeComponent();
_taskManager = new TaskManager();
}
protected override void OnStart(string[] args)
{
_taskManager.Go();
}
protected override void OnStop()
{
_taskManager.Stop();
}
}
}
The form code (different project):
namespace DataPump
{
public partial class Form1 : Form
{
private TaskManager _taskManager = new TaskManager();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
_taskManager.Go(); //Loops infinitely, does not block
label1.Text = "Running...";
}
private void button2_Click(object sender, EventArgs e)
{
label1.Text = "Stopping...";
_taskManager.Stop();
label1.Text = "Idle";
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_taskManager.Stop();
}
}
}
Selected code from TaskManager code (third project which the first two each reference):
public class TaskManager
{
//...
private IScheduler _scheduler = StdSchedulerFactory.GetDefaultScheduler();
//...
public void Go()
{
if (_scheduler.GetCurrentlyExecutingJobs().Count() == 0)
{
_scheduler.Start();
_scheduler.AddCalendar(CalendarName, MakeSAPublicHolidayCalendar(), false, true);
foreach (DatapumpJob job in JobList)
{
_scheduler.ScheduleJob(MakeJob(job), MakeTriggerCron(job));
}
}
}
//...
public void Stop()
{
foreach (string name in _scheduler.GetCurrentlyExecutingJobs().Select(j => j.JobDetail.Key.Name))
{
_scheduler.Interrupt(new JobKey(name));
}
_scheduler.Shutdown(true);
}
//...
}
Where JobList is a get only property that generates a List<DatapumpJob>where DatapumpJob implements IInterrutableJob but adds common features including a job name which gets use by the three methods beginning Make... which are all private methods within the TaskManager class.
This code is to answer a question from the comments regarding ServiceBase.Run():
Program.cs (auto-generated):
namespace DataPump
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new DataPumpService()
};
ServiceBase.Run(ServicesToRun);
}
}
}
This turned out to be a network permissions issue. The service was running, it was just unable to access the network drive. So really my question was mi-specified.
After trying this: https://serverfault.com/questions/177139/windows-service-cant-access-network-share we eventually got it to work by setting the service to run as a specific user account on the PC.
I have created a public method In my Service to write an error log from my client machine as
public partial class ATEServiceLog : ServiceBase
{
public ATEServiceLog()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
TcpChannel tcp = new TcpChannel(9090);
ChannelServices.RegisterChannel(tcp, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(AteBAC), "ATE", WellKnownObjectMode.Singleton);
}
protected override void OnStop()
{
}
public void OnWriteErrorLog(string error)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(Application.StartupPath+#"\ATELogCheck.txt", true))
{
file.WriteLine(error);
}
}
}
I have created the last method to write logs. How can I call this method from my client machine. I tried something like
NetNamedPipeBinding binding = new NetNamedPipeBinding();
ATEService.ATEServiceLog myService = new ATEService.ATEServiceLog(binding,
new EndpointAddress("tcp://localhost/ATEServiceLog"));
myService.OnWriteErrorLog("Error From My Client");
It is giving compile time error as 'ATEService.ATEServiceLog' does not contain a constructor that takes '2' arguments. How can I call my method. How to implement the error logging in the server. My service will run on server and will have multiple clients. I have to write all the clients errors in server using windows service.
I am new To windows service. I need a windows service that reads an entry from a table from database. I have a CONSOLE APP where I add new project WINDOWS SERVICE. I already have a method that access the database, and other methods. I can put a thread on start that reads the database. Where do I put the thread? ( how can I do that). Where on WINDOWS SERVICE I add those methods? I have the Windows Service like this:
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
do
{
thread.start();
bool variab = readFromDatabase (Database table);
}
}
protected override void OnStop()
{
}
I suggest that you create a class in which you do everything you need and create in in the service:
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
YourClass cl = new YourClass();
cl.DoWhatYouNeed(...);
}
protected override void OnStop()
{
}
This gives you opportunity to run and test your class separate from service, maybe during debug release.
With windows services usually a method is created to execute the main loop of the service, in a separated thread. Otherwise the service could become unresponsive. For example, you can have a method called MainLoop to execute the service logic. Use the OnStart method only to do the initializing tasks, such as read configuration values or start the threads of the service. And use the OnStop to executing cleaning tasks, stopping threads, etc...
Thread _workerThread;
bool _shouldStop;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try{
_workerThread = new Thread(new ThreadStart(MainLoop));
_shouldStop = false;
_workerThread.Start();
}
catch{}
}
private void MainLoop()
{
while (!_shouldStop)
{
try{
//your logic here
}
catch{}
}
}
protected override void OnStop()
{
_shouldStop = true;
}
You must put your code or class, which contain data access logic in OnStart method