Does a .NET Windows service require a call to ServiceBase.Run() - c#

I'm fairly new at working with Windows services but I found a peculiar incident and I would like some clarification. I have a Windows service written in C# which I install and start using the command line (great instructions found on stackoverflow). The main method of my service looks like this:
static void Main(string[] args)
{
if (args.Length == 0)
{
ServiceBase.Run(new MyServiceName());
}
else if (args.Length == 1)
{
const string name = "MyServiceName";
Type type = typeof(MyAssembly);
switch (args[0])
{
case "-install":
ServiceUtils.InstallService(name, type);
ServiceUtils.StartService(args, name);
break;
case "-uninstall":
ServiceUtils.StopService(name);
ServiceUtils.UninstallService(name, type);
break;
default:
throw new NotImplementedException();
}
}
}
When I debug, I ALWAYS send one parameter (-install) to the application. Because of this, the first if statement (if (args.Length == 0) is NEVER executed. This is expected and my service is installed and started just fine. However, if I remove that if statement and just leave the if (args.Length == 1) statement, my service installs correctly but it does not start and I get the following error:
Cannot start MyServiceName on computer '.'
My question is: Why is the code in the first if statement needed when it is NEVER executed in my application?
Here is the supporting code for the InstallService and StartService methods (which I got from stackoverflow also):
public static void InstallService(string serviceName, Type t)
{
if (IsInstalled(serviceName)) return;
try
{
Assembly a = t.Assembly;
using (AssemblyInstaller installer = GetInstaller(a))
{
IDictionary state = new Hashtable();
try
{
installer.Install(state);
installer.Commit(state);
}
catch
{
try
{
installer.Rollback(state);
}
catch
{ }
throw;
}
}
}
catch
{
throw;
}
}
public static void StartService(string[] args, string serviceName)
{
if (!IsInstalled(serviceName)) return;
Console.WriteLine("Service is installed. Attempting to start service.");
ServiceController sc = new ServiceController();
sc.ServiceName = serviceName;
if (sc.Status == ServiceControllerStatus.Stopped)
{
Console.WriteLine("Starting {0}: ", sc.ServiceName);
try
{
sc.Start(args);
sc.WaitForStatus(ServiceControllerStatus.Running);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}

the first if statement (if (args.Length == 0) is NEVER executed
That's not correct, it is executed. By ServiceController.Start(). You cannot see this because the service controller starts your EXE again, creating another process. A service process this time, not a console process. One that you don't have a debugger attached to. If you remove that if statement then the service immediately exits after getting started. And the service controller correctly complains about that with the "Cannot start MyServiceName" exception message.

if (args.Length == 0)
{
ServiceBase.Run(new MyServiceName());
}
is run when the service is started by the Service Controller, as the Service Controller doesn't pass any arguments in to Main().
If you don't do ServiceBase.Run(new MyServiceName()), then your service will not respond to any commands from the Service Controller, and you get errors as the ones you see.

Main() is still the entry point of the application. The process is started as a separate step from starting the service(s) within.
It's actually possible to have multiple services running in the same process, and this way of handling things enables that. That is... not just the same exe program, but actually in the same running process.

Related

Windows service - only one instance to allow at a time

I tried with below code to restrict the second instance of the windows service, but below code is not working for me, can anyone help me out.
I have put time interval to run service, that is 5 mints, if first instance is started and running, after 5 mints second instance is starts even though first instance is not completed.
static class Program
{
[STAThread]
static void Main()
{
bool ok;
System.Threading.Mutex m = new System.Threading.Mutex(true, "ImageImportService", out ok);
if (!ok)
{
return;
}
GC.KeepAlive(m);
if (PriorProcess() != null)
{
return;
}
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new ImageImportService()
};
ServiceBase.Run(ServicesToRun);
}
public static Process PriorProcess()
{
Process curr = Process.GetCurrentProcess();
Process[] procs = Process.GetProcessesByName(curr.ProcessName);
foreach (Process p in procs)
{
if ((p.Id != curr.Id) && (p.MainModule.FileName == curr.MainModule.FileName))
return p;
}
return null;
}
}
Service Control Manager, a component of Windows, does not allow starting a Windows service if it is already running. Just rely on it.
The main thing you need to do is to never launch your executable directly. Instead, use the ServiceController class, when starting the service from code, and Control Panel (services.msc) when starting/stopping it manually.
In some cases, you may need to take extra steps to make sure that the service does not keep any resources behind after it has been stopped, preventing its own subsequent startup until some lower level timeout. But nothing in your question indicates that you already ran into such issues (e.g., rebinding a TCP port).

Cannot evaluate expression because a native frame is on top of the call stack

I'm creating a simple window service and when I go to debug I get the error, "Cannot evaluate expression because a native frame is on top of the call stack.". Also, when I build the service in Release and run it just hangs.
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService1() };
ServiceBase.Run(ServicesToRun);
}
Thats all that is in the Program.cs file, where it normally gets hung on the ServiceBase.Run(ServicesToRun) line.
Everything I've been able to find only relates to the expression not being evaluated because the code is optimized or having to deal with asp.net and response.redirect.
Code for the Service.
public TruckRateClearService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
tmrProcess.Enabled = true;
}
protected override void OnCustomCommand(int command)
{
base.OnCustomCommand(command);
if (command == 129)
{
OnStart(null);
}
}
protected override void OnStop()
{
tmrProcess.Enabled = false;
}
private void tmrProcess_Tick(object sender, EventArgs e)
{
tmrProcess.Enabled = false;
try
{
eventLog.WriteEntry("Clearing Truck Rates Start" + DateTime.Now.ToString());
TruckRateClearingAgent.Process();
eventLog.WriteEntry("Clearing Truck Rates Finished" + DateTime.Now.ToString());
}
catch (Exception ex)
{
eventLog.WriteEntry(ex.ToString(), EventLogEntryType.Error);
}
tmrProcess.Enabled = true;
}
internal void Run()
{
tmrProcess_Tick(tmrProcess, null);
}
The Internal Void Run() was added just recent on the suggestion in the comments by Eren Ersönmez. His idea has been very helpful for helping debug my logic until I can figure the rest out.
I was able to get the into the Native call stack and it sits on one location, 76F17094 ret. Now I have no idea what this is but maybe someone else will.
Also, when I start the service and look into attaching it to VS I'm noticing two instances of it. One is the normal .exe and another is a .vshost.exe. When I start other services I only see the .exe file in the Attach to process part of the debugger. Could this be because one is on the v4 Framework ( .vshost .exe service ) and another on the v2 ( single .exe service ) Framework?
I believe I got it working. It seems that the problem lied with the timer I was using. The original timer I was using was a System.Windows.Forms timer. I switched it to System.Timers.Timers and everything started working again. Still cant attach VS to it but I can debug it still by using the Internal Run() method. Thanks for all the help n.n
Your main problem is that you're trying to directly run a windows service exe. Windows services can only be started via Service Control Manager (SCM). In order to be able to debug in VS, I'd recommend something like this:
static void Main()
{
if (Environment.UserInteractive)
{
new MyService1().Run();
Thread.Sleep(Timeout.Infinite);
}
else
{
ServiceBase.Run(new ServiceBase[] { new MyService1() });
}
}
You'd create a MyService1.Run method which spawns a new thread that runs the service loop. Also, you'd call the same Run method from within the MyService1.Onstart.
This scheme runs it as a service when being started by SCM, but treats it like a normal exe when being debugged in VS (or being run directly as an exe outside VS).
The problem
This notification means that the thread is currently executing unmanaged code, and therefore cannot be used to evaluate the expression.
In some situations, you could wait for the call to return to managed code before evaluating the expression. Unfortunately, in this situation, that won't happen until you shut down the service.
An Alternative
You might consider overriding the ServiceBase.OnCustomCommand method and putting a breakpoint there so you can evaluate your expression.
protected override void OnCustomCommand(int command)
{
//Debugger.Break() <- or just put a breakpoint in here.
}
You can invoke the custom command as follows:
c:\>sc control YourServiceName 129
The exception you're seeing means that unmanaged code is throwing an exception, so the .NET debugger can't show you the usual useful details.
What are you doing in MyService1() ? Can you post the code inside it?
Also are you trying to debug the service by just starting it from the environment. That might not work.
I usually write something like this:
static void Main(params string[] args)
{
if (args.Length > 0 && args[0] == "/console")
{
// Run whatever your service calls here
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService1() };
ServiceBase.Run(ServicesToRun);
}
}
Then in the project properties under the Debug tab enter /console as the command line arguments. You should be able to step into the application and debug it. You can only debug a service by installing it first: http://msdn.microsoft.com/en-us/library/7a50syb3(v=vs.80).aspx

start Windows Service

i am writing a windows service that checks for a particular service and check it. if it is stop it will start it...
protected override void OnStart(string[] args)
{
Thread thread = new Thread(new ThreadStart(ServiceThreadFunction));
thread.Start();
}
public void ServiceThreadFunction()
{
try
{
ServiceController dc = new ServiceController("WebClient");
//ServiceController[] services = ServiceController.GetServices();
while (true)
{
if ((int)dc.Status == 1)
{
dc.Start();
WriteLog(dc.Status.ToString);
if ((int)dc.Status == 0)
{
//heartbeat
}
}
else
{
//service started
}
//Thread.Sleep(1000);
}
}
catch (Exception ex)
{
// log errors
}
}
i want the service to check for the another service and start... plz help me how can i do that
First of all, why are you casting the ServiceController's Status property from the convenient ServiceControllerStatus enum to an int? Best to leave it as an enum. Especially since your Heartbeat code, which compares it to 0, will never be run because ServiceControllerStatus doesn't have 0 as a possible value.
Secondly, you shouldn't use a while(true) loop. Even with the Thread.Sleep you have commented out there, it's a needless drain on resources. You can just use the WaitForStatus method to wait for the service to start:
ServiceController sc = new ServiceController("WebClient");
if (sc.Status == ServiceControllerStatus.Stopped)
{
sc.Start();
sc.WaitForStatus (ServiceControllerStatus.Running, TimeSpan.FromSeconds(30));
}
This will wait up to 30 seconds (or whatever) for the service to reach the Running state.
UPDATE: I re-read the original question, and I think what you're trying to do here shouldn't even be done with code. If I understood correctly, you want to set a dependency for your service on the WebClient service when you're installing it. Then, when the user starts your service in the Service Manager, it will automatically try to start the dependent service.

How to check if a windows service is installed in C#

I've written a Windows Service that exposes a WCF service to a GUI installed on the same machine. When I run the GUI, if I can't connect to the service, I need to know if it's because the service app hasn't been installed yet, or if it's because the service is not running. If the former, I'll want to install it (as described here); if the latter, I'll want to start it up.
Question is: how do you detect if the service is installed, and then having detected that it's installed, how do you start it up?
Use:
// add a reference to System.ServiceProcess.dll
using System.ServiceProcess;
// ...
ServiceController ctl = ServiceController.GetServices()
.FirstOrDefault(s => s.ServiceName == "myservice");
if(ctl==null)
Console.WriteLine("Not installed");
else
Console.WriteLine(ctl.Status);
You could use the following as well..
using System.ServiceProcess;
...
var serviceExists = ServiceController.GetServices().Any(s => s.ServiceName == serviceName);
Actually looping like this:
foreach (ServiceController SC in ServiceController.GetServices())
may throw Access Denied exception if the account under which your application is running doesn't have rights to view service properties. On the other hand, you can safely do this even if no service with such name exist:
ServiceController SC = new ServiceController("AnyServiceName");
But accessing its properties if service doesn't exist will result in InvalidOperationException. So here's a safe way to check if a service is installed:
ServiceController SC = new ServiceController("MyServiceName");
bool ServiceIsInstalled = false;
try
{
// actually we need to try access ANY of service properties
// at least once to trigger an exception
// not neccessarily its name
string ServiceName = SC.DisplayName;
ServiceIsInstalled = true;
}
catch (InvalidOperationException) { }
finally
{
SC.Close();
}
For non-linq, you can just iterate thru the array like this:
using System.ServiceProcess;
bool serviceExists = false
foreach (ServiceController sc in ServiceController.GetServices())
{
if (sc.ServiceName == "myServiceName")
{
//service is found
serviceExists = true;
break;
}
}
I think this is the best answer for this question. There is no need to add extra processing to verify if the service exists, since it will throw an exception if it doesn't. You just need to catch it. You also do not need to close() the connecting if you wrap the entire method in using().
using (ServiceController sc = new ServiceController(ServiceName))
{
try
{
if (sc.Status != ServiceControllerStatus.Running)
{
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 10));
//service is now Started
}
else
//Service was already started
}
catch (System.ServiceProcess.TimeoutException)
{
//Service was stopped but could not restart (10 second timeout)
}
catch (InvalidOperationException)
{
//This Service does not exist
}
}
private bool ServiceExists(string serviceName)
{
ServiceController[] services = ServiceController.GetServices();
var service = services.FirstOrDefault(s => string.Equals(s.ServiceName, serviceName, StringComparison.OrdinalIgnoreCase));
return service != null;
}

Am I Running as a Service

I am currently writing a little bootstrap code for a service that can be run in the console. It essentially boils down to calling the OnStart() method instead of using the ServiceBase to start and stop the service (because it doesn't run the application if it isn't installed as a service and makes debugging a nightmare).
Right now I am using Debugger.IsAttached to determine if I should use ServiceBase.Run or [service].OnStart, but I know that isn't the best idea because some times end users want to run the service in a console (to see the output etc. realtime).
Any ideas on how I could determine if the Windows service controller started 'me', or if the user started 'me' in the console? Apparantly Environment.IsUserInteractive is not the answer. I thought about using commandline args, but that seems 'dirty'.
I could always see about a try-catch statement around ServiceBase.Run, but that seems dirty. Edit: Try catch doesn't work.
I have a solution: putting it up here for all the other interested stackers:
public void Run()
{
if (Debugger.IsAttached || Environment.GetCommandLineArgs().Contains<string>("-console"))
{
RunAllServices();
}
else
{
try
{
string temp = Console.Title;
ServiceBase.Run((ServiceBase[])ComponentsToRun);
}
catch
{
RunAllServices();
}
}
} // void Run
private void RunAllServices()
{
foreach (ConsoleService component in ComponentsToRun)
{
component.Start();
}
WaitForCTRLC();
foreach (ConsoleService component in ComponentsToRun)
{
component.Stop();
}
}
EDIT: There was another question on StackOverflow where the guy had problems with the Environment.CurrentDirectory being "C:\Windows\System32" looks like that may be the answer. I will test today.
Another workaround.. so can run as WinForm or as windows service
var backend = new Backend();
if (Environment.UserInteractive)
{
backend.OnStart();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Fronend(backend));
backend.OnStop();
}
else
{
var ServicesToRun = new ServiceBase[] {backend};
ServiceBase.Run(ServicesToRun);
}
I usually flag my Windows service as a console application which takes a command line parameter of "-console" to run using a console, otherwise it runs as a service. To debug you just set the command line parameters in the project options to "-console" and you're off!
This makes debugging nice and easy and means that the app functions as a service by default, which is what you'll want.
What works for me:
The class doing the actual service work is running in a separate thread.
This thread is started from within the OnStart() method, and stopped from OnStop().
The decision between service and console mode depends on Environment.UserInteractive
Sample code:
class MyService : ServiceBase
{
private static void Main()
{
if (Environment.UserInteractive)
{
startWorkerThread();
Console.WriteLine ("====== Press ENTER to stop threads ======");
Console.ReadLine();
stopWorkerThread() ;
Console.WriteLine ("====== Press ENTER to quit ======");
Console.ReadLine();
}
else
{
Run (this) ;
}
}
protected override void OnStart(string[] args)
{
startWorkerThread();
}
protected override void OnStop()
{
stopWorkerThread() ;
}
}
Like Ash, I write all actual processing code in a separate class library assembly, which was then referenced by the windows service executable, as well as a console app.
However, there are occasions when it is useful to know if the class library is running in the context of the service executable or the console app. The way I do this is to reflect on the base class of the hosting app. (Sorry for the VB, but I imagine that the following could be c#-ified fairly easily):
Public Class ExecutionContext
''' <summary>
''' Gets a value indicating whether the application is a windows service.
''' </summary>
''' <value>
''' <c>true</c> if this instance is service; otherwise, <c>false</c>.
''' </value>
Public Shared ReadOnly Property IsService() As Boolean
Get
' Determining whether or not the host application is a service is
' an expensive operation (it uses reflection), so we cache the
' result of the first call to this method so that we don't have to
' recalculate it every call.
' If we have not already determined whether or not the application
' is running as a service...
If IsNothing(_isService) Then
' Get details of the host assembly.
Dim entryAssembly As Reflection.Assembly = Reflection.Assembly.GetEntryAssembly
' Get the method that was called to enter the host assembly.
Dim entryPoint As System.Reflection.MethodInfo = entryAssembly.EntryPoint
' If the base type of the host assembly inherits from the
' "ServiceBase" class, it must be a windows service. We store
' the result ready for the next caller of this method.
_isService = (entryPoint.ReflectedType.BaseType.FullName = "System.ServiceProcess.ServiceBase")
End If
' Return the cached result.
Return CBool(_isService)
End Get
End Property
Private Shared _isService As Nullable(Of Boolean) = Nothing
#End Region
End Class
Jonathan, not exactly an answer to your question, but I've just finished writing a windows service and also noted the difficulty with debugging and testing.
Solved it by simply writing all actual processing code in a separate class library assembly, which was then referenced by the windows service executable, as well as a console app and a test harness.
Apart from basic timer logic, all more complex processing happened in the common assembly and could be tested/run on demand incredibly easily.
I have modified the ProjectInstaller to append the command-line argument parameter /service, when it is being installed as service:
static class Program
{
static void Main(string[] args)
{
if (Array.Exists(args, delegate(string arg) { return arg == "/install"; }))
{
System.Configuration.Install.TransactedInstaller ti = null;
ti = new System.Configuration.Install.TransactedInstaller();
ti.Installers.Add(new ProjectInstaller());
ti.Context = new System.Configuration.Install.InstallContext("", null);
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
ti.Context.Parameters["assemblypath"] = path;
ti.Install(new System.Collections.Hashtable());
return;
}
if (Array.Exists(args, delegate(string arg) { return arg == "/uninstall"; }))
{
System.Configuration.Install.TransactedInstaller ti = null;
ti = new System.Configuration.Install.TransactedInstaller();
ti.Installers.Add(new ProjectInstaller());
ti.Context = new System.Configuration.Install.InstallContext("", null);
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
ti.Context.Parameters["assemblypath"] = path;
ti.Uninstall(null);
return;
}
if (Array.Exists(args, delegate(string arg) { return arg == "/service"; }))
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run(ServicesToRun);
}
else
{
Console.ReadKey();
}
}
}
The ProjectInstaller.cs is then modified to override a OnBeforeInstall() and OnBeforeUninstall()
[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
protected virtual string AppendPathParameter(string path, string parameter)
{
if (path.Length > 0 && path[0] != '"')
{
path = "\"" + path + "\"";
}
path += " " + parameter;
return path;
}
protected override void OnBeforeInstall(System.Collections.IDictionary savedState)
{
Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service");
base.OnBeforeInstall(savedState);
}
protected override void OnBeforeUninstall(System.Collections.IDictionary savedState)
{
Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service");
base.OnBeforeUninstall(savedState);
}
}
This thread is really old, but I thought I would throw my solution out there. Quite simply, to handle this type of situation, I built a "service harness" that is used in both the console and Windows service cases. As above, most of the logic is contained in a separate library, but this is more for testing and "linkability".
The attached code by no means represents the "best possible" way to solve this, just my own approach. Here, the service harness is called by the console app when in "console mode" and by the same application's "start service" logic when it is running as a service. By doing it this way, you can now call
ServiceHost.Instance.RunningAsAService (Boolean)
from anywhere in your code to check if the application is running as a service or simply as a console.
Here is the code:
public class ServiceHost
{
private static Logger log = LogManager.GetLogger(typeof(ServiceHost).Name);
private static ServiceHost mInstance = null;
private static object mSyncRoot = new object();
#region Singleton and Static Properties
public static ServiceHost Instance
{
get
{
if (mInstance == null)
{
lock (mSyncRoot)
{
if (mInstance == null)
{
mInstance = new ServiceHost();
}
}
}
return (mInstance);
}
}
public static Logger Log
{
get { return log; }
}
public static void Close()
{
lock (mSyncRoot)
{
if (mInstance.mEngine != null)
mInstance.mEngine.Dispose();
}
}
#endregion
private ReconciliationEngine mEngine;
private ServiceBase windowsServiceHost;
private UnhandledExceptionEventHandler threadExceptionHanlder = new UnhandledExceptionEventHandler(ThreadExceptionHandler);
public bool HostHealthy { get; private set; }
public bool RunningAsService {get; private set;}
private ServiceHost()
{
HostHealthy = false;
RunningAsService = false;
AppDomain.CurrentDomain.UnhandledException += threadExceptionHandler;
try
{
mEngine = new ReconciliationEngine();
HostHealthy = true;
}
catch (Exception ex)
{
log.FatalException("Could not initialize components.", ex);
}
}
public void StartService()
{
if (!HostHealthy)
throw new ApplicationException("Did not initialize components.");
try
{
mEngine.Start();
}
catch (Exception ex)
{
log.FatalException("Could not start service components.", ex);
HostHealthy = false;
}
}
public void StartService(ServiceBase serviceHost)
{
if (!HostHealthy)
throw new ApplicationException("Did not initialize components.");
if (serviceHost == null)
throw new ArgumentNullException("serviceHost");
windowsServiceHost = serviceHost;
RunningAsService = true;
try
{
mEngine.Start();
}
catch (Exception ex)
{
log.FatalException("Could not start service components.", ex);
HostHealthy = false;
}
}
public void RestartService()
{
if (!HostHealthy)
throw new ApplicationException("Did not initialize components.");
try
{
log.Info("Stopping service components...");
mEngine.Stop();
mEngine.Dispose();
log.Info("Starting service components...");
mEngine = new ReconciliationEngine();
mEngine.Start();
}
catch (Exception ex)
{
log.FatalException("Could not restart components.", ex);
HostHealthy = false;
}
}
public void StopService()
{
try
{
if (mEngine != null)
mEngine.Stop();
}
catch (Exception ex)
{
log.FatalException("Error stopping components.", ex);
HostHealthy = false;
}
finally
{
if (windowsServiceHost != null)
windowsServiceHost.Stop();
if (RunningAsService)
{
AppDomain.CurrentDomain.UnhandledException -= threadExceptionHanlder;
}
}
}
private void HandleExceptionBasedOnExecution(object ex)
{
if (RunningAsService)
{
windowsServiceHost.Stop();
}
else
{
throw (Exception)ex;
}
}
protected static void ThreadExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
log.FatalException("Unexpected error occurred. System is shutting down.", (Exception)e.ExceptionObject);
ServiceHost.Instance.HandleExceptionBasedOnExecution((Exception)e.ExceptionObject);
}
}
All you need to do here is replace that ominous looking ReconcilationEngine reference with whatever method is boostrapping your logic. Then in your application, use the ServiceHost.Instance.Start() and ServiceHost.Instance.Stop() methods whether you are running in console mode or as a service.
Maybe checking if the process parent is C:\Windows\system32\services.exe.
The only way I've found to achieve this, is to check if a console is attached to the process in the first place, by accessing any Console object property (e.g. Title) inside a try/catch block.
If the service is started by the SCM, there is no console, and accessing the property will throw a System.IO.IOError.
However, since this feels a bit too much like relying on an implementation-specific detail (what if the SCM on some platforms or someday decides to provide a console to the processes it starts?), I always use a command line switch (-console) in production apps...
Here is a translation of chksr's answer to .NET, and avoiding the bug that fails to recognize interactive services:
using System.Security.Principal;
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);
// maybe check LocalServiceSid, and NetworkServiceSid also
bool isServiceRunningAsUser = wp.IsInRole(serviceSid);
bool isSystem = wp.IsInRole(localSystemSid);
bool isInteractive = wp.IsInRole(interactiveSid);
bool isAnyService = isServiceRunningAsUser || isSystem || !isInteractive;
This is a bit of a self-plug, but I've got a little app that will load up your service types in your app via reflection and execute them that way. I include the source code, so you could change it slightly to display standard output.
No code changes needed to use this solution. I have a Debugger.IsAttached type of solution as well that is generic enough to be used with any service. Link is in this article:
.NET Windows Service Runner
Well there's some very old code (about 20 years or so, not from me but found in the wild, wild web, and in C not C#) that should give you an idea how to do the job:
enum enEnvironmentType
{
ENVTYPE_UNKNOWN,
ENVTYPE_STANDARD,
ENVTYPE_SERVICE_WITH_INTERACTION,
ENVTYPE_SERVICE_WITHOUT_INTERACTION,
ENVTYPE_IIS_ASP,
};
enEnvironmentType GetEnvironmentType(void)
{
HANDLE hProcessToken = NULL;
DWORD groupLength = 300;
PTOKEN_GROUPS groupInfo = NULL;
SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
PSID pInteractiveSid = NULL;
PSID pServiceSid = NULL;
DWORD dwRet = NO_ERROR;
DWORD ndx;
BOOL m_isInteractive = FALSE;
BOOL m_isService = FALSE;
// open the token
if (!::OpenProcessToken(::GetCurrentProcess(),TOKEN_QUERY,&hProcessToken))
{
dwRet = ::GetLastError();
goto closedown;
}
// allocate a buffer of default size
groupInfo = (PTOKEN_GROUPS)::LocalAlloc(0, groupLength);
if (groupInfo == NULL)
{
dwRet = ::GetLastError();
goto closedown;
}
// try to get the info
if (!::GetTokenInformation(hProcessToken, TokenGroups,
groupInfo, groupLength, &groupLength))
{
// if buffer was too small, allocate to proper size, otherwise error
if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
dwRet = ::GetLastError();
goto closedown;
}
::LocalFree(groupInfo);
groupInfo = (PTOKEN_GROUPS)::LocalAlloc(0, groupLength);
if (groupInfo == NULL)
{
dwRet = ::GetLastError();
goto closedown;
}
if (!GetTokenInformation(hProcessToken, TokenGroups,
groupInfo, groupLength, &groupLength))
{
dwRet = ::GetLastError();
goto closedown;
}
}
//
// We now know the groups associated with this token. We want
// to look to see if the interactive group is active in the
// token, and if so, we know that this is an interactive process.
//
// We also look for the "service" SID, and if it's present,
// we know we're a service.
//
// The service SID will be present iff the service is running in a
// user account (and was invoked by the service controller).
//
// create comparison sids
if (!AllocateAndInitializeSid(&siaNt,
1,
SECURITY_INTERACTIVE_RID,
0, 0, 0, 0, 0, 0, 0,
&pInteractiveSid))
{
dwRet = ::GetLastError();
goto closedown;
}
if (!AllocateAndInitializeSid(&siaNt,
1,
SECURITY_SERVICE_RID,
0, 0, 0, 0, 0, 0, 0,
&pServiceSid))
{
dwRet = ::GetLastError();
goto closedown;
}
// try to match sids
for (ndx = 0; ndx < groupInfo->GroupCount ; ndx += 1)
{
SID_AND_ATTRIBUTES sanda = groupInfo->Groups[ndx];
PSID pSid = sanda.Sid;
//
// Check to see if the group we're looking at is one of
// the two groups we're interested in.
//
if (::EqualSid(pSid, pInteractiveSid))
{
//
// This process has the Interactive SID in its
// token. This means that the process is running as
// a console process
//
m_isInteractive = TRUE;
m_isService = FALSE;
break;
}
else if (::EqualSid(pSid, pServiceSid))
{
//
// 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 ).
//
m_isService = TRUE;
m_isInteractive = FALSE;
break;
}
}
if ( !( m_isService || m_isInteractive ) )
{
//
// Neither Interactive or Service was present in the current
// users token, This implies that the process is running as
// a service, most likely running as LocalSystem.
//
m_isService = TRUE;
}
closedown:
if ( pServiceSid )
::FreeSid( pServiceSid );
if ( pInteractiveSid )
::FreeSid( pInteractiveSid );
if ( groupInfo )
::LocalFree( groupInfo );
if ( hProcessToken )
::CloseHandle( hProcessToken );
if (dwRet == NO_ERROR)
{
if (m_isService)
return(m_isInteractive ? ENVTYPE_SERVICE_WITH_INTERACTION : ENVTYPE_SERVICE_WITHOUT_INTERACTION);
return(ENVTYPE_STANDARD);
}
else
return(ENVTYPE_UNKNOWN);
}
Seems I am bit late to the party, but interesting difference when run as a service is that at start current folder points to system directory (C:\windows\system32 by default). Its hardly unlikely user app will start from the system folder in any real life situation.
So, I use following trick (c#):
protected static bool IsRunAsService()
{
string CurDir = Directory.GetCurrentDirectory();
if (CurDir.Equals(Environment.SystemDirectory, StringComparison.CurrentCultureIgnoreCase))
{
return true;
}
return (false);
}
For future extension, additional check make be done for System.Environment.UserInteractive == false (but I do not know how it correlates with 'Allow service to interact with desktop' service settings).
You may also check window session by System.Diagnostics.Process.GetCurrentProcess().SessionId == 0 (I do not know how it correlates with 'Allow service to interact with desktop' service settings as well).
If you write portable code (say, with .NetCore) you may also check Environment.OSVersion.Platform to ensure that you are on windows first.

Categories

Resources