How to call my own defined method from Windows Service - c#

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.

Related

Access Singleton via Named pipe creates second singleton. Events not fired on host side

I have two processes and the second process needs to have access to a singleton in the first process. So I wrote a server that should help sharing the instance.
Something is wrong though, it seems like the client gets its own version of the singleton rather than the original instance.
The minimal example comes in two projects. Here is the client:
Program
using IPCServer;
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;
namespace IPCEventTest
{
class IPCClient
{
static void Main(string[] args)
{
Process ipcserver = Process.Start("IPCServer.exe");
Thread.Sleep(2000);
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main Start Connect");
Module module = Connect("WellKnownName"); //this name is used by the host
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main End Connect");
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main Start Raise");
module.RaiseEvent(); //raise event should raise within the server process
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main End Raise");
while (true) ;
}
public static Module Connect(string id)
{
Console.WriteLine("Start Connect");
ChannelFactory<IModuleServer> pipeFactory =
new ChannelFactory<IModuleServer>(
new NetNamedPipeBinding(),
new EndpointAddress($"net.pipe://localhost/{id}")
);
IModuleServer serverProxy = pipeFactory.CreateChannel();
Module ret = serverProxy.GetModule();
Console.WriteLine("End Connect");
return ret;
}
}
}
The following files set up the Host:
Program
using System;
namespace IPCServer
{
class Program
{
static HOST host;
static void Main(string[] args)
{
host = new HOST("WellKnownName");
Module.Instance.myevent += Instance_myevent;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Server Subscribed to {Module.Instance.id}");
while (true) ;
}
private static void Instance_myevent(object sender, EventArgs e)
{
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Server Event Fired from {(sender as Module).id}");
}
}
}
Module
using System;
using System.Linq;
namespace IPCServer
{
public class Module
{
public static Module Instance { get; } = new Module();
public event EventHandler myevent = delegate { };
public string id;
private Module()
{
var guid4 = Guid.NewGuid().ToString().Take(4);
id = new String(guid4.ToArray());
Console.WriteLine($"Module Constructor {id}");
myevent += Module_myevent;
}
private void Module_myevent(object sender, EventArgs e)
{
Console.WriteLine($"Module Listener {(sender as Module).id}");
}
public void RaiseEvent()
{
Console.WriteLine($"Module Start Raise {id}");
myevent(this, EventArgs.Empty);
Console.WriteLine($"Module End Raise {id}");
}
}
}
Host
using System;
using System.ServiceModel;
namespace IPCServer
{
internal class HOST
{
ServiceHost host;
internal HOST(string id)
{
host = new ServiceHost(typeof(ModuleServer), new Uri[] { new Uri("net.pipe://localhost") });
host.AddServiceEndpoint(typeof(IModuleServer), new NetNamedPipeBinding(), id);
host.Open();
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Host Opened");
}
~HOST()
{
if (host.State == CommunicationState.Opened)
{
host.Close();
}
host = null;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Host Destructed");
}
}
[ServiceContract]
public interface IModuleServer
{
[OperationContract]
Module GetModule();
}
public class ModuleServer : IModuleServer
{
public Module GetModule()
{
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} ModuleServer start GetModule");
Module ret = Module.Instance;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} ModuleServer end GetModule");
return ret;
}
}
}
The example runs and here is the output on my system:
Why am i not getting my Singleton from the server process.
Why is my event not raised in the server.
Edit: The Server opens the host and also subscribes to the singleton. After the client connects it raises the event via member function. Event has two subscribers, one in the constructor and one on the server side. only the Module internal subscription is handled - there is no eventhandling on the server side - no event is fired on the server side. The module listener is triggered but not inside the host process. This event is handled on the client side.
Why am i not getting my Singleton from the server process
Named pipes use Serialization to pass the object from server to client. That means that the client side have to re-run the constructor and copy existing properties.
public string id; is a field so it won't be 'copied' so the random value set by the constructor is left. That's why you have different Ids for the "same" object.
To solve this, you can change this to :
[DataContract]
public class Module
{
[DataMember]
public string id {get; set;}
}
Why is my event not raised in the server.
This is not how WCF named pipes works as you just have a duplicate version a client side. I suggest you to read about Duplex Channel

Windows service with Nancy does not start host

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.

Start service on other machine: service on Local Computer started and then stopped

I created a C# windows service and installed it successfully on my local developer machine (it works well).
Now I'm trying to install the service on a different machine.
I copied the "Release" folder to the new machine and installed the service.
When I start the service on the new machine I get the following error:
"service on Local Computer started and then stopped. Some services stop automatically if they are not in use by other services or programs."
I don't get any message to the Application event log, I even added debug message as the first line of the program, but i see nothing in the Event Viewer (as if the code doesn't start at all). :/
What have i done wrong?
"started and then stopped" message usually appears when your server throws an exception during start up. Which could be for many reasons, including invalid paths and inability to write to the Application Event Log due to missing source or insufficient privileges.
I usually include an option to run my service as a console app.
Which allows me to display any exceptions using Console.WriteLine.
Following assumes your service extends from System.ServiceProcess.ServiceBase.
partial class MyService : ServiceBase
{
private static void Main(string[] args)
{
MyService svc = new MyService();
if (Environment.UserInteractive)
RunConsole(args, svc);
else
Run(svc);
}
public MyService()
{
InitializeComponent();
}
protected static bool KeepRunning { get; set; }
protected override void OnStart(string[] args)
{
StartServiceHost();
}
protected override void OnStop()
{
StopServiceHost();
}
protected override void OnShutdown()
{
StopServiceHost();
base.OnShutdown();
}
private static void RunConsole(string[] args, ConverterService svc)
{
// need to hold on to Ctrl+C, otherwise StopServiceHost() never gets called
Console.CancelKeyPress += (sender, e) => ShutDown(svc);
KeepRunning = true;
svc.OnStart(args);
Console.WriteLine("Press <Ctrl+C> to exit.");
while (KeepRunning)
{
Console.ReadLine();
}
}
private void StartServiceHost()
{
// start your service
}
private void StopServiceHost()
{
// stop your service
}
private static void ShutDown(MyService svc)
{
Console.WriteLine("exiting...");
svc.OnStop();
KeepRunning = false;
}
}
Well, I've found the problem:
I used password decryption with DataProctionScope.LocalMachine.
So when I changed the machine- the decryption failed.
I had to re-encrypt the passwords on the local machine and then the decryption worked fine.
Thank you for your responds!
*The eventlog debugging didn't work because of my fault.

Passing data between a wcf service and its hosting windows service

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

WCF Service host receives null object

Environment: .net 4.6.1 on Windows 10 x64
Problem:
1. WCF Contract implemented with an event.
2. Service host will fire the event every time an event log entry is logged and called by the client.
3. The host application triggers properly however the object (EventLogEntry) does not get posted properly and is always null.
4. I have checked during debug the client side Winforms application passes a instantiated object. This somehow doesn't make it to the
server side.
5. at this Stage both the client and the server are on the same machine.
Question:
Why is object null and how can I fix this?
WCF Service Code:
[ServiceContract]
public interface IloggerWCFServiceContract
{
[OperationContract(IsOneWay = true)]
void WriteEvent(EntryWrittenEventArgs e);
[OperationContract(IsOneWay = true)]
[ServiceKnownType(typeof(EventLogEntry))]
void OnEventWritten(EntryWrittenEventArgs e);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class wcf_logger_servicebase : IloggerWCFServiceContract
{
public event EventHandler<EntryWrittenEventArgs> eventwritten;
public void OnEventWritten(EntryWrittenEventArgs e)
{
if (eventwritten != null)
this.eventwritten(this, e);
}
public void WriteEvent(EntryWrittenEventArgs e)
{
OnEventWritten(new EntryWrittenEventArgs(e.Entry));
}
}
Wcf Winforms Server code:
wcf_logger_servicebase loggerWCFService = new wcf_logger_servicebase();
ServiceHost loggerServiceHost = new ServiceHost(loggerWCFService, new Uri(loggerGlobalStatics.namedPipeServerAddress));
loggerWCFService.eventwritten += new EventHandler<EntryWrittenEventArgs>(eventhandler_entryWritten);
.....
loggerServiceHost.AddServiceEndpoint(typeof(IloggerWCFServiceContract), new NetNamedPipeBinding(), loggerGlobalStatics.namedPipeServerAddress);
loggerServiceHost.Open();
......
private void eventhandler_entryWritten(object sender, EntryWrittenEventArgs e)
{
--->textBox1.Text += e.Entry.ToString() + "\r\n" ;
}
WCF Winforms Client code:
ChannelFactory<IloggerWCFServiceChannel> loggerChannelFactory;
IloggerWCFServiceChannel loggerServiceChannel = null;
wcf_logger_servicebase loggerWCFService = new wcf_logger_servicebase();
EndpointAddress serverEndpoint;
System.ServiceModel.Channels.Binding binding;
public client()
{
InitializeComponent();
serverEndpoint = new EndpointAddress(new Uri(loggerGlobalStatics.namedPipeServerAddress));
binding = new NetNamedPipeBinding();
loggerChannelFactory = new ChannelFactory<IloggerWCFServiceChannel>(binding, serverEndpoint);
loggerChannelFactory.Open();
loggerServiceChannel = loggerChannelFactory.CreateChannel();
loggerServiceChannel.Open();
....
private void eventLog1_EntryWritten(object sender, System.Diagnostics.EntryWrittenEventArgs e)
{
loggerServiceChannel.WriteEvent(e);
}
The reason is that EntryWrittenEventArgs has no setter for Entry.
See this SO question for more details.
Why not just send Entry directly without encapsulating it in EntryWrittenEventArgs?

Categories

Resources