ServiceBase denies access to service name

  • Thread starter Thread starter nickdu
  • Start date Start date
N

nickdu

It appears the ServiceBase class denies a derived class access to the service
name. It does this in its ServiceMainCallback by the fact that it skips
over the first argument:

public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef = &this.status)
{
string[] state = null;
if (argCount > 0)
{
char** chPtr = (char**) argPointer.ToPointer();
state = new string[argCount - 1];
for (int i = 0; i < state.Length; i++)
{
chPtr++;
state = Marshal.PtrToStringUni(*((IntPtr*) chPtr));
}
}
 
Hi Nick,

Thanks for your post.

There are many ways to set different service names for multiple instances
of a service class. Can you show me how you implemented this? Any sample
code is appreciated.

In the meanwhile, I will build a sample service project and use app.config
file to configure service names for multiple service instances. After my
researching on that, I will tell you how I implement it with some sample
code. Thanks.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).
 
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.
 
Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Hi Nick,

After several hours research on this issue, we found a solution to this
issue.

For a windows service with multi-instance running at the same time, we can
use WMI Win32_Service
class(http://msdn.microsoft.com/en-us/library/aa394418(VS.85).aspx) to
query the Name of a service by specifying the Process ID of the running
service. And here is the sample code:

using System;
using System.ServiceProcess;
using System.Management;
using System.Diagnostics;

namespace WindowsService3
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
int Pid = Process.GetCurrentProcess().Id;

ManagementObjectSearcher searcher = new
ManagementObjectSearcher(
"root\\CIMV2",
"SELECT Name FROM Win32_Service WHERE ProcessId = " + Pid);

String serviceName = null;
foreach (ManagementObject queryObj in searcher.Get())
{
serviceName = queryObj["Name"].ToString();
break;
}
}
}
}

Please try the code above and let me know if it works for you.

At beginning, I am trying to use app.config file to set the service name.
However, in this way, it requires multiple file copies of the service
executable file. That may not meet your requirement. As a reference, you
can take a look of
http://www.codeproject.com/KB/dotnet/MultipleInstNetWinService.aspx.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 
Hongye, thank's for your investigation into this. However, I'm very
skeptical of WMI and usually try to stay away from it at all costs. Our
experience of WMI is that it's unstable. In addition, what I was trying to
point out was a deficiency in the ServiceBase class in that it skips over the
service name and therefore does not make it available to the service. This
should be fixed.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com
 
Hi,

You seem to have a few misconceptions about .Net services and how the
ServiceName is set.

First of all you are misunderstanding the ServiceMainCallback and its
arguments. The arguments to ServiceMainCallback are not the command line
arguments of the executable but rather the arguments sent to the service
itself by the Service Control Manager. There is no service name argument, in
fact the arguments to ServiceMainCallback are almost always argCount = 0,
argPointer = null. These are the arguments sent to the OnStart method of
your service.

Even if these were the command line parameters arg[0] would be the name of
the executable not the service.

Also, this would not work for your purpose anyway since the
ServiceMainCallback is called by the SCM after ServiceBase.Run is called and
the ServiceName property has to be set before Run is called or the service
cannot properly connect to the SCM.

It is possible, and relatively easy, to run multiple named services using
the same service class but the names must be set in the Main method of the
service process before ServiceBase.Run is called. Your service class should
accept a name parameter in its constructor that is used to set the
ServiceName property.

You can then set up your service to run with a command line parameter that
is parsed in Main and sent to the service class in its constructor, each
named service would run in a different service process with a different
command line parameter. This, unfortunately, causes additional complexity
for installation and you may not want a separate executable for each
service.

Another method is to have a config file for the service executable that
contains the names of all the services in, say, its appSettings section.
This config file would be read in the Main method and a separate instance of
the service class would be created with each name and sent to the Run
method. This is easier to install and manage but many people do not go for
this for lack of isolation reasons.

If you wanted to go further and set up separate AppDomains for each service
you would do as above but your service class would just be a shell that
would spin up a worker thread and create a separate AppDomain into which
your real ServiceWork class would be loaded.

Hope this helps.

nickdu said:
It appears the ServiceBase class denies a derived class access to the
service
name. It does this in its ServiceMainCallback by the fact that it skips
over the first argument:

public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef = &this.status)
{
string[] state = null;
if (argCount > 0)
{
char** chPtr = (char**) argPointer.ToPointer();
state = new string[argCount - 1];
for (int i = 0; i < state.Length; i++)
{
chPtr++;
state = Marshal.PtrToStringUni(*((IntPtr*) chPtr));
}
}
.
.
.

Can this be fixed to either:

1. Provide the parameter in the args value. I assume this would break a
bunch of apps so it's probably not possible.

or

2. Process the service name argument (arg[0]) and set the ServiceName
property on the service object.

Actually the feature I'm trying to implement is one where I can have
multiple instances of the same service (different service names of course)
each with its own application configuration file. To accomplish this the
ServiceBase class could have created a new appdomain and run the service
component in that domain setting the app configuration file to the name of
the service, similar to how ASP.NET sets the app configuration to
web.config.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com
 
Thanks for your reply and feedback, Nick. Stephen has posted a great answer
to your question regarding ServiceMainCallback. This seems to be limitation
issue. I will forward your message to proper channel and product team.

For the WMI method, I suggest you to have a try of it, since this is a very
simple usage in this case and we have run the service in our lab for many
times without any error.

Have a nice day.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
I don't mind being educated on any misconceptions I have, but I don't think
you're correct on every point. The goal of what I'm trying to do is:

1. I've created a single managed service process that I want to use to
handle multiple services, each in their own process.

2. Since each service would be using the same binary and each would need its
own configuration, there needs to be some way for each service to have its
own configuration file. Similar to how web apps work I can envision some
service code create a new app domain and setting up the application config to
be <service name>.config.

#1 is easy to do in the unmanaged world because I have access to the command
line arguments and I'm pretty sure that the first command line argument is
the service name not the binary name. And unfortunately the .NET
implementation skips over the first command line argument.

--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


Stephen Martin said:
Hi,

You seem to have a few misconceptions about .Net services and how the
ServiceName is set.

First of all you are misunderstanding the ServiceMainCallback and its
arguments. The arguments to ServiceMainCallback are not the command line
arguments of the executable but rather the arguments sent to the service
itself by the Service Control Manager. There is no service name argument, in
fact the arguments to ServiceMainCallback are almost always argCount = 0,
argPointer = null. These are the arguments sent to the OnStart method of
your service.

Even if these were the command line parameters arg[0] would be the name of
the executable not the service.

Also, this would not work for your purpose anyway since the
ServiceMainCallback is called by the SCM after ServiceBase.Run is called and
the ServiceName property has to be set before Run is called or the service
cannot properly connect to the SCM.

It is possible, and relatively easy, to run multiple named services using
the same service class but the names must be set in the Main method of the
service process before ServiceBase.Run is called. Your service class should
accept a name parameter in its constructor that is used to set the
ServiceName property.

You can then set up your service to run with a command line parameter that
is parsed in Main and sent to the service class in its constructor, each
named service would run in a different service process with a different
command line parameter. This, unfortunately, causes additional complexity
for installation and you may not want a separate executable for each
service.

Another method is to have a config file for the service executable that
contains the names of all the services in, say, its appSettings section.
This config file would be read in the Main method and a separate instance of
the service class would be created with each name and sent to the Run
method. This is easier to install and manage but many people do not go for
this for lack of isolation reasons.

If you wanted to go further and set up separate AppDomains for each service
you would do as above but your service class would just be a shell that
would spin up a worker thread and create a separate AppDomain into which
your real ServiceWork class would be loaded.

Hope this helps.

nickdu said:
It appears the ServiceBase class denies a derived class access to the
service
name. It does this in its ServiceMainCallback by the fact that it skips
over the first argument:

public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef = &this.status)
{
string[] state = null;
if (argCount > 0)
{
char** chPtr = (char**) argPointer.ToPointer();
state = new string[argCount - 1];
for (int i = 0; i < state.Length; i++)
{
chPtr++;
state = Marshal.PtrToStringUni(*((IntPtr*) chPtr));
}
}
.
.
.

Can this be fixed to either:

1. Provide the parameter in the args value. I assume this would break a
bunch of apps so it's probably not possible.

or

2. Process the service name argument (arg[0]) and set the ServiceName
property on the service object.

Actually the feature I'm trying to implement is one where I can have
multiple instances of the same service (different service names of course)
each with its own application configuration file. To accomplish this the
ServiceBase class could have created a new appdomain and run the service
component in that domain setting the app configuration file to the name of
the service, similar to how ASP.NET sets the app configuration to
web.config.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com

 
#1 is done pretty much exactly the same way in .Net as it is in unmanaged
C/C++. The service name is not and has never been part of the command line
to a service executable. A service executable is just a normal Win32 binary
and has the exact same command line components as any other executable.
Let's say that arg[0] of the command line parameters to a service process
was the service name, what would arg[0] be in a service process that
contained two services named: MyServiceOne and MyServiceTwo?

#2 is easy enough using the Xml namespaces to parse your <service
name>.config file. Config files and the Configuration namespace classes are
a nice convenience but hardly necessary.

nickdu said:
I don't mind being educated on any misconceptions I have, but I don't think
you're correct on every point. The goal of what I'm trying to do is:

1. I've created a single managed service process that I want to use to
handle multiple services, each in their own process.

2. Since each service would be using the same binary and each would need
its
own configuration, there needs to be some way for each service to have its
own configuration file. Similar to how web apps work I can envision some
service code create a new app domain and setting up the application config
to
be <service name>.config.

#1 is easy to do in the unmanaged world because I have access to the
command
line arguments and I'm pretty sure that the first command line argument is
the service name not the binary name. And unfortunately the .NET
implementation skips over the first command line argument.

--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


Stephen Martin said:
Hi,

You seem to have a few misconceptions about .Net services and how the
ServiceName is set.

First of all you are misunderstanding the ServiceMainCallback and its
arguments. The arguments to ServiceMainCallback are not the command line
arguments of the executable but rather the arguments sent to the service
itself by the Service Control Manager. There is no service name argument,
in
fact the arguments to ServiceMainCallback are almost always argCount = 0,
argPointer = null. These are the arguments sent to the OnStart method of
your service.

Even if these were the command line parameters arg[0] would be the name
of
the executable not the service.

Also, this would not work for your purpose anyway since the
ServiceMainCallback is called by the SCM after ServiceBase.Run is called
and
the ServiceName property has to be set before Run is called or the
service
cannot properly connect to the SCM.

It is possible, and relatively easy, to run multiple named services using
the same service class but the names must be set in the Main method of
the
service process before ServiceBase.Run is called. Your service class
should
accept a name parameter in its constructor that is used to set the
ServiceName property.

You can then set up your service to run with a command line parameter
that
is parsed in Main and sent to the service class in its constructor, each
named service would run in a different service process with a different
command line parameter. This, unfortunately, causes additional complexity
for installation and you may not want a separate executable for each
service.

Another method is to have a config file for the service executable that
contains the names of all the services in, say, its appSettings section.
This config file would be read in the Main method and a separate instance
of
the service class would be created with each name and sent to the Run
method. This is easier to install and manage but many people do not go
for
this for lack of isolation reasons.

If you wanted to go further and set up separate AppDomains for each
service
you would do as above but your service class would just be a shell that
would spin up a worker thread and create a separate AppDomain into which
your real ServiceWork class would be loaded.

Hope this helps.

nickdu said:
It appears the ServiceBase class denies a derived class access to the
service
name. It does this in its ServiceMainCallback by the fact that it
skips
over the first argument:

public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef =
&this.status)
{
string[] state = null;
if (argCount > 0)
{
char** chPtr = (char**) argPointer.ToPointer();
state = new string[argCount - 1];
for (int i = 0; i < state.Length; i++)
{
chPtr++;
state = Marshal.PtrToStringUni(*((IntPtr*) chPtr));
}
}
.
.
.

Can this be fixed to either:

1. Provide the parameter in the args value. I assume this would break
a
bunch of apps so it's probably not possible.

or

2. Process the service name argument (arg[0]) and set the ServiceName
property on the service object.

Actually the feature I'm trying to implement is one where I can have
multiple instances of the same service (different service names of
course)
each with its own application configuration file. To accomplish this
the
ServiceBase class could have created a new appdomain and run the
service
component in that domain setting the app configuration file to the name
of
the service, similar to how ASP.NET sets the app configuration to
web.config.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com

 
When I get a few free moments I'll write an unmanaged service to verify the
first argument to the service.

With respect to #2, I really want it integrated with the application
configuration file so that any other code dependent on the application config
(all the .net settings which make use of the app config, any third party code
which uses the app config) will just work.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


Stephen Martin said:
#1 is done pretty much exactly the same way in .Net as it is in unmanaged
C/C++. The service name is not and has never been part of the command line
to a service executable. A service executable is just a normal Win32 binary
and has the exact same command line components as any other executable.
Let's say that arg[0] of the command line parameters to a service process
was the service name, what would arg[0] be in a service process that
contained two services named: MyServiceOne and MyServiceTwo?

#2 is easy enough using the Xml namespaces to parse your <service
name>.config file. Config files and the Configuration namespace classes are
a nice convenience but hardly necessary.

nickdu said:
I don't mind being educated on any misconceptions I have, but I don't think
you're correct on every point. The goal of what I'm trying to do is:

1. I've created a single managed service process that I want to use to
handle multiple services, each in their own process.

2. Since each service would be using the same binary and each would need
its
own configuration, there needs to be some way for each service to have its
own configuration file. Similar to how web apps work I can envision some
service code create a new app domain and setting up the application config
to
be <service name>.config.

#1 is easy to do in the unmanaged world because I have access to the
command
line arguments and I'm pretty sure that the first command line argument is
the service name not the binary name. And unfortunately the .NET
implementation skips over the first command line argument.

--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


Stephen Martin said:
Hi,

You seem to have a few misconceptions about .Net services and how the
ServiceName is set.

First of all you are misunderstanding the ServiceMainCallback and its
arguments. The arguments to ServiceMainCallback are not the command line
arguments of the executable but rather the arguments sent to the service
itself by the Service Control Manager. There is no service name argument,
in
fact the arguments to ServiceMainCallback are almost always argCount = 0,
argPointer = null. These are the arguments sent to the OnStart method of
your service.

Even if these were the command line parameters arg[0] would be the name
of
the executable not the service.

Also, this would not work for your purpose anyway since the
ServiceMainCallback is called by the SCM after ServiceBase.Run is called
and
the ServiceName property has to be set before Run is called or the
service
cannot properly connect to the SCM.

It is possible, and relatively easy, to run multiple named services using
the same service class but the names must be set in the Main method of
the
service process before ServiceBase.Run is called. Your service class
should
accept a name parameter in its constructor that is used to set the
ServiceName property.

You can then set up your service to run with a command line parameter
that
is parsed in Main and sent to the service class in its constructor, each
named service would run in a different service process with a different
command line parameter. This, unfortunately, causes additional complexity
for installation and you may not want a separate executable for each
service.

Another method is to have a config file for the service executable that
contains the names of all the services in, say, its appSettings section.
This config file would be read in the Main method and a separate instance
of
the service class would be created with each name and sent to the Run
method. This is easier to install and manage but many people do not go
for
this for lack of isolation reasons.

If you wanted to go further and set up separate AppDomains for each
service
you would do as above but your service class would just be a shell that
would spin up a worker thread and create a separate AppDomain into which
your real ServiceWork class would be loaded.

Hope this helps.

It appears the ServiceBase class denies a derived class access to the
service
name. It does this in its ServiceMainCallback by the fact that it
skips
over the first argument:

public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef =
&this.status)
{
string[] state = null;
if (argCount > 0)
{
char** chPtr = (char**) argPointer.ToPointer();
state = new string[argCount - 1];
for (int i = 0; i < state.Length; i++)
{
chPtr++;
state = Marshal.PtrToStringUni(*((IntPtr*) chPtr));
}
}
.
.
.

Can this be fixed to either:

1. Provide the parameter in the args value. I assume this would break
a
bunch of apps so it's probably not possible.

or

2. Process the service name argument (arg[0]) and set the ServiceName
property on the service object.

Actually the feature I'm trying to implement is one where I can have
multiple instances of the same service (different service names of
course)
each with its own application configuration file. To accomplish this
the
ServiceBase class could have created a new appdomain and run the
service
component in that domain setting the app configuration file to the name
of
the service, similar to how ASP.NET sets the app configuration to
web.config.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com

 
I just did a simple test and confirmed my prior statement: the first argument
to a Win32 own service is the service name. My service executable name is
MyService.exe. I registered the service as follows:

sc create FooService binPath= c:\...\MyService.exe ...

In my service main I do the following:

void WINAPI Service::ServiceMain(DWORD /*argc*/, const char * const *argv)
{
// Cleanup
Service *service = NULL;
// Cleanup
wchar_t *name = NULL;
SERVICE_STATUS_HANDLE statusHandle;
unsigned long cch;
int result;

cch = strlen(argv[0]) + 1;

HANDLE file;
HRESULT hr;
DWORD bytesWritten;
if ((file = CreateFile("c:\\servicename", GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE)
{
hr = GetLastError();
hr = HRESULT_FROM_WIN32(hr);
goto cleanup;
}
WriteFile(file, argv[0], strlen(argv[0]), &bytesWritten, NULL);
CloseHandle(file);

When I checked c:\servicename I see 'FooService'.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


Stephen Martin said:
#1 is done pretty much exactly the same way in .Net as it is in unmanaged
C/C++. The service name is not and has never been part of the command line
to a service executable. A service executable is just a normal Win32 binary
and has the exact same command line components as any other executable.
Let's say that arg[0] of the command line parameters to a service process
was the service name, what would arg[0] be in a service process that
contained two services named: MyServiceOne and MyServiceTwo?

#2 is easy enough using the Xml namespaces to parse your <service
name>.config file. Config files and the Configuration namespace classes are
a nice convenience but hardly necessary.

nickdu said:
I don't mind being educated on any misconceptions I have, but I don't think
you're correct on every point. The goal of what I'm trying to do is:

1. I've created a single managed service process that I want to use to
handle multiple services, each in their own process.

2. Since each service would be using the same binary and each would need
its
own configuration, there needs to be some way for each service to have its
own configuration file. Similar to how web apps work I can envision some
service code create a new app domain and setting up the application config
to
be <service name>.config.

#1 is easy to do in the unmanaged world because I have access to the
command
line arguments and I'm pretty sure that the first command line argument is
the service name not the binary name. And unfortunately the .NET
implementation skips over the first command line argument.

--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


Stephen Martin said:
Hi,

You seem to have a few misconceptions about .Net services and how the
ServiceName is set.

First of all you are misunderstanding the ServiceMainCallback and its
arguments. The arguments to ServiceMainCallback are not the command line
arguments of the executable but rather the arguments sent to the service
itself by the Service Control Manager. There is no service name argument,
in
fact the arguments to ServiceMainCallback are almost always argCount = 0,
argPointer = null. These are the arguments sent to the OnStart method of
your service.

Even if these were the command line parameters arg[0] would be the name
of
the executable not the service.

Also, this would not work for your purpose anyway since the
ServiceMainCallback is called by the SCM after ServiceBase.Run is called
and
the ServiceName property has to be set before Run is called or the
service
cannot properly connect to the SCM.

It is possible, and relatively easy, to run multiple named services using
the same service class but the names must be set in the Main method of
the
service process before ServiceBase.Run is called. Your service class
should
accept a name parameter in its constructor that is used to set the
ServiceName property.

You can then set up your service to run with a command line parameter
that
is parsed in Main and sent to the service class in its constructor, each
named service would run in a different service process with a different
command line parameter. This, unfortunately, causes additional complexity
for installation and you may not want a separate executable for each
service.

Another method is to have a config file for the service executable that
contains the names of all the services in, say, its appSettings section.
This config file would be read in the Main method and a separate instance
of
the service class would be created with each name and sent to the Run
method. This is easier to install and manage but many people do not go
for
this for lack of isolation reasons.

If you wanted to go further and set up separate AppDomains for each
service
you would do as above but your service class would just be a shell that
would spin up a worker thread and create a separate AppDomain into which
your real ServiceWork class would be loaded.

Hope this helps.

It appears the ServiceBase class denies a derived class access to the
service
name. It does this in its ServiceMainCallback by the fact that it
skips
over the first argument:

public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef =
&this.status)
{
string[] state = null;
if (argCount > 0)
{
char** chPtr = (char**) argPointer.ToPointer();
state = new string[argCount - 1];
for (int i = 0; i < state.Length; i++)
{
chPtr++;
state = Marshal.PtrToStringUni(*((IntPtr*) chPtr));
}
}
.
.
.

Can this be fixed to either:

1. Provide the parameter in the args value. I assume this would break
a
bunch of apps so it's probably not possible.

or

2. Process the service name argument (arg[0]) and set the ServiceName
property on the service object.

Actually the feature I'm trying to implement is one where I can have
multiple instances of the same service (different service names of
course)
each with its own application configuration file. To accomplish this
the
ServiceBase class could have created a new appdomain and run the
service
component in that domain setting the app configuration file to the name
of
the service, similar to how ASP.NET sets the app configuration to
web.config.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com

 
So it appears the SCM, at least for a Win32 OWN Service, sets arg[0] to the
service name. The .NET service class should not be skipping over this value.
Instead it should be used to set the service name. Currently the .NET
implementation purposely skips over the first argument (Stephen this is why
you usually see the length of the args as zero since most people don't
specify arguments to their service binary when registering their service).
I'm not suggesting the first argument, the service name, should come in as
one of the arguments, but instead it should be used to set the service name.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


nickdu said:
I just did a simple test and confirmed my prior statement: the first argument
to a Win32 own service is the service name. My service executable name is
MyService.exe. I registered the service as follows:

sc create FooService binPath= c:\...\MyService.exe ...

In my service main I do the following:

void WINAPI Service::ServiceMain(DWORD /*argc*/, const char * const *argv)
{
// Cleanup
Service *service = NULL;
// Cleanup
wchar_t *name = NULL;
SERVICE_STATUS_HANDLE statusHandle;
unsigned long cch;
int result;

cch = strlen(argv[0]) + 1;

HANDLE file;
HRESULT hr;
DWORD bytesWritten;
if ((file = CreateFile("c:\\servicename", GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE)
{
hr = GetLastError();
hr = HRESULT_FROM_WIN32(hr);
goto cleanup;
}
WriteFile(file, argv[0], strlen(argv[0]), &bytesWritten, NULL);
CloseHandle(file);

When I checked c:\servicename I see 'FooService'.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


Stephen Martin said:
#1 is done pretty much exactly the same way in .Net as it is in unmanaged
C/C++. The service name is not and has never been part of the command line
to a service executable. A service executable is just a normal Win32 binary
and has the exact same command line components as any other executable.
Let's say that arg[0] of the command line parameters to a service process
was the service name, what would arg[0] be in a service process that
contained two services named: MyServiceOne and MyServiceTwo?

#2 is easy enough using the Xml namespaces to parse your <service
name>.config file. Config files and the Configuration namespace classes are
a nice convenience but hardly necessary.

nickdu said:
I don't mind being educated on any misconceptions I have, but I don't think
you're correct on every point. The goal of what I'm trying to do is:

1. I've created a single managed service process that I want to use to
handle multiple services, each in their own process.

2. Since each service would be using the same binary and each would need
its
own configuration, there needs to be some way for each service to have its
own configuration file. Similar to how web apps work I can envision some
service code create a new app domain and setting up the application config
to
be <service name>.config.

#1 is easy to do in the unmanaged world because I have access to the
command
line arguments and I'm pretty sure that the first command line argument is
the service name not the binary name. And unfortunately the .NET
implementation skips over the first command line argument.

--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


:

Hi,

You seem to have a few misconceptions about .Net services and how the
ServiceName is set.

First of all you are misunderstanding the ServiceMainCallback and its
arguments. The arguments to ServiceMainCallback are not the command line
arguments of the executable but rather the arguments sent to the service
itself by the Service Control Manager. There is no service name argument,
in
fact the arguments to ServiceMainCallback are almost always argCount = 0,
argPointer = null. These are the arguments sent to the OnStart method of
your service.

Even if these were the command line parameters arg[0] would be the name
of
the executable not the service.

Also, this would not work for your purpose anyway since the
ServiceMainCallback is called by the SCM after ServiceBase.Run is called
and
the ServiceName property has to be set before Run is called or the
service
cannot properly connect to the SCM.

It is possible, and relatively easy, to run multiple named services using
the same service class but the names must be set in the Main method of
the
service process before ServiceBase.Run is called. Your service class
should
accept a name parameter in its constructor that is used to set the
ServiceName property.

You can then set up your service to run with a command line parameter
that
is parsed in Main and sent to the service class in its constructor, each
named service would run in a different service process with a different
command line parameter. This, unfortunately, causes additional complexity
for installation and you may not want a separate executable for each
service.

Another method is to have a config file for the service executable that
contains the names of all the services in, say, its appSettings section.
This config file would be read in the Main method and a separate instance
of
the service class would be created with each name and sent to the Run
method. This is easier to install and manage but many people do not go
for
this for lack of isolation reasons.

If you wanted to go further and set up separate AppDomains for each
service
you would do as above but your service class would just be a shell that
would spin up a worker thread and create a separate AppDomain into which
your real ServiceWork class would be loaded.

Hope this helps.

It appears the ServiceBase class denies a derived class access to the
service
name. It does this in its ServiceMainCallback by the fact that it
skips
over the first argument:

public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef =
&this.status)
{
string[] state = null;
if (argCount > 0)
{
char** chPtr = (char**) argPointer.ToPointer();
state = new string[argCount - 1];
for (int i = 0; i < state.Length; i++)
{
chPtr++;
state = Marshal.PtrToStringUni(*((IntPtr*) chPtr));
}
}
.
.
.

Can this be fixed to either:

1. Provide the parameter in the args value. I assume this would break
a
bunch of apps so it's probably not possible.

or

2. Process the service name argument (arg[0]) and set the ServiceName
property on the service object.

Actually the feature I'm trying to implement is one where I can have
multiple instances of the same service (different service names of
course)
each with its own application configuration file. To accomplish this
the
ServiceBase class could have created a new appdomain and run the
service
component in that domain setting the app configuration file to the name
of
the service, similar to how ASP.NET sets the app configuration to
web.config.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com

 
As I indicated in a reply to Stephen, my original statement appears to be
correct. The service name for a win32 own service is specified as the first
argument on the command line. The .NET implementation should not skip over
this argument but instead use it to set the service name.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com
 
Hi Nick,

Thanks very much for your great work on this issue.

After reviewing all of your posts, I started a .NET Framework service debug
session in our lab to verify if the arguments passed to ServiceMainCallback
method is the same as you tested in WIN32 service. The result is the same
as yours. argPointer is a string array and the first value is service name.

Here are the repro steps:
1. Create a windows service application with two services.
2. Start one of the services, so that we can use Windbg to attach to the
service process.
3. Using SOS to set break point at
System.ServiceProcess.ServiceBase.ServiceMainCallback.
4. Start the other service, and it will break at the ServiceMainCallback
method.
5. "!clrstack -p" to show all its parameters. At this point, the first
parameter argCount is 1 and argPointer is an address.
6. "du <argPointer's address>" to show its unicode string value. Service
name is in the output.

I do not know why the product team skips the service name in code. It may
have some undocumented reason. I will certainly report this issue to the
proper channel. In the meanwhile, I encourage you to report this issue to
connect website as well. The site URL is http://connect.microsoft.com/.
That will make it easy for other customers with the same issue to vote for
this change.

I was so impressed by your insistence and technical depth. It is my
pleasure to work with you. Thanks again for your great work on this issue.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 
Back
Top