We have built a Windows Service that is running on client's machines, which occasionally downloads a newer version of itself, and then performs a self-update: installs a new service, starts it, stops the old one, and eventually deletes it. The service cannot stop itself directly and do other stuff, so it spins another executable, which does some of the work. getting this right is tricky, and this is particularly bad when the newer service is built using a newer .Net Framework (such as the recent switch from .Net 2.0 to .net 4.0). The problem is that a .Net 2.0 library cannot operate on a .Net 4.0 service.
Now, one approach would be to have the older version of the service spin a helper program which comes with the newer version, but ... what if the behavior of it HAS to change in a breaking way? I feel safer about not mixing up the versions, even if some things usually stay the same - helps to keep the design complexity down.
Now, there appears to be a .Net version-neutral way of operating on Windows Services: the sc.exe
tool: http://support.microsoft.com/kb/251192
I wonder if it is indeed the silver bullet that I was seeking. Now, because I will be calling this guy programmatically, and checking for errors, then I might as well use an API for this. Ideally I would have one native C++ project, which compiles to a native exe, which interfaces with SC.exe. Is this possible? If not, then how can I go about locating sc.exe on different computers? They could be 32-bit or 64-bit, and running any version of Windows starting with Win XP SP2/3.
Let me know if you have questions about my question, or some ingenious idea, or an answer to the exact question that I posed here.
Edit: If I try to install a 4.0 service using 2.0 code, I get the same error as if I do:
> C:\Windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe MyService4.exe
Microsoft (R) .NET Framework Installation utility Version 2.0.50727.4927 Copyright (c) Microsoft Corporation. All rights reserved.
Exception occurred while initializing the installation: System.BadImageFormatException: Could not load file or assembly 'file:///[path]\MyService4.exe' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded..
sc.exe is actually always at the same path: %windir%\System32\sc.exe (x64 uses System32 for 64-bit binaries :-). Don't know how your current setup looks like, but if you have, or can switch to MSI - it can run custom install action which can be your new .exe with a config file either going for SCM API or just launching sc.exe 2-3 times to do stop, config, start. Technically you could even use .cmd as a custom install action in MSI but it may look a bit ugly (user seeing cmd window coming up).
If you want to go for SCM API and you know your C++, that would definitely be the best way to go - no layers in-between to mess something up. Also, once you have separate binary just for install tasks you are not bound by .NET's embedded installation procedure - you can reach for the same NT API from C# and C++. The key is that the binary that's doing admin tasks to stop, start, config etc. the service has to be different from the binary for the service itself - that's .NET's problem - lumping up.
Since you are doing a major switch, if your prior install isn't under MSI then a new MSI won't be able to handle everything on it's own, so you may want to literally drop a .cmd file to just stop and clean up the old service by using sc.exe, just to start clean, and then start new install binary - whatever it is. MSI would stop and unistall window service but only if it was previously installed by MSI. It's kind of sticky that way :-)
Your native C++ app can call the functions documented here: http://msdn.microsoft.com/en-us/library/ms685942(v=VS.85).aspx (the SCM API that others have mentioned)
You'll want to do something along the lines of
OpenSCManager
OpenService (or CreateService)
ChangeServiceConfig
CloseServiceHandle
I'm not sure what you mean by this:
The problem is that a .Net 2.0 library cannot operate on a .Net 4.0 service.
The ServiceController
class is capable of controlling any service, even native ones. I haven't tried it, but I believe that the ServiceInstaller
class could be used to (un)install any service as well.
If ServiceController
and ServiceInstaller
don't meet your needs, I'd recommend wrapping the native API directly instead of sc.exe
. The MSDN docs should get you started; you'll be needing the Service Control Manager functions. The sc.exe
program is just a thin command-line wrapper around the SCM API.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With