I have Windows Service, which is running tasks on timer tick. I also selected to communicate with Windows Service through WCF named pipe channel. I can create WCF Service instance and open it for listening. But how I access objects relying in Windows Service via WCF?
This is what my Windows Service looks like:
public partial class MyService : ServiceBase
{
private ServiceHost m_svcHost = null;
private myObject = null;
...
// Run this method from external WCF client
private void SomeMethod()
{
}
protected override void OnStart(string[] args)
{
if (m_svcHost != null) m_svcHost.Close();
m_svcHost = new ServiceHost(typeof(MyCommunicationService));
m_svcHost.Open();
// initialize and work with myObject
}
protected override void OnStop()
{
if (m_svcHost != null)
{
m_svcHost.Close();
m_svcHost = null;
}
}
}
So what I want, to have access to myObject within WCF Service, when client will make inquiry. Or even run a method on myObject.
You can create a communication channel between your MyService (WCF Hosting entity) and the MyCommunicationService (WCF Service instance) simply using a static property:
//This can be put into a separate DLL if needed.
public interface IMyHostCallbacks
{
void HostCallback(string iSomeDataFromWCFToHost);
}
public static class Host
{
public static IMyHostCallbacks Current;
}
public partial class MyService : ServiceBase, IMyHostCallbacks
{
protected override void OnStart(string[] args)
{
//Set the static callback reference.
Host.Current = this;
if (m_svcHost != null) m_svcHost.Close();
m_svcHost = new ServiceHost(typeof(MyCommunicationService));
m_svcHost.Open();
// initialize and work with myObject
}
//Here you have data from WCF and myObject available.
void IMyHostCallbacks.HostCallback(string iSomeDataFromWCFToHost)
{
//Be careful here.
//Depending on your WCF service and WCF clients this might
//get called simultaneously from different threads.
lock(myObject)
{
myObject.DoSomething(iSomeDataFromWCFToHost);
}
}
}
Surely you can even put the static field into your MyService class but at least some abstraction between MyService and MyCommunicationService would not hurt.
Now, in any place in your MyCommunicationService you can:
void WCFServiceMethod()
{
Host.Current.HostCallback("some data");
}
Related
I have three projects:
- Client (Windows Forms)
- Domain (Business logic)
- Service (Server)
The Domain project have AsyncServer that is used by the Service project and AsyncClient that is used by the Client project.
I want that the client to get the same of instance of the service have, because I tried to add the Service as a reference and to make it static and it doesn't work, also I implemented the Singleton Pattern to it, and still doesn't work, because every time when I try to access the Service, it creates a new instance.
So is there a way to get the same instance of the Program class or another class from the Service project?
Thank you!
A part of code from Service project:
ChatService:
private static ChatService chatService;
private static readonly object syncObject = new object();
public AsyncServer AsyncServer { get; private set; }
private ChatService()
{
InitializeComponent();
AsyncServer = new AsyncServer();
}
public static ChatService GetInstance()
{
lock (syncObject)
{
if (chatService == null)
{
chatService = new ChatService();
}
}
return chatService;
}
Program: (the service is called with console parameter)
public static ChatService ChatServer { get; private set; }
public static void Main(string[] args)
{
if (args[0] == "console")
{
ChatServer = ChatService.GetInstance();
ChatServer.RunAsConsole(args);
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
ChatService.GetInstance()
};
ServiceBase.Run(ServicesToRun);
}
}
A part of code from Client project:
ChatForm:
private void OnConnectButtonClick(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(txtIP.Text) && !string.IsNullOrEmpty(txtName.Text))
{
asyncClient = new AsyncClient(txtIP.Text);
asyncClient.Connect();
asyncServer = Service.Program.ChatServer.AsyncServer;
asyncServer.ChatContentReceivedMethod += DisplayChatContent;
SetControlsForConnection();
}
}
If I debug the Client project, the ChatServer is null.
Let's assume we have the following two classes, How can we listen for Errors and if any error occurred, recreate the singleton? I have put together the following code, but would like to know if there is a pattern for safely raise error, dispose object and recreate it automatically?
`
static void Main(string[] args)
{
MyFirstClass.Instance.SayHello();
}
}
class MySecondClass
{
public int ID { get; set; }
public void SayHelloFromSecondClass()
{
Console.WriteLine("Say Hello From Second Class");
}
public MySecondClass(int id)
{
ID = id;
}
}
public sealed class MyFirstClass
{
private static readonly MyFirstClass instance = new MyFirstClass();
private static MySecondClass msc;
public event EventHandler ErrorOccuredEvent;
private MyFirstClass() { }
public static MyFirstClass Instance
{
get
{
msc = new MySecondClass(id: 1);
return instance;
}
}
public void SayHello()
{
Console.WriteLine("Hello World...");
}
static void ErrorOccured(object sender, EventArgs e)
{
Console.WriteLine("Oops");
msc = null;
Thread.Sleep(5000);
GC.Collect();
msc = new MySecondClass(id: 2);
}
}
`
If I understand well, MyFirstClass (which is a singleton) is a kind of wrapper around MySecondClass that turns MySecondClass into a singleton as well.
Let's call MyFirstClass: Wrapper
Let's call MySecondClass: Service
If the clients always consume the Service through the single instance of Wrapper, then re-creating a Wrapper will not help, because the clients might keep a reference to Wapper. Re-creating Service can help if the clients don't see it and cannot keep a reference to it. Therefore they must consume the service indirectly.
It's easiest to achieve this through an interface:
public interface IHelloService
{
void SayHello();
}
public class HelloService : IHelloService
{
public void SayHello()
{
Console.WriteLine("Hello");
}
}
public class HelloServiceWrapper : IHelloService
{
public static readonly IHelloService Instance = new HelloServiceWrapper();
private HelloServiceWrapper () {}
private IHelloService _service;
public void SayHello()
{
EnsureServiceAvailable();
_service.SayHello();
}
private void EnsureServiceAvailable()
{
if(_service == null) {
_service = new HelloService();
}
}
private void HandleError()
{
_service = null;
}
}
But if the error happens when the client is using the service ...
HelloServiceWrapper.Instace.SayHello();
... this call might fail.
You would have to re-create the service instantly in order to make succeed the client's call (assuming that re-creating the service will solve the problem and that the error will not occur again immediately):
public void SayHello()
{
try {
_service.SayHello();
} catch {
_service = new HelloService();
_service.SayHello();
}
}
Note: Disposing the service invalidates the object and makes any reference a client has to it invalid. But re-creating a new one does not give the client a new reference! You would need to have a reference to the clients reference in order to be able to give the client a new instance.
Is it possible to have an application that runs as a service if it is registered as such but if it is double clicked simply starts a regular interactive application?
Yes. You can use the Environment.UserInteractive variable. You will need to create a small wrapper around your service to expose the OnStart() and OnStop() methods since they are protected.
var service = new MyService();
if (Environment.UserInteractive)
{
service.Start(args);
Console.WriteLine("Press any key to stop program");
Console.Read();
service.Stop();
}
else
{
ServiceBase.Run(service);
}
Wrapper Class (Make sure to extend ServiceBase)
public partial class MyService : ServiceBase
{
protected override void OnStart(string[] args)
{
//start code
}
protected override void OnStop()
{
//stopcode
}
public void Start(string[] args)
{
OnStart(args);
}
}
I have a WCF service hosted in a consol application(that also acts as the Windows Service installer), pleas see more here : http://msdn.microsoft.com/en-us/library/ms733069.aspx
This is how the class in the consol application looks like :
public class MyAppWindowsService : ServiceBase
{
public ServiceHost _MyAppClientServiceHost = null;
public ServiceHost _MyAppIntegrationServiceHost = null;
public ServiceHost _MyAppserviceHost = null;
public MyAppWindowsService()
{
// Name the Windows Service
ServiceName = "MyApp Service";
}
public static void Main()
{
ServiceBase.Run(new MyAppWindowsService());
}
private void StopService(ServiceHost serviceHost)
{
if (serviceHost != null)
{
serviceHost.Close();
serviceHost = null;
}
}
private ServiceHost StartService(Type serviceType)
{
ServiceHost serviceHost = null;
// Create a ServiceHost for the CalculatorService type and
// provide the base address.
serviceHost = new ServiceHost(serviceType);
// Open the ServiceHostBase to create listeners and start
// listening for messages.
serviceHost.Open();
return serviceHost;
}
private void StartServices()
{
StopService(_MyAppClientServiceHost);
StopService(_MyAppIntegrationServiceHost);
StopService(_MyAppServiceHost);
_MyAppClientServiceHost = StartService(typeof(MyApp.ServiceImplementation.MyAppClientService));
_MyAppIntegrationServiceHost = StartService(typeof(MyApp.ServiceImplementation.MyAppIntegration));
_MyAppServiceHost = StartService(typeof(MyApp.ServiceImplementation.HL7Service));
}
private void StopServices()
{
StopService(_MyAppClientServiceHost);
StopService(_MyAppIntegrationServiceHost);
StopService(_MyAppHl7ServiceHost);
}
// Start the Windows service.
protected override void OnStart(string[] args)
{
StartServices();
}
protected override void OnStop()
{
StopServices();
}
}
This is made for running in a Windows Service, how do I make so I can run this as a regular selfhost in debug mode(during development)? or do I really have to start a special project to be able to debug this servuce during runtime?
Edit:
I decided to use the existing windows service project but change the main to something like this :
public static void Main()
{
if (Debugger.IsAttached)
{
Console.WriteLine("--- MyApp Services ---");
Console.WriteLine("Starting services...");
Instance.StartServices();
Console.WriteLine("--Finished--");
Console.WriteLine("Press any key to exit");
Console.ReadKey();
Instance.StopServices();
}
else
ServiceBase.Run(new MyAppWindowsService());
}
This is what I do
Solution A
Install a Windows Service using InstallUtil from my Debug\bin folder
Stop and start service using sc start or sc stop
Once service started do Debug > Attach to Process... and attach VS to the service
Solution B
Have a Debugger.Break call on the first line of the OnStart method.
Solution C
Add a temp separate console application that does the same job as your service.
I'm trying to write simple wcf service self hosted in windows service
But ServiceHandle of windows service is always 0
I need to detect hardware change using RegisterDeviceNotification
One of it's parameters is Handle, in my case is ServiceHandle
public partial class MyService : ServiceBase, IMyService
{
private ServiceHost host;
public static void Main()
{
ServiceBase.Run(new MyService());
}
public MyService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try
{
host = new ServiceHost(typeof(MyService), new Uri(#"net.pipe://localhost/MyService"));
host.Open();
}
catch (Exception e)
{
EventLog.WriteEntry("MyService:", e.Message);
}
}
protected override void OnStop()
{
host.Close();
}
#region IMyService Members
public void Register()
{
//Here the ServiceHost is 0
}
#endregion
}
What can cause the problem?
Thanks
The ServiceHandle - no matter what value - is not required to host a WCF service as a Windows service. Simply instantiate the ServiceHost in OnStart and close it in OnStop and you should be fine.
This is why ServiceHandle is always 0 in your case:
Your Windows service class implements your WCF service contract. This is not a good thing and is also the cause for the ServiceHandle property being 0. For every call to the WCF service a new instance of the MyService class is instantiated (if you didn't change the defaults). This instance is a normal instance of the class that doesn't know it's a Windows Service, so all Windows Service related properties have their default values. Only the instance that's created by the Windows Service manager has all the appropriate properties set.
You can try for yourself: In OnStart, insert the following line and inspect the value for myServiceVar.ServiceHandle. You'll see it is 0:
MyService myServiceVar = new MyService();
What you really want to do is the following: Have a different class implement the service contract, for example like that:
public class MyWCFService : IMyService
{
public static IntPtr ServiceHandle;
public void Register()
{
// Use MyWCFService.ServiceHandle here
}
}
In the OnStart method, set the ServiceHandle variable of MyWCFService:
protected override void OnStart(string[] args)
{
try
{
MyWCFService.ServiceHandle = this.ServiceHandle;
host = new ServiceHost(typeof(MyWCFService), new Uri(#"net.pipe://localhost/MyService"));
host.Open();
}
catch (Exception e)
{
EventLog.WriteEntry("MyWCFService:", e.Message);
}
}