How to Check if a Process is a Windows Service in C#

To make the long story long, sometimes it is handy to be able to tell if a given process is a service. Actually, I started from a simpler place – I simply wanted to tell if the current process was running as a service… And what do you know, turned out not to be so simple after all.

As usual, I did a fair amount of Googling before settling on the DIY course of action. Unfortunately, Google came up short on a succinct way to accomplish this seemingly trivial feat. Approaches suggested by my fellow code warriors included:

  • Get a list of all services via ServiceController.GetServices() and then compare them to the running application. Couple of issues with this one: getting the process path from Service controller is not a get_property type of operation and if there are processes with the same name/path where one is running as a service and another as a regular app this option fails.
  • Check the Security ID associated with the process. If it is service-like (e.g. SECURITY_SERVICE_RID or SECURITY_LOCAL_SYSTEM_RID) assume it is a service, otherwise it is not. Well… That does not work very well. I ran some quick tests and my console app ran as SECURITY_NT_NON_UNIQUE. I then ran the same app as a service (yep, that’s right..) and it showed up as SECURITY_LOCAL_SYSTEM_RID. Success – I thought. Well, then I changed the service “Logon As” properies to run as my user and the SID went right back to SECURITY_NT_NON_UNIQUE, so Failure! As a side note, in the pursuit of this solution I used values returned by System.Security.Principal.WindowsIdentity.GetCurrent().User. I also found this snippet which brought back some memories.. Oh coding C++ in 1995.. Things were so much simple back then…
  • Another suggestion was to see if ServiceBase appears anywhere in the call stack… I didn’t want to touch that one.

So, without further ado, the solution came from Mephistopheles disguised as WMI. I’ll be honest, I don’t like WMI. Althought, it has gone a long way since I first started messing with it in Windows 2000, it caused me so much sufferfing back then with unmitigatable memory leaks (try polling network cable status through WMI from C++ via COM… see how long it takes your app to run out of memory) and goofy object model. But alas, it seems to be the easiest solution here and also it *has* gotten easier to use with C# and seemingly better memory management. Perfomance is still iffy in terms of speed, but this particular problem should not require you to check more than once. So here it goes:

 

/// <summary>
/// returns true if the current process is a service, false otherwise
/// </summary>
/// <param name="pID">process Id to check</param>
/// <returns>true if the current process is a service, false otherwise</returns>
public static bool IsService(uint pID)
{
    using (ManagementObjectSearcher Searcher = new ManagementObjectSearcher(
    "SELECT * FROM Win32_Service WHERE ProcessId =" + "\"" + pID + "\""))
    {
        foreach (ManagementObject service in Searcher.Get())
            return true;
    }
    return false;
}

 

Usage: IsService((uint)Process.GetCurrentProcess().Id);

Obvisouly, this would work for other PIDs, not just those of the current process. As a bonus, here is a function to get the executable path for a Windows Service:

/// <summary>
/// Attempts to detemine the path associated with the service identified by serviceName
/// </summary>
/// <param name="serviceName">name of the service to execute</param>
/// <returns>string representing service path, or null, if not found</returns>
public static string GetServiceImagePath(string serviceName)
{
    string servicePath = null;
    using(ManagementObjectSearcher Searcher = new ManagementObjectSearcher(
        "SELECT * FROM Win32_Service WHERE Name =" + "\"" + serviceName + "\""))
    {
        foreach(ManagementObject service in Searcher.Get())
            servicePath = service["PathName"].ToString();
    }
    return servicePath;
}

 

Final note: to use WMI, don’t forget to include “using System.Management;” along with your other using directives, and to add reference to System.Management .NET component.

Also, pemissions will be an issue if your app is running as a restricted account.

Hope this helps. If you find a better way, please let me know!

Cheers!