I want to debug a service written in C# and the old fashioned way is just too long. I have to stop the service, start my application that uses the service in debug mode (Vis
The approach I always take is to isolate out all of your application's logic into class libraries. This makes your service project really just a shell that hosts your class libraries as a service.
By doing this, you can easily unit test and debug your code, without having to deal with the headache of debugging a service by attaching to a process. I'd recommend unit testing of course, but if you're not doing that then adding a console application that calls the same entry point as your service is the way to go.
You might want to check out TopShelf as well in your adventures.
http://codebetter.com/blogs/dru.sellers/archive/2009/01/11/topshelf.aspx
http://code.google.com/p/topshelf/
I use this to check if my process is running as a service or not.
public class ServiceDiagnostics
{
readonly bool _isUserService;
readonly bool _isLocalSystem;
readonly bool _isInteractive;
public ServiceDiagnostics()
{
var wi = WindowsIdentity.GetCurrent();
var wp = new WindowsPrincipal(wi);
var serviceSid = new SecurityIdentifier(WellKnownSidType.ServiceSid, null);
var localSystemSid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
var interactiveSid = new SecurityIdentifier(WellKnownSidType.InteractiveSid, null);
this._isUserService = wp.IsInRole(serviceSid);
// Neither Interactive or Service was present in the current user's token, This implies
// that the process is running as a service, most likely running as LocalSystem.
this._isLocalSystem = wp.IsInRole(localSystemSid);
// This process has the Interactive SID in its token. This means that the process is
// running as a console process.
this._isInteractive = wp.IsInRole(interactiveSid);
}
public bool IsService
{
get { return this.IsUserService || this.IsLocalSystem || !this.IsInteractive; }
}
public bool IsConsole
{
get { return !this.IsService; }
}
/// <summary>
/// This process has the Service SID in its token. This means that the process is running
/// as a service running in a user account (not local system).
/// </summary>
public bool IsUserService
{
get { return this._isUserService; }
}
/// <summary>
/// Neither Interactive or Service was present in the current user's token, This implies
/// that the process is running as a service, most likely running as LocalSystem.
/// </summary>
public bool IsLocalSystem
{
get { return this._isLocalSystem; }
}
/// <summary>
/// This process has the Interactive SID in its token. This means that the process is
/// running as a console process.
/// </summary>
public bool IsInteractive
{
get { return this._isInteractive; }
}
}
You can do something like this in the main entry point:
static void Main()
{
#if DEBUG
Service1 s = new Service1();
s.Init(); // Init() is pretty much any code you would have in OnStart().
#else
ServiceBase[] ServicesToRun;
ServicesToRun=new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
#endif
}
and in your OnStart Event handler:
protected override void OnStart(string[] args)
{
Init();
}
To avoid using global defines I generally test at run-time whether I'm a service or regular application through the Environment.UserInteractive property.
[MTAThread()]
private static void Main()
{
if (!Environment.UserInteractive)
{
ServiceBase[] aServicesToRun;
// More than one NT Service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = New System.ServiceProcess.ServiceBase () {New ServiceMain, New MySecondUserService}
//
aServicesToRun = new ServiceBase[] {new ServiceMain()};
Run(aServicesToRun);
}
else
{
var oService = new ServiceMain();
oService.OnStart(null);
}
}
You could call the service methods via reflection as seen below.
Using Environment.UserInteractive
enables us to know if we are running as a console app or as a service.
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
if (!Environment.UserInteractive)
{
// This is what normally happens when the service is run.
ServiceBase.Run(ServicesToRun);
}
else
{
// Here we call the services OnStart via reflection.
Type type = typeof(ServiceBase);
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo method = type.GetMethod("OnStart", flags);
foreach (ServiceBase service in ServicesToRun)
{
Console.WriteLine("Running " + service.ServiceName + ".OnStart()");
// Your Main method might not have (string[] args) but you could add that to be able to send arguments in.
method.Invoke(service, new object[] { args });
}
Console.WriteLine("Finished running all OnStart Methods.");
foreach (ServiceBase service in ServicesToRun)
{
Console.WriteLine("Running " + service.ServiceName + ".OnStop()");
service.Stop();
}
}