8
votes

Comment retarder l'arrêt et exécuter un processus dans le service de fenêtre

Je dois exécuter un processus, c'est-à-dire une application sur l'arrêt Windows, existe-t-il une méthode pour retarder l'arrêt Windows et exécuter l'application dans Windows Service ...

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


8 commentaires

Il n'est généralement pas une bonne idée de retarder l'arrêt lorsque l'utilisateur a confirmé sa demande d'arrêt. En outre, Windows refuse diverses actions lors de l'arrêt. Une de ces actions commence un service Windows. Pour que votre processus commence réellement, vous aurez peut-être besoin de beaucoup d'ingénierie et de piratage.


Vous devez redéfinir votre système - essayer de démarrer de nouveaux processus lorsque Windows veut que Windows semble être une conception assez médiocre - quel état est votre système si Windows se bloque et redémarre?


Puis puis-je abandonner l'arrêt du service de fenêtre et ouvrir une application sur l'arrêt de la sortie de l'application.


@deepu: Non, vous ne pouvez pas. Il n'y a pas de moyen fiable d'abandonner une fermeture et d'un service Windows ne peut pas accéder aux applications en mode utilisateur. Ma réponse fournit plus de détails, mais le résumé est que vous devez trouver une approche différente. Peut-être que si vous avez expliqué ce que vous essayez d'accomplir, au lieu de votre solution proposée, nous pourrions vous donner de meilleurs conseils.


@Codygray: Merci pour votre réponse ci-dessous ... Je vais décrire brièvement ce que j'essaie d'accomplir ... je suis proposé de faire une application qui enregistre le temps d'utilisateur et arrêtez le système de Windows XP. J'ai pu exécuter l'application sur le démarrage où je reçois les détails de l'heure du système à dB, mais je dois exécuter la demande à l'arrêt, pour vous connecter lorsque le système a été arrêté et quelle est la raison de la fermeture. J'ai fait une application de fenêtre .NET en C # et j'ai appelé l'application à l'aide du service de fenêtre. S'il vous plaît suggérer des idées pour l'entraîner


Je pense que @deepu veut quelque chose de similaire à la fenêtre "Raison de l'arrêt" Windows Server 2003 a. Voir ma réponse ci-dessous.


Lors de la recherche, j'ai constaté que nous pouvons exécuter un script sur shutdown / démarrage sous Windows. C'est une belle méthode pour exécuter mon fichier application (EXE) à ce sujet. Si je l'exécute, je pourrai-je mettre à jour la DB avec les valeurs L'application.


@deepu: Peut-être au prochain démarrage.


5 Réponses :


11
votes

La capacité des applications à bloquer un arrêt du système en attente a été sévèrement limitée dans Windows Vista. Les détails sont résumés dans deux articles pratiques sur MSDN: Changements d'arrêt de Windows Vista et Changements d'arrêt de l'application dans Windows Vista .

Comme cette page indique, vous ne devez pas compter sur la capacité de bloquer l'arrêt pendant plus de 5 secondes. Si vous souhaitez tenter de bloquer un événement d'arrêt en attente, votre application doit utiliser le nouveau ShutdownBlockReasonCreate Fonction , qui vous permet d'enregistrer une chaîne qui explique à l'utilisateur la raison pour laquelle vous pensez que l'arrêt doit être bloqué. L'utilisateur se réserve la capacité de répondre à vos conseils et d'annuler l'arrêt, ou de faire preuve de prudence au vent et d'annuler quand même.

Dès que votre demande finition faire tout ce qui ne devrait pas être interrompu par un arrêt, vous devez appeler le shutdownblockreasondestroy fonction , qui libère la chaîne de motif et indique que le système peut maintenant être arrêté.

N'oubliez pas non plus que Windows Services est maintenant exécuté dans une session isolée et il est interdit d'interagir avec l'utilisateur. Ma réponse ici fournit également plus de détails, ainsi qu'un joli diagramme.

Fondamentalement, c'est impossible. Windows va vous battre pour vous battre et clouer sur le point de démarrer un processus séparé de votre service, ainsi que toute tentative que vous apportez pour bloquer un arrêt en attente. En fin de compte, l'utilisateur a le pouvoir de remplacer tout ce que vous essayez de tirer. Cela ressemble à quelque chose que vous devez résoudre à l'aide de stratégies de sécurité plutôt que d'une application - posez des questions à ce sujet sur défaut de serveur .


2 commentaires

+1 Lorsque l'utilisateur dit qu'ils veulent arrêter, vous devez maintenant respecter ce souhait.


@David: Quel est le point d'être un programmeur si nous permettons à l'utilisateur d'avoir un contrôle sur leur machine? Ennuyeuse.



0
votes

Voici un article sur le TRAVAILLE DE L'ÉVÉNEMENT ÉTUDDOI. Vous pouvez l'activer dans Windows XP. Il incite à l'utilisateur une raison de l'arrêt.


9 commentaires

@MPelletier: Je ne veux pas savoir quelle erreur a provoqué l'arrêt du système. Ce que je voulais dire, c'est lorsque l'utilisateur ferme le système normalement, mon application s'exécutera lorsque l'utilisateur donne la raison de la fermeture du système comme "J'ai Assister à une réunion "," pause déjeuner "," aller à la maison - travail au bureau ".. etc espère que vous obtenez l'idée.


Je pense que c'est ce que Shutdown Event Tracker est destiné aux erreurs, mais sans erreur, enregistrant essentiellement un arrêt.


Cela étant dit, je ne veux jamais travailler à un endroit qui a un tel système en place.


@MPelletier: YA, il signale tous les événements d'arrêt..mais mon intention est de charger une application sur l'arrêt .. :)


@MPelletier: Oui, j'accepte totalement votre commentaire ... mais je dois voir que ces choses sont possibles ou de ne pas exécuter une demande avant l'arrêt.


@deepu: Veuillez consulter ce Discussion . C'est vb, mais vous pouvez peut-être l'adapter.


@MPelletier: J'ai vérifié la discussion que vous avez mentionnée qu'il utilise les méthodes de crochet pour remplacer l'arrêt, mais cela est possible sur les formulaires.Je utilisez le service de fenêtre.Je n'ai aucune idée de la façon de l'utiliser en service ...


@deepu: Si vous voulez avoir l'air plus dans le traqueur d'événements Shotdown, il peut s'agir de personnalisé . La chose la plus simple que je puisse voir est l'utilisation de cela, puis sur Red RetingArt disposer d'un programme exécuté qui envoie les dernières journaux à votre DB.


@MPelletier: Mon intention est de mettre à jour la base de données avec le temps et la raison de l'utilisateur, je ne peux donc pas relâcher sur le suivi des événements, c'est-à-dire que je ne peux pas mettre à jour la DB avec des raisons système.



1
votes


0
votes
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);
        }
    }
}

0 commentaires

5
votes

sur Windows Vista SP1 et plus haut, le nouveau service_control_preshutdown est disponible. Malheureusement, il n'est pas encore pris en charge par .NET Framework, mais voici la solution de contournement en utilisant la réflexion. Il suffit d'hériter de votre classe de service à partir de ServiceProShutDownBase code>, de remplacement OnStop code> et d'appeler périodiquement requierAdditionaltime () code>. Notez que CansShutdown code> doit être défini sur false code>. xxx pré>

Voici l'exemple d'utilisation: p>

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


0 commentaires