views:

433

answers:

2

I've looked and couldn't find what should be a simple question:

How can a Windows Service determine the ServiceName for which it was started?

I know the installation can hack at the registry and add a command line argument, but logically that seems like it should be unnecessary, hence this question.

I'm hoping to run multiple copies of a single binary more cleanly than the registry hack.

Edit:

This is written in C#. My apps Main() entry point does different things, depending on command line arguments:

  • Install or Uninstall the service. The command line can provide a non-default ServiceName and can change the number of worker threads.
  • Run as a command-line executable (for debugging),
  • Run as a "Windows Service". Here, it creates an instance of my ServiceBase-derived class, then calls System.ServiceProcess.ServiceBase.Run(instance);

Currently, the installation step appends the service name and thread count to the ImagePath in the registry so the app can determine it's ServiceName.

A: 

The ServiceMain() entry point that every service executable must implement receives the ServiceName as its first input argument.

If you are writing your service using .NET, the ServiceMain() entry point is implemented by .NET for you. The ServiceName is assigned when the service is installed using the ServiceProcess.ServiceBase.ServiceName property. If you are trying to customize a .NET service to support dynamic ServiceName values, I have no clue how to access the actual ServiceName at runtime.

Remy Lebeau - TeamB
to identify at runtime use command SC QUERYEX
lsalamon
@Remy - it looks like ServiceMain is for C++ code ( http://msdn.microsoft.com/en-us/library/ms685138%28VS.85%29.aspx ), I'm using C#. @Isalamon - I believe you're mistaken, but if you can show how **SC** can be used, please post an answer with details.
NVRAM
Even .NET services have a ServiceMain() entry point, just one that .NET implements for you (the ServiceBase.ServiceMainCallback() method). I just noticed that the ServiceBase.OnStart event has an args parameter. I have not written services in .NET, but assuming ServiceBase does not strip out the first parameter provided by ServiceMain() (the ServiceName) when building up the args parameter, then you should be able to pull out the ServiceName from it.
Remy Lebeau - TeamB
Unfortunately, .NET does strip out the argument. And the OnStart() parameters are only used when starting a service *interactively*. Ref: http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework/2009-01/msg00073.html
NVRAM
+2  A: 

From: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024

Here is a WMI solution. Overriding the ServiceBase.ServiceMainCallback() might also work, but this seems to work for me...

    protected String GetServiceName()
    {
        // Calling System.ServiceProcess.ServiceBase::ServiceNamea allways returns
        // an empty string,
        // see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024

        // So we have to do some more work to find out our service name, this only works if
        // the process contains a single service, if there are more than one services hosted
        // in the process you will have to do something else

        int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
        String query = "SELECT * FROM Win32_Service where ProcessId = " + processId;
        System.Management.ManagementObjectSearcher searcher =
            new System.Management.ManagementObjectSearcher(query);

        foreach (System.Management.ManagementObject queryObj in searcher.Get()) {
            return queryObj["Name"].ToString();
        }

        throw new Exception("Can not get the ServiceName");
    }
NVRAM