Silently update a Windows service - c#

I have built a Windows service, now I want it to auto-update. I have read about a creating a second service to do that or different program , cant use click one, what about myBuild? Does anyone know it? What is the best way? Can I just change assemblies?

If you want your service to run while you are performing an update, here is what I had done before to achieve this:
Put your updateble logic into a separate DLL.
Create an AppDomain within your service.
Create file monitor that fires an event whenever you copy that file (you can use MSFT Ent Lib Updates)
Unload the old dll while blocking (queue) the threads that execute stuff from that dll
Load in the new dll file into the app domain.
Let your threads know to continue processing.

Download the new exe and any additional assembly's.
Rename your existing assembly's.
Copy in your new assembly's.
Restart Service. You can build the service restart function into your main service exe.
When service starts check for renamed files from step 2 and delete them to clean up.
To restart your service do
System.Diagnostics.Process.Start
(System.Reflection.Assembly.GetEntryAssembly().Location)
Then in your service do
private const string _mutexId = "MyUniqueId";
private static Mutex _mutex;
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
try
{
bool alreadyRunning = false;
try
{
Mutex.OpenExisting(_mutexId);
alreadyRunning = true;
}
catch (WaitHandleCannotBeOpenedException)
{
alreadyRunning = false;
}
catch
{
alreadyRunning = true;
}
if (alreadyRunning)
{
using (ServiceController sc = new ServiceController("MyServiceName"))
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 120));
sc.Start();
}
return;
}
}
catch
{
}
_mutex = new Mutex(true, _mutexId);
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
// Load the service into memory.
ServiceBase.Run(ServicesToRun);
_mutex.Close();
}

You could modify your Windows Service so that it is simply a runner for your main application, and has the functionality to update your main application.
So you would have:
Service.exe: Runs Application.exe, monitors remote location for updates to Application.exe. Sends start/stop events to Application.exe
Application.exe : What used to be your Service.exe. Recieves start/stop events.

Related

How to re-start a console application if it crashes?

I have created a console application in C#. How can I program this application so that it will re-start itself after a crash?
If I understand your question correctly, you want to attempt to re-start a console app in the event of a crash. In C# console-apps the method defined as the entry point (usually static void main) is the root of the call stacks in the app. You essentially would need to call that method recursively. You will want to make sure that the app eventually fails if it is in some unintended or unrecoverable state.
For example in the main class:
static int retryCount;
const int numberOfRetries = 3;
static void Main(string[] args)
{
try
{
var theApp = new MyApplicationType(args);
theApp.StartMyAppLogic();
}
catch (ExpectedExceptionType expectThisTypeOfException)
{
thisMethodHandlesExceptions(expectThisTypeOfException);
}
catch (AnotherExpectedExceptionType alsoExpectThisTypeOfException)
{
thisMethodHandlesExceptions(alsoExpectThisTypeOfException);
}
catch (Exception unexpectedException)
{
if(retryCount < numberOfRetries)
{
retryCount++;
Main(args);
}
else
{
throw;
}
}
}
You can use a watchdog to process your monitor and restart it if crashed:
see: What's the best way to watchdog a desktop application?
You can use a windows service instead and set it's recovery options as indicated here: https://serverfault.com/questions/48600/how-can-i-automatically-restart-a-windows-service-if-it-crashes
You can use a scheduled task in task manager to start your application periodically , and set it to only start if previous run has ended:
https://support.microsoft.com/en-us/kb/323527
You could try something like this:
static void Main(string[] args)
{
try
{
// Application code goes here
}
catch (Exception)
{
var applicationPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
Process.Start(applicationPath);
Environment.Exit(Environment.ExitCode);
}
}
Basically, wrap all the code in a try/catch, and if any exceptions occur, the program will retrieve the .exe location with System.Reflection.Assembly.GetExecutingAssembly().Location; and then call Process.Start to run the application again.
You should control your console app from another application (watchdog, sheduler, procmon, servman, ...).
E.g. you can create your console app as a service and control it from service manager.

Windows Service Never Restarted from C# Code

I have written a code to restart my windows service but it never works properly. Service is able to stop but never start again. My Project is self hosted WCF service inside a Windows Service. below is the code.
Restart Method
public static void RestartService(string serviceName)
{
try
{
ServiceController svc = new ServiceController(serviceName);
if (svc.Status == ServiceControllerStatus.Running) svc.Stop();
svc.WaitForStatus(ServiceControllerStatus.Stopped, new System.TimeSpan(0, 0, 20));
svc.Start();
svc.WaitForStatus(ServiceControllerStatus.Running, new System.TimeSpan(0, 0, 20));
svc.Dispose();
}
catch (Exception ex)
{
Logging.WriteException(ex);
}
}
Main Program
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
try
{
ServiceBase[] ServicesToRun =
{
new MyService()
};
ServiceBase.Run(ServicesToRun);
}
catch (Exception)
{
}
}
The correct way to manage restart of services is by using Windows Service Recovery Actions, described here.
They allow you to tell the Service Control Manager what to do in the event of a failure, including any restart actions, how many times to attempt a restart, and how long to wait between attempts.
This is way more robust than anything you could write in the service code itself.

Start an offline ClickOnce Application and wait for Exit

I have deployed a ClickOnce Windows Forms application (App A)
Another application (App B) starts App A with a filename as parameter.
I do this with this Code
var basePath = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
var location = String.Format(#"{0}\{1}\{2}\{3}",
basePath, "MyCompany", "MyProduct", "MyApp.appref-ms");
var fileName = #"c:\temp\somefile.ext";
var uri = new Uri(fileName).ToString();
Process.Start(location, uri);
App A grabs the file name from AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData[0] and show the content.
This works like a charm. However, now I want App B to wait for App A to exit.
But a call to Process.WaitForExit() returns instantly.
Is there a way to open a ClickOnce App and wait for it to exit? I can, if necessary, change the way the app is opend but the requirement is that I need to run the app as a ClickOnce app (I know that somewhere in my user profile AppData\Local\Apps\2.0\ folder the exe exists and can be started directly but If I do that ApplicationDeployment.IsNetworkDeployed is false and ApplicationDeployment.CurrentDeployment is null. In that I loose the ClickOnce Update Capabilities).
my suggestion would be to use Mutex in App A, and let App B check and wait for it. This is the cleanest way from my point of view.
App A does this when starts:
private static Mutex mutex;
public static void Main()
{
// if you want your app to be limited to a single instance
// across ALL SESSIONS (multiple users & terminal services), then use the following line instead:
// string mutexName = string.Format("Global\\{0}", ProgramInfo.AssemblyGuid);
var mutexName = string.Format("Local\\{0}", SOME_SHARED_GUID);
mutex = new Mutex(true, mutexName, out singleInstance);
if (singleInstance == false)
{
// that means your app has more than one instance running
// you need to decide what to do here.
}
// rest of initialization code
Application.Run();
// release the mutex so App B can continue
mutex.ReleaseMutex();
}
and App B just waits for the mutex to be released:
Process.Start(location, uri);
Thread.Sleep(5000); // give it 5 seconds or so to check for updates and start
var mutexName = string.Format("Local\\{0}", SOME_SHARED_GUID);
mutex = new Mutex(false, mutexName);
mutex.WaitOne();
The problem is that starting the appref-ms process does not actually start the application it starts the deployment manifest, which then launches the application itself, so the process you are starting exits straight away.
You can add a check to see when you application has started if you know the name (which I assume you do) like this:
string myAppName = "YourAppName";
DateTime startTime = DateTime.Now;
int newProcessId = 0;
List<int> runningProcessIds = new List<int>();
//find all the running processes and record their Ids
foreach (void proc_loopVariable in Process.GetProcessesByName(myAppName)) {
proc = proc_loopVariable;
runningProcessIds.Add(proc.Id);
}
//start the new process
Process.Start(location);
//wait for the new application to be started
while (!(Process.GetProcessesByName(myAppName).Count != runningProcessIds.Count)) {
//timeout if we have not seen the application start
if ((DateTime.Now - startTime).TotalSeconds > 30)
break;
}
//loop through all the running processes again to find the id of the one that has just started
foreach (void proc_loopVariable in Process.GetProcessesByName(myAppName)) {
proc = proc_loopVariable;
if (!runningProcessIds.Contains(proc.Id)) {
newProcessId = proc.Id;
break;
}
}
//wait for the application to finish
Process.GetProcessById(newProcessId).WaitForExit();
Debug.WriteLine("Finished");

Start Windows Service in C#

I want to start a windows service that was just installed.
ServiceBase[] ServicesToRun;
if (bool.Parse(System.Configuration.ConfigurationManager.AppSettings["RunService"]))
{
ServicesToRun = new ServiceBase[] { new IvrService() };
ServiceBase.Run(ServicesToRun);
}
The IvrService code is:
partial class IvrService : ServiceBase
{
public IvrService()
{
InitializeComponent();
Process myProcess;
myProcess = System.Diagnostics.Process.GetCurrentProcess();
string pathname = Path.GetDirectoryName(myProcess.MainModule.FileName);
//eventLog1.WriteEntry(pathname);
Directory.SetCurrentDirectory(pathname);
}
protected override void OnStart(string[] args)
{
string sProcessName = Process.GetCurrentProcess().ProcessName;
if (Environment.UserInteractive)
{
if (sProcessName.ToLower() != "services.exe")
{
// In an interactive session.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new IvrInteractive());
IvrApplication.Start(); // the key function of the service, start it here
return;
}
}
}
I am not sure how to start the service. Using ServiceController.Start()? But I already have ServiceBase.Run(ServicesToRun); Is it for starting a service?
Code hint is definitely appreciated.
To answer the question about starting a service from code, you would want something like this (which would be equivalent to running net start myservice from the command line):
ServiceController sc = new ServiceController();
sc.ServiceName = "myservice";
if (sc.Status == ServiceControllerStatus.Running ||
sc.Status == ServiceControllerStatus.StartPending)
{
Console.WriteLine("Service is already running");
}
else
{
try
{
Console.Write("Start pending... ");
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 10));
if (sc.Status == ServiceControllerStatus.Running)
{
Console.WriteLine("Service started successfully.");
}
else
{
Console.WriteLine("Service not started.");
Console.WriteLine(" Current State: {0}", sc.Status.ToString("f"));
}
}
catch (InvalidOperationException)
{
Console.WriteLine("Could not start the service.");
}
}
This will start the service, but keep in mind that it will be a different process than the one that is executing the above code.
Now to answer the question about debugging a service.
One option is to attach after the service has been started.
The other is to make your service executable be able to run the main code, not as a service but as a normal executable (typically setup via a command line parameter). Then you can F5 into it from your IDE.
EDIT: Adding sample flow of events (based on questions from some of the comments)
OS is asked to start a service. This can be done from the control panel, the command line, APIs (such as the code above), or automatically by the OS (depending on the service's startup settings).
The operating system then creates a new process.
The process then registers the service callbacks (for example ServiceBase.Run in C# or StartServiceCtrlDispatcher in native code). And then starts running its code (which will call your ServiceBase.OnStart() method).
The OS can then request that a service be paused, stopped, etc. At which point it will send a control event to the already running process (from step 2). Which will result in a call to your ServiceBase.OnStop() method.
EDIT: Allowing a service to run as a normal executable or as a command line app: One approach is to configure your app as a console application, then run different code based on a command line switch:
static void Main(string[] args)
{
if (args.Length == 0)
{
// we are running as a service
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run(ServicesToRun);
}
else if (args[0].Equals("/debug", StringComparison.OrdinalIgnoreCase))
{
// run the code inline without it being a service
MyService debug = new MyService();
// use the debug object here
}
First you need to install the service using InstallUtil, e.g.
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe C:\MyService.exe
You need to start the service from the command line, e.g.
net start ServiceName // whatever the service is called
Then you need to attach to the process with Visual Studio, via Tools > Attach To Process.
Stick the breakpoints in the solution wherever you want it to break and the dev environment should take over.
To start it from the dev environment, put the net start ServiceName command into a batch file, and then in the Project Properties > Build Events "Post Build Event Command Line" you can add the path to the batch file.
*note that looking now, I'm not sure if you need to even use a batch file, you might be able to put the command directly in there. Try and edit this answer with whatever works.

Process cannot access the file because it is being used by another process on Directory.Move()

We have a client application with the following architecture: a manager process manages a couple of worker processes (reader and writer) and periodically queries the server for version updates. If a version update is available, the manager downloads it to the client computer, shuts down the worker threads, starts an updater process to handle the update and exits. The updater, on startup, receives the manager PID and the update file location; it then waits for the manager to exit, backs up all files of the manager and workers, recreates their directories and spreads the new version files to the new directories.
When going through this process as described, the first call to Directory.Move(string, string) – which serves to back up the manager directory – throws the IOException. The strange thing is, if I let the manager shut down without starting the updater and then start the updater executable myself, the exception is not thrown.
Manager code for managing worker threads:
public void Run()
{
_config = GetConfiguration();
Process reader, writer;
//Start reader and writer with appropriate arguments
//Keep reader and writer alive
reader.Kill();
writer.Kill();
reader.WaitForExit();
writer.WaitForExit();
reader.Dispose();
writer.Dispose();
}
Manager code for querying the database:
EndpointAddress endpoint;
BasicHttpBinding httpBinding = new BasicHttpBinding();
httpBinding.MaxReceivedMessageSize = 2000000000;
ChannelFactory<IService> chanFactory = new ChannelFactory<IService>(httpBinding);
IService service;
try
{
endpoint = new EndpointAddress(ConfigurationManager.AppSettings["Service URL"]);
service = chanFactory.CreateChannel(endpoint);
UpdateInstructions instructions = service.GetUpdateInstructions(_config.SiteID, Assembly.GetExecutingAssembly().GetName().Version.ToString(), _config.Version);
HandleUpdateInstructions(instructions); //Downloads files and starts the updater process
}
catch (Exception ex)
{
//Report exception
}
finally
{
if (chanFactory.State != CommunicationState.Faulted)
chanFactory.Close();
}
Manager code for starting the updater process:
private void StartUpdater(string updateFilePath, string configFilePath)
{
ProcessStartInfo updaterStartInfo = new ProcessStartInfo(_config.UpdaterExePath, string.Format("{0} \"{1}\" \"{2}\"", Process.GetCurrentProcess().Id, updateFilePath, configFilePath));
Process updater = Process.Start(updaterStartInfo);
updater.Dispose();
}
Updater code for waiting for the manager to close:
bool isManagerUp = true;
while (isManagerUp)
{
try
{
Process managerProcess = Process.GetProcessById(bDoxForceManagerPID);
managerProcess.WaitForExit();
managerProcess.Dispose();
isManagerUp = false;
}
catch
{
isManagerUp = false;
}
}
Updater code for updating a module:
//updateDirectory is the directory of the new files to be inserted, moduleDirectory is the working directory of the module that will be updated, in this case the manager
private void UpdateModule(DirectoryInfo updateDirectory, DirectoryInfo moduleDirectory)
{
string backupDirectory = MakeBackupDirectoryFullPath(moduleDirectory.Parent.FullName);
Directory.Move(moduleDirectory.FullName, backupDirectory); // IOException as described above.
Directory.CreateDirectory(moduleDirectory.FullName);
foreach (FileInfo updateFile in updateDirectory.EnumerateFiles())
{
string newFilePath = moduleDirectory.FullName + "\\" + updateFile.Name;
File.Copy(updateFile.FullName, newFilePath);
}
Directory.Delete(updateDirectory.FullName, true);
}
Thank to Adam Caviness answer we were able to figure it out.
Our processes were Console applications, they created a .vshost files that kept on working after the processes were order to terminate.
Attempting to move the directory with the running .vshost files caused the problem.
Turning the processes into Windows services didn't create a .vshost files and solved this issue.
I suggest you use MS (formally SysInternals) Process Monitor to track this down and thus first rule out any anti-virus/anti-malware/heuristics (should you not be going av commando like we devs do). The clue to that makes me point you in this direction is that you can start the updater yourself and the exception is not thrown. Just this year already I've ran into this issue and had to add an AV directory exclusion.

Categories