как отложить выключение и запустить процесс в оконной службе - PullRequest
6 голосов
/ 07 марта 2011

Мне нужно запустить процесс, т. Е. Приложение при завершении работы Windows, есть ли способ отложить закрытие Windows и запустить приложение в службе Windows ...

protected override void OnShutdown()
{
    // Add your save code here
    // Add your save code here
    StreamWriter str = new StreamWriter("D:\\Log.txt", true);
    str.WriteLine("Service stoped due to on" + DateTime.Now.ToString());
    str.Close();

    base.OnShutdown();
}

Я использовал функцию, над которойотменяет завершение работы, и мне удалось записать запись журнала в текстовый файл, но я не смог запустить приложение после этого. При поиске я обнаружил, что задержка была ниже, чем через несколько секунд после того, как пользователь завершил работу

this.RequestAdditionalTime (250000);

это дает дополнительную задержку времени 25 секунд при событии завершения работы, но я не смог запустить приложение.Может кто-нибудь предложить метод или идеи для запуска приложения при завершении работы.

Ответы [ 5 ]

11 голосов
/ 07 марта 2011

Возможность приложений блокировать ожидающее завершение работы системы была строго ограничена в Windows Vista.Подробности суммированы в двух удобных статьях на MSDN: Изменения при выключении для Windows Vista и Изменения при выключении приложений в Windows Vista .

Как показывает эта страница, вы не должныне полагайтесь на способность блокировать отключение на срок более 5 секунд.Если вы хотите попытаться заблокировать ожидающее событие выключения, ваше приложение должно использовать новую ShutdownBlockReasonCreate функцию , которая позволяет вам зарегистрировать строку, объясняющую пользователю причину, по которой вы считаете, что выключение должно бытьзаблокирован.Пользователь оставляет за собой возможность прислушаться к вашему совету и отменить завершение работы или отменить осторожность и все равно отменить.

Как только ваше приложение завершит , делая то, что не должно бытьпрерванный выключением, вы должны вызвать соответствующую ShutdownBlockReasonDestroy функцию , которая освобождает строку причины и указывает, что теперь система может быть закрыта.

Также помните, что WindowsСервисы теперь работают в изолированном сеансе и им запрещено взаимодействовать с пользователем. Мой ответ здесь также предоставляет более подробную информацию, а также симпатичную диаграмму.

В принципе, это невозможно.Windows сделает все возможное, чтобы запустить отдельный процесс от вашей Службы, а также любую вашу попытку заблокировать ожидающее завершение работы.В конечном счете, пользователь может переопределить все, что вы пытаетесь вытащить.Это похоже на то, что вы должны решить, используя политики безопасности, а не приложение - задавайте вопросы об этом на Ошибка сервера .

4 голосов
/ 10 сентября 2014

В Windows Vista с пакетом обновления 1 (SP1) и более поздних версий доступен новый SERVICE_CONTROL_PRESHUTDOWN. К сожалению, пока не поддерживается .NET Framework, но здесь есть обходной путь, использующий отражение. Просто унаследуйте свой класс обслуживания от ServicePreshutdownBase, переопределите OnStop и периодически звоните RequestAdditionalTime(). Обратите внимание, что CanShutdown должно быть установлено на 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;
            }
        }
    }
}

Вот пример использования:

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");
    }
}

Это будет работать в течение 3 минут 20 секунд. Если вам нужно больше времени, вам нужно настроить службу. Лучшее место для этого - во время установки. Просто используйте ServicePreshutdownInstaller вместо ServiceInstaller и установите для PreshutdownTimeout максимальное время, которое вам когда-либо понадобится.

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);
}
1 голос
/ 05 декабря 2011

У меня аналогичная проблема , и есть один прием, который может сработать в вашем случае. Вы можете запустить рассматриваемое приложение до того, как выключение будет инициировано с флагом CREATE_SUSPENDED (см. this ). Это гарантирует, что процесс будет создан, но никогда не будет запущен. При выключении вы можете ResumeThread этот процесс, и он продолжит выполнение.

Обратите внимание, что, возможно, процесс не сможет быть инициализирован и запущен в любом случае, так как во время завершения работы некоторых функций ОС произойдет сбой.

Еще одно следствие: процесс, который должен запускаться при завершении работы, будет отображаться в диспетчере задач. Можно было бы убить этот процесс.

0 голосов
/ 26 сентября 2013

пространство имен 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);
    }
}

}

0 голосов
/ 07 марта 2011

Вот статья в журнале событий завершения работы.Вы можете активировать его в Windows XP.Он запрашивает у пользователя причину отключения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...