I have to run a process ie a application on windows shutdown, is there any method to delay the windows shutdown and run the application in windows service...
The ability of applications to block a pending system shutdown was severely restricted in Windows Vista. The details are summarized in two handy articles on MSDN: Shutdown Changes for Windows Vista and Application Shutdown Changes in Windows Vista.
As that page indicates, you shouldn't rely on the ability to block shutdown for any longer than 5 seconds. If you wish to attempt to block a pending shutdown event, your application should use the new ShutdownBlockReasonCreate function, which allows you to register a string that explains to the user the reason why you think the shutdown should be blocked. The user reserves the ability to heed your advice and cancel the shutdown, or throw caution to the wind and cancel anyway.
As soon as your application finishes doing whatever it is that should not be interrupted by a shutdown, you should call the corresponding ShutdownBlockReasonDestroy function, which frees the reason string and indicates that the system can now be shut down.
Also remember that Windows Services now run in an isolated session and are prohibited from interacting with the user. My answer here also provides more details, as well as a pretty diagram.
Basically, this is impossible. Windows is going to fight you tooth and nail over starting up a separate process from your Service, as well as any attempt you make to block a pending shutdown. Ultimately, the user has the power to override anything you try to pull. This sounds like something you should solve using security policies, rather than an application—ask questions about that on Server Fault.
Here is an article on the Shutdown Event Tracker. You can activate it in Windows XP. It prompts the user for a reason for shutdown.
namespace WindowsService1
{
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS
{
public int serviceType;
public int currentState;
public int controlsAccepted;
public int win32ExitCode;
public int serviceSpecificExitCode;
public int checkPoint;
public int waitHint;
}
public enum SERVICE_STATE : uint
{
SERVICE_STOPPED = 0x00000001,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_RUNNING = 0x00000004,
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007
}
public enum ControlsAccepted
{
ACCEPT_STOP = 1,
ACCEPT_PAUSE_CONTINUE = 2,
ACCEPT_SHUTDOWN = 4,
ACCEPT_PRESHUTDOWN = 0xf,
ACCEPT_POWER_EVENT = 64,
ACCEPT_SESSION_CHANGE = 128
}
[Flags]
public enum SERVICE_CONTROL : uint
{
STOP = 0x00000001,
PAUSE = 0x00000002,
CONTINUE = 0x00000003,
INTERROGATE = 0x00000004,
SHUTDOWN = 0x00000005,
PARAMCHANGE = 0x00000006,
NETBINDADD = 0x00000007,
NETBINDREMOVE = 0x00000008,
NETBINDENABLE = 0x00000009,
NETBINDDISABLE = 0x0000000A,
DEVICEEVENT = 0x0000000B,
HARDWAREPROFILECHANGE = 0x0000000C,
POWEREVENT = 0x0000000D,
SESSIONCHANGE = 0x0000000E
}
public enum INFO_LEVEL : uint
{
SERVICE_CONFIG_DESCRIPTION = 0x00000001,
SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002,
SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 0x00000003,
SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 0x00000004,
SERVICE_CONFIG_SERVICE_SID_INFO = 0x00000005,
SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 0x00000006,
SERVICE_CONFIG_PRESHUTDOWN_INFO = 0x00000007,
SERVICE_CONFIG_TRIGGER_INFO = 0x00000008,
SERVICE_CONFIG_PREFERRED_NODE = 0x00000009
}
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_PRESHUTDOWN_INFO
{
public UInt32 dwPreshutdownTimeout;
}
[Flags]
public enum SERVICE_ACCESS : uint
{
STANDARD_RIGHTS_REQUIRED = 0xF0000,
SERVICE_QUERY_CONFIG = 0x00001,
SERVICE_CHANGE_CONFIG = 0x00002,
SERVICE_QUERY_STATUS = 0x00004,
SERVICE_ENUMERATE_DEPENDENTS = 0x00008,
SERVICE_START = 0x00010,
SERVICE_STOP = 0x00020,
SERVICE_PAUSE_CONTINUE = 0x00040,
SERVICE_INTERROGATE = 0x00080,
SERVICE_USER_DEFINED_CONTROL = 0x00100,
SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
SERVICE_QUERY_CONFIG |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS |
SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_START |
SERVICE_STOP |
SERVICE_PAUSE_CONTINUE |
SERVICE_INTERROGATE |
SERVICE_USER_DEFINED_CONTROL)
}
[Flags]
public enum SCM_ACCESS : uint
{
STANDARD_RIGHTS_REQUIRED = 0xF0000,
SC_MANAGER_CONNECT = 0x00001,
SC_MANAGER_CREATE_SERVICE = 0x00002,
SC_MANAGER_ENUMERATE_SERVICE = 0x00004,
SC_MANAGER_LOCK = 0x00008,
SC_MANAGER_QUERY_LOCK_STATUS = 0x00010,
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x00020,
SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
SC_MANAGER_CONNECT |
SC_MANAGER_CREATE_SERVICE |
SC_MANAGER_ENUMERATE_SERVICE |
SC_MANAGER_LOCK |
SC_MANAGER_QUERY_LOCK_STATUS |
SC_MANAGER_MODIFY_BOOT_CONFIG
}
public partial class Service1 : ServiceBase
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll")]
internal static extern bool SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, IntPtr lpInfo);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
const int SERVICE_ACCEPT_PRESHUTDOWN = 0x100;
const int SERVICE_CONTROL_PRESHUTDOWN = 0xf;
public Service1()
{
InitializeComponent();
CanShutdown = true;
tim = new Timer();
tim.Interval = 5000;
tim.Elapsed += tim_Elapsed;
FieldInfo acceptedCommandsFieldInfo = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
int value = (int)acceptedCommandsFieldInfo.GetValue(this);
acceptedCommandsFieldInfo.SetValue(this, value | SERVICE_ACCEPT_PRESHUTDOWN);
StreamWriter writer = new StreamWriter("D:\\LogConst.txt", true);
try
{
IntPtr hMngr = OpenSCManager("localhost", null, (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
IntPtr hSvc = OpenService(hMngr, "WindowsService1", (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
SERVICE_PRESHUTDOWN_INFO spi = new SERVICE_PRESHUTDOWN_INFO();
spi.dwPreshutdownTimeout = 5000;
IntPtr lpInfo = Marshal.AllocHGlobal(Marshal.SizeOf(spi));
if (lpInfo == IntPtr.Zero)
{
writer.WriteLine(String.Format("Unable to allocate memory for service action, error was: 0x{0:X} -- {1}", Marshal.GetLastWin32Error(), DateTime.Now.ToLongTimeString()));
}
Marshal.StructureToPtr(spi, lpInfo, false);
// apply the new timeout value
if (!ChangeServiceConfig2(hSvc, (int)INFO_LEVEL.SERVICE_CONFIG_PRESHUTDOWN_INFO, lpInfo))
writer.WriteLine(DateTime.Now.ToLongTimeString() + " Failed to change service timeout");
else
writer.WriteLine(DateTime.Now.ToLongTimeString() + " change service timeout : " + spi.dwPreshutdownTimeout);
}
catch (Exception ex)
{
writer.WriteLine(DateTime.Now.ToLongTimeString() + " " + ex.Message);
}
writer.Close();
}
void tim_Elapsed(object sender, ElapsedEventArgs e)
{
result = false;
StreamWriter writer = new StreamWriter("D:\\hede.txt", true);
writer.WriteLine(DateTime.Now.ToLongTimeString());
//System.Threading.Thread.Sleep(5000);
writer.Close();
result = true;
tim.Stop();
}
Timer tim;
bool result = false;
protected override void OnStart(string[] args)
{
RequestAdditionalTime(1000);
tim.Start();
}
protected override void OnStop()
{
}
protected override void OnCustomCommand(int command)
{
StreamWriter writer = new StreamWriter("D:\\Log.txt", true);
try
{
if (command == SERVICE_CONTROL_PRESHUTDOWN)
{
int checkpoint = 1;
writer.WriteLine(DateTime.Now.ToLongTimeString());
while (!result)
{
SERVICE_STATUS myServiceStatus = new SERVICE_STATUS();
myServiceStatus.currentState = (int)SERVICE_STATE.SERVICE_STOP_PENDING;
myServiceStatus.serviceType = 16;
myServiceStatus.serviceSpecificExitCode = 0;
myServiceStatus.checkPoint = checkpoint;
SetServiceStatus(this.ServiceHandle, ref myServiceStatus);
checkpoint++;
}
writer.WriteLine(DateTime.Now.ToLongTimeString());
}
}
catch (Exception ex)
{
writer.WriteLine(DateTime.Now.ToLongTimeString() + " " + ex.Message);
}
writer.Close();
base.OnCustomCommand(command);
}
}
}
On Windows Vista SP1 and higher, the new SERVICE_CONTROL_PRESHUTDOWN is available. Unfortunately it is not supported by .NET framework yet, but here is workaround using reflection. Just inherit your service class from ServicePreshutdownBase
, override OnStop
and periodically call RequestAdditionalTime()
. Note that CanShutdown
should be set to false
.
public class ServicePreshutdownBase : ServiceBase
{
public bool Preshutdown { get; private set; }
public ServicePreshutdownBase()
{
Version versionWinVistaSp1 = new Version(6, 0, 6001);
if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= versionWinVistaSp1)
{
var acceptedCommandsField = typeof (ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
if (acceptedCommandsField == null)
throw new InvalidOperationException("Private field acceptedCommands not found on ServiceBase");
int acceptedCommands = (int) acceptedCommandsField.GetValue(this);
acceptedCommands |= 0x00000100; //SERVICE_ACCEPT_PRESHUTDOWN;
acceptedCommandsField.SetValue(this, acceptedCommands);
}
}
protected override void OnCustomCommand(int command)
{
// command is SERVICE_CONTROL_PRESHUTDOWN
if (command == 0x0000000F)
{
var baseCallback = typeof(ServiceBase).GetMethod("ServiceCommandCallback", BindingFlags.Instance | BindingFlags.NonPublic);
if (baseCallback == null)
throw new InvalidOperationException("Private method ServiceCommandCallback not found on ServiceBase");
try
{
Preshutdown = true;
//now pretend stop was called 0x00000001
baseCallback.Invoke(this, new object[] {0x00000001});
}
finally
{
Preshutdown = false;
}
}
}
}
Here is example usage:
public partial class Service1 : ServicePreshutdownBase
{
public Service1()
{
InitializeComponent();
this.CanShutdown = false;
}
protected override void OnStop()
{
WriteLog(Preshutdown ? "Service OnPreshutdown" : "Service OnStop");
for (int i = 0; i < 180; i++)
{
Thread.Sleep(1000);
WriteLog("Service stop in progress...");
RequestAdditionalTime(2000);
}
WriteLog(Preshutdown ? "Service preshutdown completed" : "Service stop completed");
}
}
This will work for 3min 20s, if you need more time, then you need to configure service. The best place to do so is during installation. Just use the ServicePreshutdownInstaller
instead of ServiceInstaller
and set the PreshutdownTimeout
to maximum time you will ever need.
public class ServicePreshutdownInstaller : ServiceInstaller
{
private int _preshutdownTimeout = 200000;
/// <summary>
/// Gets or sets the preshutdown timeout for the service.
/// </summary>
///
/// <returns>
/// The preshutdown timeout of the service. The default is 200000ms (200s).
/// </returns>
[DefaultValue(200000)]
[ServiceProcessDescription("ServiceInstallerPreshutdownTimeout")]
public int PreshutdownTimeout
{
get
{
return _preshutdownTimeout;
}
set
{
_preshutdownTimeout = value;
}
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
Version versionWinVistaSp1 = new Version(6, 0, 6001);
if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version < versionWinVistaSp1)
{
//Preshutdown is not supported
return;
}
Context.LogMessage(string.Format("Setting preshutdown timeout {0}ms to service {1}", PreshutdownTimeout, ServiceName));
IntPtr service = IntPtr.Zero;
IntPtr sCManager = IntPtr.Zero;
try
{
// Open the service control manager
sCManager = OpenSCManager(null, null, ServiceControlAccessRights.SC_MANAGER_CONNECT);
if (sCManager == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to open Service Control Manager.");
// Open the service
service = OpenService(sCManager, ServiceName, ServiceAccessRights.SERVICE_CHANGE_CONFIG);
if (service == IntPtr.Zero) throw new Win32Exception();
// Set up the preshutdown timeout structure
SERVICE_PRESHUTDOWN_INFO preshutdownInfo = new SERVICE_PRESHUTDOWN_INFO();
preshutdownInfo.dwPreshutdownTimeout = (uint)_preshutdownTimeout;
// Make the change
int changeResult = ChangeServiceConfig2(
service,
ServiceConfig2InfoLevel.SERVICE_CONFIG_PRESHUTDOWN_INFO,
ref preshutdownInfo);
// Check that the change occurred
if (changeResult == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to change the Service configuration.");
}
Context.LogMessage(string.Format("Preshutdown timeout {0}ms set to service {1}", PreshutdownTimeout, ServiceName));
}
finally
{
// Clean up
if (service != IntPtr.Zero)CloseServiceHandle(service);
if (sCManager != IntPtr.Zero)Marshal.FreeHGlobal(sCManager);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_PRESHUTDOWN_INFO
{
public UInt32 dwPreshutdownTimeout;
}
[Flags]
public enum ServiceControlAccessRights : int
{
SC_MANAGER_CONNECT = 0x0001, // Required to connect to the service control manager.
SC_MANAGER_CREATE_SERVICE = 0x0002, // Required to call the CreateService function to create a service object and add it to the database.
SC_MANAGER_ENUMERATE_SERVICE = 0x0004, // Required to call the EnumServicesStatusEx function to list the services that are in the database.
SC_MANAGER_LOCK = 0x0008, // Required to call the LockServiceDatabase function to acquire a lock on the database.
SC_MANAGER_QUERY_LOCK_STATUS = 0x0010, // Required to call the QueryServiceLockStatus function to retrieve the lock status information for the database
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020, // Required to call the NotifyBootConfigStatus function.
SC_MANAGER_ALL_ACCESS = 0xF003F // Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table.
}
[Flags]
public enum ServiceAccessRights : int
{
SERVICE_QUERY_CONFIG = 0x0001, // Required to call the QueryServiceConfig and QueryServiceConfig2 functions to query the service configuration.
SERVICE_CHANGE_CONFIG = 0x0002, // Required to call the ChangeServiceConfig or ChangeServiceConfig2 function to change the service configuration. Because this grants the caller the right to change the executable file that the system runs, it should be granted only to administrators.
SERVICE_QUERY_STATUS = 0x0004, // Required to call the QueryServiceStatusEx function to ask the service control manager about the status of the service.
SERVICE_ENUMERATE_DEPENDENTS = 0x0008, // Required to call the EnumDependentServices function to enumerate all the services dependent on the service.
SERVICE_START = 0x0010, // Required to call the StartService function to start the service.
SERVICE_STOP = 0x0020, // Required to call the ControlService function to stop the service.
SERVICE_PAUSE_CONTINUE = 0x0040, // Required to call the ControlService function to pause or continue the service.
SERVICE_INTERROGATE = 0x0080, // Required to call the ControlService function to ask the service to report its status immediately.
SERVICE_USER_DEFINED_CONTROL = 0x0100, // Required to call the ControlService function to specify a user-defined control code.
SERVICE_ALL_ACCESS = 0xF01FF // Includes STANDARD_RIGHTS_REQUIRED in addition to all access rights in this table.
}
public enum ServiceConfig2InfoLevel : int
{
SERVICE_CONFIG_DESCRIPTION = 0x00000001, // The lpBuffer parameter is a pointer to a SERVICE_DESCRIPTION structure.
SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002, // The lpBuffer parameter is a pointer to a SERVICE_FAILURE_ACTIONS structure.
SERVICE_CONFIG_PRESHUTDOWN_INFO = 0x00000007 // The lpBuffer parameter is a pointer to a SERVICE_PRESHUTDOWN_INFO structure.
}
[DllImport("advapi32.dll", EntryPoint = "OpenSCManager")]
public static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
ServiceControlAccessRights desiredAccess);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
public static extern int CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.dll", EntryPoint = "OpenService")]
public static extern IntPtr OpenService(
IntPtr hSCManager,
string serviceName,
ServiceAccessRights desiredAccess);
[DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]
public static extern int ChangeServiceConfig2(
IntPtr hService,
ServiceConfig2InfoLevel dwInfoLevel,
ref SERVICE_PRESHUTDOWN_INFO lpInfo);
}
I have a similar problem, and there is one trick that might work in your case. You can start application in question before shutdown is initiated with CREATE_SUSPENDED flag (see this). This will ensure that the process will be created, but never run. On shutdown you can ResumeThread that process, and it will go on with execution.
Note, that it might be possible that the process will not be able to initialize and run anyway, since during shutdown some OS functions will fail.
Another implication is: the process which is supposed to run on shutdown will show in task manager. It would be possible to kill that process.