Base Class for Windows Service for databses - c#

I'm trying to create a base class for a windows service so I can change as little as possible when deploying to different databases. I have this but there is an unhandled exception:
"An unhandled exception of type 'System.StackOverflowException' occurred in >Microsoft.VisualStudio.HostingProcess.Utilities.dll",
I'm new to services so I could totally be off on this, so far this is what I have:
public partial class Service1 : ServiceBase
{
namespace SecureVoiceBase
{
public Service1()
{
try
{
InitializeComponent();
}
catch (Exception e)
{
EventLog.WriteEntry(e.Message);
}
}
protected override void OnStart(string[] args)
{
//code here
}
//OnStop, Timers as well...
}
}
public class Version_10 : ServiceBase// Derived class, This is where I will call
{
//certain methods depending on which database I will use
Version_10 set = new Version_10();
public void Start(string[] args)
{
set.OnStart(args);
}
}
This is my Program.cs:
namespace testservice
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(params string[] args)
{
var service = new Version_10();
if (!Environment.UserInteractive)
{
var servicesToRun = new ServiceBase[] { service };
ServiceBase.Run(servicesToRun);
return;
}
Console.WriteLine("Running as a Console Application");
Console.WriteLine(" 1. Run Service");
Console.WriteLine(" 2. Other Option");
Console.WriteLine(" 3. Exit");
Console.Write("Enter Option: ");
var input = Console.ReadLine();
switch (input)
{
case "1":
service.Start(args);
Console.WriteLine("Running Service - Press Enter To Exit");
Console.ReadLine();
break;
case "2":
// TODO!
break;
}
Console.WriteLine("Closing");
}
// ServiceBase[] ServicesToRun;
// ServicesToRun = new ServiceBase[]
// {
// new Version_10()
// };
// ServiceBase.Run(ServicesToRun);
}
}
As usually I have other methods that I call but I figured it would just be a waste of space. Am I totally off on base classes?

You problem lies here:
public class Version_10 : ServiceBase
{
Version_10 set = new Version_10(); // <-- Recursive call on object construct
It has nothing to do with services or anything. Your code has a recursive call with trigger the StackOverflow exception.
UPDATE:
To solve your problem, change your Version_10 class to:
public class Version_10 : ServiceBase
{
public void Start(string[] args)
{
this.OnStart(args);
}
}

This part is recursive that's why you are getting a System.StackOverflowException.
public class Version_10 : ServiceBase
{
**Version_10 set = new Version_10();**
public void Start(string[] args)
{
set.OnStart(args);
}
}
Maybe you should check some articles:
A basic Windows service in C#

You are getting a Stack Overflow exception because of this:
public class Version_10 : ServiceBase
{
Version_10 set = new Version_10();
}
When you create an instance of Version_10 it creates an instance of Version_10 which creates an instance of Version_10 which creates an instance of Version_10 which creates an instance of Version_10 which creates an instance of Version_10 etc....

Related

How to subscribe to a event from a dll file and get a list generated by the dll?

So I have a dll file that generates people and data about them. Every time something changes the dll raises an event. I need to subscribe to the event and then process the data.
I have the following information about the dll
namespace PeopleGenerator
{
public class RawPeopleDataEventArgs : EventArgs
{
public RawPeopleDataEventArgs(List<string> peopleData)
{
PeopleData = peopleData;
}
public List<string> PeopleData { get; }
}
public interface IPeopleGenerator
{
event EventHandler<RawPeopleDataEventArgs> PeopleDataReady;
}
}
I have also been given info about a factory I can use to get an IPeopleGenerator object
namespace PeopleGenerator
{
public class PeopleGeneratorFactory
{
public static IPeopleGenerator CreatePeopleDataReceiver()
}
}
Now I have tried to make a subscription class
namespace Test
{
class EventSubscriber
{
public EventSubscriber(object o, RawPeopleDataEventArgs args)
{
List<string> listofpeople = args.PeopleData;
printList(listofpeople);
}
void printList(List<string> print)
{
print.ForEach(Console.WriteLine);
// More data processing to happen here
}
}
}
My problem is I cant't figure out how to start generating the data from the dll. with the factory class. My thought is something like this
static void Main(string[] args)
{
//generate a new object
EventSubscriber sub = new EventSubscriber(????);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
You subscribe methods, not classes, modify your subscriber as following:
namespace Test
{
class EventSubscriber
{
public HandlePeople(object o, RawPeopleDataEventArgs args)
{
List<string> listofpeople = args.PeopleData;
printList(listofpeople);
}
void printList(List<string> print)
{
print.ForEach(Console.WriteLine);
// More data processing to happen here
}
}
}
And use instance of subscriber (and its method) to handle events:
static void Main(string[] args)
{
var recvr = PeopleGenerator.PeopleGeneratorFactory.CreatePeopleDataReceiver();
var subscriber = new EventSubscriber();
recvr.PeopleDataReady += new subscriber.Handle;
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}

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 WCF service in a new AppDomain to enable shadow copy (Windows Service hosted)

I have a WCF service library (MyWCFService), which uses MEF to load plugins and hosted by Windows services (All .NET 4.0). I am now trying to run it in a new AppDomain and to enable ShadowCopyFiles in a hope that I can update plugins in the runtime. Here is the code in the Windows service project.
Program.cs
static class Program
{
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
ServiceBase.Run(ServicesToRun);
}
}
MyService.cs
public partial class MyService: ServiceBase
{
internal static ServiceHost MyServiceHost = null;
public MyService()
{
// this works but is deprecated..
AppDomain.CurrentDomain.SetShadowCopyFiles();
//this is not working.. DLLs still get locked. Require for a new AppDomain
//AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles = "true";
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if(MyServiceHost !=null)
{
MyServiceHost.Close();
}
try
{
MyServiceHost= new ServiceHost(typeof(MyWCFService));
MyServiceHost.Open();
}
catch(Exception)
{
}
}
protected override void OnStop()
{
if (MyServiceHost!= null)
{
MyServiceHost.Close();
MyServiceHost= null;
}
}
}
Is there any way of doing it? I have done a lot of search, but still don't know how to make it work with my current settings (or I just can't understand...)
I have tried to create a new AppDomain inside Main() and used
domain.DoCallBack(new CrossAppDomainDelegate(() => { ServiceBase.Run(ServicesToRun); })) to start the service but I can't start it and keep getting "Error 1053: The service did not respond to the start or control request in a timely fashion".
And then I tried to just enable Shadow copy for the current appdomain by setting AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles = "true"; in MyWCFService.cs just before InitializeComponent(); I can start the service but the dlls are still locked. however, if I use AppDomain.CurrentDomain.SetShadowCopyFiles(); (a deprecated method) to enable the shadow copy, everything works. I'm more confused.
OK, I ended up with creating a shell/proxy class inherited from MarshalByRefObject and start the service from there, and here is the code:
ServiceShell.cs
public class ServiceShell:MarshalByRefObject
{
internal static ServiceHost MyServiceHost = null;
public void Run()
{
if (MyServiceHost != null)
{
MyServiceHost.Close();
}
try
{
MyServiceHost = new ServiceHost(typeof(MyWCFService));
MyServiceHost.Open();
}
catch (Exception)
{
}
}
public void Stop()
{
if (MyServiceHost!= null)
{
MyServiceHost.Close();
MyServiceHost = null;
}
}
}
MyService.cs
public partial class MyService: ServiceBase
{
AppDomain domain;
ServiceShell runner;
public MyService()
{
var setup = new AppDomainSetup
{
ShadowCopyFiles = "true"
};
domain = AppDomain.CreateDomain("MyServiceHostDomain", AppDomain.CurrentDomain.Evidence, setup);
runner = (ServiceShell)domain.CreateInstanceAndUnwrap
(typeof(ServiceShell).Assembly.FullName, typeof(ServiceShell).FullName);
InitializeComponent();
}
protected override void OnStart(string[] args)
{
runner.Run();
}
protected override void OnStop()
{
runner.Stop();
AppDomain.Unload(domain);
}
}

Access static object from other instance

I have a Program class which has:
private static ClientBase objClientBase = new ClientBase(new List<RecordType> { RecordType.none }, ModuleType.Monitor);
static void Main(string[] args)
{
objClientBase.Connect(); //IRRELEVANT
objQueueMon = new Main(); //<-INSIDE THIS IS WHERE I WANT TO ACCESS objClientBase
objClientBase.MainModuleThreadManualResetEvent.WaitOne(); //IRRELEVANT
}
This Progam creates a Main class instance as you see:
objQueueMon = new Main();
Notice that they are separated in different files, but the Main class instance is created inside the Program class.
Inside my Program class I want to access that objClientBase.
Do I have to create a constructor method and pass it or make a public access to it?
So what I want to achieve is, inside the Main class, do a objClientBase.FUNCTION
You can do exactly what you just said:
public class Main {
private ClientBase _caller;
public Main (ClientBase caller) {
_caller = caller;
}
}
Or, you can set it later
public class Main {
private ClientBase _caller;
public Main () {
}
// only your assembly sets it
internal SetClientBase(ClientBase cb) {
_caller = cb;
}
// but anyone gets it
// Now you can let some client execute "Function"
public ClientBase Caller {
{return _caller;}
}
}
Just an example
Change the constructor of your Main class to accept a ClientBase object, like this:
public class Main
{
private ClientBase _clientBase;
public Main(ClientBase clientBase)
{
_clientBase = clientBase;
}
public void SomeMethod()
{
// Use ClientBase.FUNCTION here
_clientBase.FUNCTION();
}
}

c# register commandline argument don't start new instance

Application c:\pinkPanther.exe is running and it is application i wrote in c#.
Some other application starts c:\pinkPanther.exe purpleAligator greenGazelle OrangeOrangutan and i would like not to start new instance of c:\pinkPanther.exe with these arguments, but to currently running c:\pinkPanther.exe register it and react to it somehow.
How to do it?
EDIT!!!: i'm very sorry about pinkPanther.exe and ruzovyJeliman.exe that caused the confusion - i translated question from my native language and missed it :(
This is assuming your application is a WinForms app, as that will make it easier to keep it open. This is a very simple example, but it will show you the basics:
Add a reference to Microsoft.VisualBasic.
Create an Application class inheriting from WindowsFormsApplicationBase. This base class contains built-in mechanisms for creating a single-instance application and responding to repeated calls on the commandline with new arguments:
using Microsoft.VisualBasic.ApplicationServices;
//omitted namespace
public class MyApp : WindowsFormsApplicationBase {
private static MyApp _myapp;
public static void Run( Form startupform ) {
_myapp = new MyApp( startupform );
_myapp.StartupNextInstance += new Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventHandler( _myapp_StartupNextInstance );
_myapp.Run( Environment.GetCommandLineArgs() );
}
static void _myapp_StartupNextInstance( object sender, Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs e ) {
//e.CommandLine contains the new commandline arguments
// this is where you do what you want with the new commandline arguments
// if you want it the window to come to the front:
e.BringToForeground = true;
}
private MyApp( Form mainform ) {
this.IsSingleInstance = true;
this.MainForm = mainform;
}
}
All you have to change in Main() is call Run() on your new class rather than Application.Run():
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false );
MyApp.Run( new MyMainForm() );
}
}
WindowsFormsApplicationBase has some other capabilities you can explore, as well.
To communicate with the other instance of the application, you need some sort of inter-process communication. Apparently, WCF is the recommended form of IPC in .Net. You can do that with code like this (using WPF, but WinForms would be similar):
[ServiceContract]
public interface ISingletonProgram
{
[OperationContract]
void CallWithArguments(string[] args);
}
class SingletonProgram : ISingletonProgram
{
public void CallWithArguments(string[] args)
{
// handle the arguments somehow
}
}
public partial class App : Application
{
private readonly Mutex m_mutex;
private ServiceHost m_serviceHost;
private static string EndpointUri =
"net.pipe://localhost/RuzovyJeliman/singletonProgram";
public App()
{
// find out whether other instance exists
bool createdNew;
m_mutex = new Mutex(true, "RůžovýJeliman", out createdNew);
if (!createdNew)
{
// other instance exists, call it and exit
CallService();
Shutdown();
return;
}
// other instance does not exist
// start the service to accept calls and show UI
StartService();
// show the main window here
// you can also process this instance's command line arguments
}
private static void CallService()
{
var factory = new ChannelFactory<ISingletonProgram>(
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), EndpointUri);
var singletonProgram = factory.CreateChannel();
singletonProgram.CallWithArguments(Environment.GetCommandLineArgs());
}
private void StartService()
{
m_serviceHost = new ServiceHost(typeof(SingletonProgram));
m_serviceHost.AddServiceEndpoint(
typeof(ISingletonProgram),
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None),
EndpointUri);
m_serviceHost.Open();
}
protected override void OnExit(ExitEventArgs e)
{
if (m_serviceHost != null)
m_serviceHost.Close();
m_mutex.Dispose();
base.OnExit(e);
}
}

Categories

Resources