Comment remplacer un exe/DLL en cours d'utilisation — Restart Manager et le problème du « fichier en cours d'utilisation » dans les mises à jour automatiques

· · Restart Manager, Mise à jour automatique, Installateur, MSI, Windows API, C#, P/Invoke, Distribution

« Nous annonçons par haut-parleur, à chaque mise à jour, que tout le monde doit fermer l’application en cliquant sur la croix. » « J’avais programmé une tâche nocturne pour remplacer un exe sur le serveur de fichiers, mais le matin, je découvrais un échec avec “le processus ne peut pas accéder au fichier”. » — Ce problème de « fichier en cours d’utilisation » revient très souvent dans les consultations sur la distribution et la mise à jour d’applications métier. Le « redémarrage nécessaire » que les installateurs affichent en dernier recours a la même origine : dans tous les cas, la cause est qu’on ne peut pas remplacer un fichier que quelqu’un tient encore ouvert.

Ce qui rend ce problème délicat, c’est qu’il ne se produit que « de temps en temps ». Sur un poste de développement, on ferme soi-même l’application avant de la mettre à jour, donc le problème ne se reproduit pas ; mais dans un environnement partagé en production, il suffit qu’une seule personne soit rentrée chez elle en laissant l’application ouverte pour que toute la mise à jour nocturne échoue. Et le message d’erreur ne dit jamais qui tient le fichier.

En réalité, Windows dispose d’un mécanisme standard de l’OS conçu précisément pour ce problème : l’API Restart Manager, qui énumère « qui tient le fichier », ferme ces applications en douceur, et va jusqu’à les redémarrer après la mise à jour. De plus, en exploitant la propriété selon laquelle « un exe en cours d’exécution peut être renommé », on peut aussi concevoir une mise à jour qui bascule vers la nouvelle version dès le prochain démarrage, sans jamais arrêter le processus. Cet article s’adresse aux développeurs confrontés au message « le fichier est en cours d’utilisation » dans la mise à jour automatique ou l’installateur d’une application métier, et couvre, avec l’appui de la documentation officielle et du code de diagnostic en C#, le mécanisme du verrouillage, l’utilisation de Restart Manager, les bonnes pratiques côté application, et les motifs d’implémentation de la mise à jour automatique.

1. Pour commencer, la conclusion

  • Un exe en cours d’exécution ou une DLL chargée ne peut être ni « écrasé » ni « supprimé », mais son « renommage (déplacement) » au sein du même volume est possible. Renommer l’ancien fichier sous un nom provisoire avant de placer le nouveau est la forme de base d’un rename-then-replace, le socle des systèmes de mise à jour maison.
  • Restart Manager est une API standard de l’OS depuis Windows Vista, conçue précisément pour le « problème du fichier en cours d’utilisation ». Lorsqu’on lui enregistre un fichier à mettre à jour, elle énumère les applications et services qui l’occupent, et se charge de les arrêter puis de les redémarrer. Son objectif est de réduire, voire d’éliminer, le besoin de redémarrer l’OS.1
  • Le déroulement de l’API est RmStartSession → RmRegisterResources → RmGetList → RmShutdown → (mise à jour) → RmRestart → RmEndSession. L’arrêt se fait dans l’ordre applications GUI → applications console → services → Explorateur, et le redémarrage dans l’ordre inverse.12
  • RmGetList à lui seul suffit à construire un outil de diagnostic qui révèle « qui tient le fichier ». Cela tient en quelques dizaines de lignes de P/Invoke en C# (voir le code plus loin).3
  • Les MSI (Windows Installer 4.0 et ultérieur) utilisent automatiquement Restart Manager. En ajoutant la boîte de dialogue MsiRMFilesInUse au paquet, on peut proposer à l’utilisateur l’option « fermer automatiquement l’application et redémarrer ».4
  • Il existe aussi des bonnes pratiques côté application. Enregistrer la ligne de commande de redémarrage avec RegisterApplicationRestart, répondre à WM_QUERYENDSESSION (lParam=ENDSESSION_CLOSEAPP), et sauvegarder les données non enregistrées avant de se terminer sur WM_ENDSESSION. Une application qui implémente ces trois points « redémarre exactement comme avant, même si elle a été fermée pour la mise à jour ».56
  • Pour éviter les boucles de redémarrage, une application dont le lancement remonte à moins de 60 secondes n’est pas redémarrée. Par ailleurs, le délai avant l’arrêt forcé par Restart Manager est de 30 secondes pour une application et de 20 secondes pour un service.62
  • Lorsqu’il est vraiment impossible de remplacer le fichier, le dernier recours est MoveFileEx + MOVEFILE_DELAY_UNTIL_REBOOT (réservation du remplacement au redémarrage de l’OS). Cela nécessite des droits d’administrateur, et la réservation est enregistrée dans PendingFileRenameOperations du registre.7

2. Pourquoi ne peut-on pas remplacer un fichier en cours d’utilisation — et pourquoi le renommage, lui, passe

Lorsque Windows exécute un exe ou charge une DLL, l’OS mappe ce fichier en tant que fichier mappé en mémoire (section image). En raison du mécanisme de pagination, le contenu réel du fichier continue d’être référencé pendant l’exécution, si bien que la réécriture et la suppression de son contenu sont bloquées. C’est le comportement bien connu où l’Explorateur répond « le fichier est utilisé par un autre processus » lors d’une copie, où File.Copy renvoie une IOException (violation de partage), et où File.Delete renvoie une UnauthorizedAccessException.

Ce qui compte ici, c’est une particularité de Windows assez peu connue. Même si le « contenu » du fichier est verrouillé, son « nom » sur le répertoire, lui, peut être modifié. Même pour un exe en cours d’exécution, le renommage (déplacement) réussit dès lors qu’il reste dans le même volume. En effet, ce que retient l’image en cours d’exécution, c’est le contenu réel du fichier, pas son chemin.

Cette asymétrie est à l’origine de la forme de base des mises à jour maison : le motif rename-then-replace.

1. Renommer MyApp.exe (en cours d'exécution) en MyApp.exe.old   ← réussit même en cours d'exécution
2. Placer le nouveau MyApp.exe au chemin d'origine                ← réussit car le nom est libéré
3. Le nouveau MyApp.exe est utilisé dès le prochain démarrage
4. Après la fin de l'ancien processus, supprimer .old à un moment donné
   (impossible à supprimer pendant l'exécution, donc à nettoyer lors de la prochaine mise à jour ou au démarrage)

L’intérêt de ce motif est de permettre « la nouvelle version dès le prochain démarrage » sans jamais arrêter le processus, et les frameworks de mise à jour comme l’updater de Chrome ou Squirrel/Velopack reposent eux aussi, fondamentalement, sur cette propriété. Trois points de vigilance : le renommage ne fonctionne que dans le même volume (un déplacement vers un autre volume se traduit par une copie + suppression, et la suppression échoue) ; il faut prévoir dans la conception le nettoyage du fichier .old ; et comme « l’ancien processus actuellement en cours » continue de tourner dans son ancienne version, il existe une période pendant laquelle l’ancien et le nouveau processus coexistent.

Notez que si vous voulez déterminer manuellement « qui tient le fichier » dans le cadre d’une investigation d’incident, le moyen le plus rapide est de rechercher les handles et les DLL chargées avec Process Explorer ou la commande Handle. La marche à suivre est détaillée dans l’article jumeau publié le même jour, « Traquer les blocages et les fuites avec Process Explorer / Handle / VMMap ». Dans cet article, nous utilisons Restart Manager pour faire cela de façon programmatique, c’est-à-dire comme méthode à intégrer directement dans l’updater lui-même.

3. Le fonctionnement de Restart Manager — de RmStartSession à RmRestart

Restart Manager est un ensemble d’API (rstrtmgr.dll) intégré en standard depuis Windows Vista / Windows Server 2008, et son objectif est énoncé clairement dès le début de la documentation : « La principale raison pour laquelle une installation ou une mise à jour exige un redémarrage du système est que le fichier à mettre à jour est utilisé par une application ou un service en cours d’exécution ; Restart Manager réduit ou supprime ce besoin de redémarrage en arrêtant puis en redémarrant les applications et services non critiques. »1 Vous avez sans doute déjà vu, pendant l’installation d’un paquet MSI, la boîte de dialogue listant les processus avec le message « Les applications suivantes utilisent des fichiers… » : les installateurs et mises à jour de grands produits comme Visual Studio ou Office s’appuient eux aussi sur ce mécanisme.

Le déroulement de l’API est linéaire.

Étape Fonction Ce qu’elle fait
1 RmStartSession Démarre une session et obtient un handle ainsi qu’une clé de session (chaîne GUID)
2 RmRegisterResources Enregistre les chemins de fichiers à mettre à jour (les noms de processus et de services sont aussi possibles)
3 RmGetList Énumère les applications et services qui utilisent les ressources enregistrées3
4 RmShutdown Les arrête (en douceur normalement, de force si spécifié)2
5 C’est le moment où l’on remplace le fichier
6 RmRestart Redémarre les applications enregistrées pour le redémarrage
7 RmEndSession Ferme la session

« À quel moment remplacer le fichier » est le point le plus facile à mal comprendre ; voici donc la chronologie. On ne peut remplacer le fichier que pendant ce court instant après le retour de RmShutdown et avant l’appel à RmRestart.

Application en cours d'utilisation(GUI/service)Restart Manager(rstrtmgr.dll)Programme de mise à jourApplication en cours d'utilisation(GUI/service)Restart Manager(rstrtmgr.dll)Programme de mise à jourC'est ici que « qui tient le fichier » est déterminéSi RebootReasons≠0, un redémarrage de l'OS est nécessaire⑤ C'est ici que l'on remplace le fichier← le verrou n'est levé que pendant cette fenêtre① RmStartSessionHandle de session + clé de session② RmRegisterResources(fichiers à mettre à jour)③ RmGetListListe des processus en cours d'utilisation + RebootReasons④ RmShutdownWM_QUERYENDSESSION / WM_ENDSESSION(CTRL_C_EVENT pour une console)Sauvegarde puis finFin de l'arrêt⑥ RmRestartRedémarre les applications enregistrées dans l'ordre inverse⑦ RmEndSession

Voici quelques points de spécification à retenir.

  • Ordre d’arrêt et ordre de redémarrage. L’arrêt se fait dans l’ordre applications GUI → applications console → services → Explorateur, et après la mise à jour, les applications enregistrées sont redémarrées dans l’ordre inverse.1
  • La manière de terminer les applications suit un ordre « du plus courtois au moins courtois ». Les applications GUI reçoivent WM_QUERYENDSESSION/WM_ENDSESSION (lParam=ENDSESSION_CLOSEAPP), et celles qui ne répondent pas reçoivent aussi WM_CLOSE. Les applications console reçoivent CTRL_C_EVENT, et les services sont arrêtés via le SCM. Même lorsque RmForceShutdown est spécifié, une fin en douceur est d’abord tentée, puis les applications qui ne répondent pas sont arrêtées de force au bout de 30 secondes (20 secondes pour les services).52
  • Seules les applications enregistrées via RegisterApplicationRestart peuvent être redémarrées. En spécifiant RmShutdownOnlyRegistered dans RmShutdown, on peut adopter un comportement plus prudent : « n’arrêter que si toutes les applications sont enregistrées pour le redémarrage ».25
  • On ne peut pas arrêter une application au-delà des frontières de session. Un installateur qui s’exécute en tant que service LocalSystem ne peut pas arrêter ni redémarrer une application qui tourne dans une session utilisateur. C’est le point d’achoppement le plus fréquent lorsqu’on conçoit une mise à jour nocturne sans surveillance (la solution concrète est donnée dans la section suivante).2
  • Les services système critiques et les processus critiques sont exclus. Dans ce cas, un verdict indiquant qu’un redémarrage de l’OS est nécessaire (RM_REBOOT_REASON) est renvoyé.13

3.1 Comment franchir les frontières de session

C’est une contrainte que l’on rencontre inévitablement en concevant une mise à jour « lancée la nuit depuis un service ». Depuis LocalSystem (session 0), on ne peut ni arrêter ni redémarrer une application qui tourne dans une session utilisateur connectée.2 La parade ne consiste pas à « tendre le bras depuis la session 0 », mais tient tout entière dans le fait de placer des bras et des jambes à l’intérieur de la session cible. Il existe trois options réalistes.

Méthode Ce qu’elle fait Adaptée / inadaptée
(A) Lancement via le Planificateur de tâches en tant qu’utilisateur cible Enregistrer une tâche de l’agent de mise à jour sous le compte de l’utilisateur cible, en « s’exécutant uniquement quand l’utilisateur est connecté », et la déclencher au signal de mise à jour La plus simple à mettre en œuvre. Adaptée aux postes internes où l’utilisateur connecté est identifiable
(B) Lancer un agent résident à la connexion Faire tourner un petit processus résident dans chaque session utilisateur, qui attend le signal de mise à jour via un dossier partagé ou un événement Fonctionne même si l’utilisateur cible est indéterminé ou en cas de connexions simultanées multiples. Le coût de maintenance du processus résident est à prendre en compte
(C) Faire démarrer un processus dans la session utilisateur depuis un service Le service LocalSystem récupère le jeton de la session cible et l’utilise pour lancer l’updater Le plus flexible, mais aussi le plus lourd en implémentation et en gestion des privilèges

Pour (A), l’enregistrement avec schtasks se fait ainsi. L’essentiel est de mettre l’utilisateur cible dans /RU et d’ajouter /IT pour créer une tâche qui « s’exécute de façon interactive uniquement quand cet utilisateur est connecté ».

:: Enregistre une tâche qui fait tourner l'agent de mise à jour "dans la session de l'utilisateur cible"
:: Adapter la spécification des identifiants (/RP) ou l'écrasement d'une tâche existante (/F) selon la politique de l'environnement
schtasks /create /TN "MyApp Update Agent" /TR "C:\App\Updater.exe /shutdown-and-update" ^
         /SC ONCE /ST 02:00 /RU CORP\taro /IT

:: Depuis le batch nocturne, on peut aussi la lancer immédiatement sans attendre l'heure programmée
schtasks /run /TN "MyApp Update Agent"

Si vous choisissez (C), déterminez l’ID de la session cible avec WTSGetActiveConsoleSessionId ou WTSEnumerateSessions, obtenez le jeton primaire de cet utilisateur avec WTSQueryUserToken, puis lancez l’updater avec CreateProcessAsUser. L’appel à WTSQueryUserToken exige de s’exécuter sous le compte LocalSystem et de détenir le privilège SE_TCB_NAME, et la documentation officielle précise explicitement que c’est réservé aux « services hautement fiables », qu’il faut « veiller à ne pas laisser fuiter le jeton et toujours fermer le handle une fois son usage terminé ».8 Une mauvaise manipulation ouvre une brèche d’élévation de privilèges ; s’il suffit de (A) ou (B), il n’est pas nécessaire de forcer le choix de cette option.

Quelle que soit la méthode retenue, la structure reste la même : c’est le processus qui tourne à l’intérieur de la session qui prend en charge tout, de RmStartSession jusqu’à RmRestart. Le service de la session 0 se contente de « distribuer les fichiers de mise à jour » et de « donner le signal ».

Clarifions aussi la relation avec MSI. Les MSI de Windows Installer 4.0 et ultérieur utilisent automatiquement Restart Manager. Le comportement par défaut est de « ne pas redémarrer l’OS, mais d’arrêter et de redémarrer l’application si possible ». Ce que l’auteur du paquet peut faire, c’est ajouter la boîte de dialogue MsiRMFilesInUse, qui propose à l’utilisateur, en UI complète, l’option « fermer automatiquement l’application et redémarrer » (les anciens Installer se replient sur la boîte de dialogue FilesInUse classique) ; contrôler le comportement via des propriétés comme MSIRESTARTMANAGERCONTROL ; et, depuis une action personnalisée, appeler RmJoinSession via la propriété MsiRestartManagerSessionKey pour enregistrer des ressources supplémentaires (cette action personnalisée doit être placée avant l’action InstallValidate, où la détection des fichiers en cours d’utilisation a lieu). En installation silencieuse, Restart Manager est toujours utilisé et l’application est automatiquement fermée.4

Autrement dit, si vous distribuez via MSI, vous n’avez presque jamais besoin d’écrire vous-même du « code qui appelle Restart Manager » : l’essentiel consiste à mettre en place les bonnes pratiques côté application (voir plus loin). Ce n’est que si vous écrivez un updater maison que l’appel direct de cette API prend tout son sens. Le choix du mode de distribution lui-même est traité dans « Comment choisir un mode de distribution d’application Windows - MSI/MSIX/ClickOnce/xcopy/mise à jour maison ».

4. Faire apparaître « qui tient le fichier » en C# — le code de diagnostic RmGetList

Au sein de Restart Manager, RmGetList est utile même pour ceux qui n’écrivent pas de code de mise à jour. Comme l’API permet d’obtenir « la liste des processus et services qui utilisent ce fichier », on peut transformer le message d’erreur de l’updater, qui passe de « le fichier est en cours d’utilisation » à « c’est MyApp.exe (PID 4132) sur le poste comptabilité qui le tient».

4.1 Le squelette — seulement quatre appels

En dehors des déclarations P/Invoke et des définitions de structures, le corps du traitement se résume à ceci. Gardez cette forme en tête avant de lire la version complète qui suit.

// 【Squelette】Les définitions de structures et les déclarations P/Invoke figurent dans la version complète en 4.3
var sessionKey = new StringBuilder(CCH_RM_SESSION_KEY + 1);

// ① Démarrer la session
int rc = RmStartSession(out uint session, 0, sessionKey);
if (rc != 0) throw new Win32Exception(rc);
try
{
    // ② Enregistrer les fichiers à examiner (doivent être des chemins complets)
    var fullPaths = Array.ConvertAll(args, Path.GetFullPath);
    rc = RmRegisterResources(session, (uint)fullPaths.Length, fullPaths, 0, null, 0, null);
    if (rc != 0) throw new Win32Exception(rc);

    // ③ Énumérer les processus/services en cours d'utilisation
    //    Si le buffer est insuffisant, ERROR_MORE_DATA et le nombre nécessaire sont renvoyés : on réalloue et on réessaie
    uint count = 0;
    RM_PROCESS_INFO[] apps = null;
    while (true)
    {
        rc = RmGetList(session, out uint needed, ref count, apps, out uint reasons);
        if (rc == 0) break;
        if (rc != ERROR_MORE_DATA) throw new Win32Exception(rc);
        count = needed;
        apps = new RM_PROCESS_INFO[needed];
    }

    // apps[0] à apps[count-1] contiennent « qui tient le fichier »
}
finally
{
    RmEndSession(session);   // ④ Toujours fermer
}

4.2 Ce qui est renvoyé — la forme de la sortie et RM_APP_TYPE

Si vous exécutez la version complète (4.3) comme WhoLocks.exe C:\App\MyApp.exe, la sortie prend la forme suivante. Les valeurs sont des exemples à but pédagogique ; le contenu réel varie selon l’environnement.

Processus/services en cours d'utilisation : 2 (RebootReasons=0)
  PID=4132   Type=1 Redémarrable=True  Session=2 Nom=MyApp Service=
  PID=6284   Type=3 Redémarrable=False Session=0 Nom=MyAppAgent Service=MyAppAgent

Trois clés de lecture. Le Type (RM_APP_TYPE) détermine la manière de terminer l’application, si Redémarrable (bRestartable) vaut False, elle ne redémarre pas automatiquement après la mise à jour, et si Session (TSSessionId) diffère de la vôtre, on retombe sur la contrainte du chapitre 3 selon laquelle on ne peut pas franchir les frontières de session.

Voici les valeurs numériques du type et leur signification.9

Valeur Nom Signification
0 RmUnknownApp Application qui ne peut être classée dans aucune autre catégorie. Ne peut être arrêtée que de force
1 RmMainWindow Application Windows fonctionnant comme un processus unique et possédant une fenêtre de premier niveau
2 RmOtherWindow Application Windows qui n’est ni un processus unique ni dotée d’une fenêtre de premier niveau
3 RmService Service Windows
4 RmExplorer Explorateur Windows
5 RmConsole Application console unique
1000 RmCritical Ne peut pas être arrêtée, donc un redémarrage de l’OS est nécessaire pour terminer l’installation. Processus critique, droits insuffisants, ou processus ayant lui-même démarré Restart Manager

En pratique, dès que 0 ou 1000 apparaît, on peut conclure qu’« une mise à jour en douceur est impossible ». Pour 1 ou 5, l’application peut être fermée avec les bonnes pratiques du chapitre 3 (WM_QUERYENDSESSION / CTRL_C_EVENT), et pour 3, elle s’arrête via le gestionnaire de contrôle des services.

4.3 Version complète

Voici à quoi cela ressemble en P/Invoke depuis C# (fonctionne aussi bien sous .NET Framework 4.8 que sous .NET 8).

// WhoLocks.cs ── énumère « qui tient » un fichier donné à l'aide de Restart Manager
// Utilisation : WhoLocks.exe C:\App\MyApp.exe C:\App\MyLib.dll
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

internal static class Program
{
    private const int CCH_RM_SESSION_KEY = 32;      // La clé de session est une chaîne GUID (32 caractères + terminateur)
    private const int CCH_RM_MAX_APP_NAME = 255;
    private const int CCH_RM_MAX_SVC_NAME = 63;
    private const int ERROR_MORE_DATA = 234;

    [StructLayout(LayoutKind.Sequential)]
    private struct RM_UNIQUE_PROCESS
    {
        public uint dwProcessId;
        public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
    }

    // RM_APP_TYPE : 1=application GUI, 2=autre fenêtre, 3=service, 4=Explorer, 5=console, 1000=critique

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct RM_PROCESS_INFO
    {
        public RM_UNIQUE_PROCESS Process;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
        public string strAppName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
        public string strServiceShortName;
        public int ApplicationType;
        public uint AppStatus;
        public uint TSSessionId;
        [MarshalAs(UnmanagedType.Bool)]
        public bool bRestartable;
    }

    // L'API Restart Manager renvoie le code d'erreur Win32 dans sa valeur de retour, pas via GetLastError
    [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
    private static extern int RmStartSession(
        out uint pSessionHandle, int dwSessionFlags, StringBuilder strSessionKey);

    [DllImport("rstrtmgr.dll")]
    private static extern int RmEndSession(uint dwSessionHandle);

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
    private static extern int RmRegisterResources(uint dwSessionHandle,
        uint nFiles, string[] rgsFileNames,
        uint nApplications, RM_UNIQUE_PROCESS[] rgApplications,
        uint nServices, string[] rgsServiceNames);

    [DllImport("rstrtmgr.dll")]
    private static extern int RmGetList(uint dwSessionHandle,
        out uint pnProcInfoNeeded, ref uint pnProcInfo,
        [In, Out] RM_PROCESS_INFO[] rgAffectedApps, out uint lpdwRebootReasons);

    private static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.Error.WriteLine("Utilisation : WhoLocks <chemin_du_fichier> ...");
            Environment.Exit(2);
        }

        var sessionKey = new StringBuilder(CCH_RM_SESSION_KEY + 1);
        int rc = RmStartSession(out uint session, 0, sessionKey);
        if (rc != 0) throw new Win32Exception(rc, $"Échec de RmStartSession (rc={rc})");
        try
        {
            // Selon le contrat de l'API, les noms de fichiers enregistrés doivent être des chemins complets.
            // On normalise avant l'enregistrement pour que cela fonctionne même si des chemins relatifs sont passés
            var fullPaths = Array.ConvertAll(args, Path.GetFullPath);
            rc = RmRegisterResources(session, (uint)fullPaths.Length, fullPaths, 0, null, 0, null);
            if (rc != 0) throw new Win32Exception(rc, $"Échec de RmRegisterResources (rc={rc})");

            // On interroge le nombre nécessaire → on alloue le tableau → on récupère. Si des processus
            // apparaissent entre les deux appels, ERROR_MORE_DATA peut être renvoyé à nouveau, d'où la boucle de réessai
            uint count = 0;
            uint rebootReasons;
            RM_PROCESS_INFO[] apps = null;
            while (true)
            {
                rc = RmGetList(session, out uint needed, ref count, apps, out rebootReasons);
                if (rc == 0) break;
                if (rc != ERROR_MORE_DATA) throw new Win32Exception(rc, $"Échec de RmGetList (rc={rc})");
                count = needed;
                apps = new RM_PROCESS_INFO[needed];
            }

            Console.WriteLine($"Processus/services utilisant le fichier : {count} (RebootReasons={rebootReasons})");
            for (int i = 0; i < count; i++)
            {
                RM_PROCESS_INFO a = apps[i];
                Console.WriteLine(
                    $"  PID={a.Process.dwProcessId,-6} Type={a.ApplicationType} " +
                    $"Redémarrable={a.bRestartable} Session={a.TSSessionId} " +
                    $"Nom={a.strAppName} Service={a.strServiceShortName}");
            }
        }
        finally
        {
            RmEndSession(session);   // Toujours libérer la session dans le bloc finally
        }
    }
}

Trois points essentiels. Premièrement, la valeur de retour de l’API Restart Manager est directement le code d’erreur Win32 ; on n’utilise pas GetLastError (il n’est pas nécessaire de mettre SetLastError = true sur DllImport). Deuxièmement, RmGetList suit la convention d’appel « si le buffer est insuffisant, renvoyer ERROR_MORE_DATA (234) et le nombre nécessaire », d’où l’écriture en boucle interroger → allouer → récupérer.3 Troisièmement, si lpdwRebootReasons est différent de 0 (RmRebootReasonNone), cela signifie qu’« arrêter l’application ne suffit pas, un redémarrage de l’OS est nécessaire ».3 Pour la façon de marshaler les chaînes de structures avec ByValTStr, ainsi que pour les principes généraux d’écriture sécurisée de ce type de P/Invoke, voir « Appeler les API Win32 en toute sécurité depuis C# — guide pratique du P/Invoke ».

En ajoutant à ce code les deux P/Invoke RmShutdown et RmRestart, on obtient le squelette d’un updater maison « énumérer → arrêter en douceur → remplacer → redémarrer ». Attention toutefois : RmShutdown ne doit être appelé que par le processus qui a lui-même exécuté RmStartSession, et ne doit jamais être appelé depuis une action personnalisée MSI (car c’est le MSI lui-même qui gère la session).4

5. Les bonnes pratiques côté application — les trois règles pour « se fermer proprement et redémarrer exactement comme avant »

Ni Restart Manager ni MSI ne tuent un processus sans ménagement avec terminate (sauf si l’arrêt forcé est explicitement spécifié). Si l’application ne répond pas, on finit malgré tout par déranger l’utilisateur avec la boîte de dialogue « fichier en cours d’utilisation ». Pour rendre une application métier « robuste face aux mises à jour », il faut implémenter côté application les trois règles suivantes.5

(1) Enregistrer le redémarrage avec RegisterApplicationRestart. À appeler une fois, juste après le lancement. Restart Manager ne peut redémarrer que les applications enregistrées, et c’est le seul « moyen de communiquer à l’OS la ligne de commande à utiliser au redémarrage ». Les principales spécifications sont : ne pas inclure le nom de l’exe dans la ligne de commande (l’OS l’ajoute lui-même), une longueur maximale de RESTART_MAX_CMD_LINE, et l’absence de redémarrage pendant les 60 premières secondes suivant le lancement, pour éviter les boucles de redémarrage. Si le redémarrage en cas de plantage ou de blocage n’est pas souhaité, on peut spécifier RESTART_NO_CRASH | RESTART_NO_HANG pour limiter le redémarrage aux seules mises à jour.6

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern int RegisterApplicationRestart(string commandLine, int flags);

// À enregistrer au lancement. Le drapeau "/restored" permet de brancher vers un traitement de restauration après redémarrage
// RESTART_NO_CRASH(1) | RESTART_NO_HANG(2) = ne redémarrer qu'en cas de fin provoquée par une mise à jour
RegisterApplicationRestart("/restored", 1 | 2);

(2) Répondre à WM_QUERYENDSESSION (lParam=ENDSESSION_CLOSEAPP). Avant de terminer l’application, Restart Manager envoie ce message pour demander « puis-je te fermer ? ». À ce stade, on ne se termine pas encore ; on renvoie TRUE si l’on est prêt (car d’autres applications pourraient, elles, ne pas l’être). Renvoyer FALSE permet d’annuler l’arrêt, mais comme l’arrêt forcé finit de toute façon par s’imposer, il vaut mieux éviter une conception qui repose sur FALSE. C’est aussi la dernière occasion de rappeler RegisterApplicationRestart au moment de la mise à jour. En intégrant dans les arguments de la ligne de commande l’état nécessaire à la restauration (comme l’identifiant du formulaire ouvert), on peut revenir à « l’écran d’origine » après le redémarrage.56

(3) Sauvegarder les données non enregistrées puis se terminer sur WM_ENDSESSION. L’ordre effectif de terminaison arrive via WM_ENDSESSION (wParam=TRUE, lParam=ENDSESSION_CLOSEAPP). Dans le délai imparti (30 secondes pour une application en cas d’arrêt forcé), il faut terminer la sauvegarde automatique des données non enregistrées, la sérialisation de l’état de travail et la fermeture des connexions avant de se terminer. Les recommandations officielles préconisent avant tout de « sauvegarder régulièrement les données utilisateur ». Pour une application console, c’est CTRL_C_EVENT qui arrive à la place des messages WM_, donc on fait la même chose via SetConsoleCtrlHandler (ou Console.CancelKeyPress en C#).52

En WPF/WinForms, Application.SessionEnding / Form.FormClosing (CloseReason.WindowsShutDown) constituent le point d’entrée, mais pour distinguer précisément le drapeau ENDSESSION_CLOSEAPP (la différence entre un redémarrage de l’OS et « seule l’application se ferme, pour redémarrer ensuite »), il faut accrocher la procédure de fenêtre. Par ailleurs, pour éviter que le processus relancé après redémarrage n’entre en conflit avec « sa propre instance précédente en train de se terminer », il est aussi recommandé de revoir en même temps la conception du Mutex empêchant les instances multiples (voir « Empêcher les instances multiples d’une application Windows »).

Une fois ces trois règles réunies, l’expérience de mise à jour MSI passe de « annoncer par haut-parleur à tout le monde de fermer l’application » à « l’application se ferme automatiquement lorsque la mise à jour démarre, et revient automatiquement à l’écran d’origine après la mise à jour». C’est exactement le même mécanisme que le comportement d’Office qui « rouvre le document après un redémarrage ».

6. Motifs d’implémentation de la mise à jour automatique — processus séparé, rename-then-replace, remplacement au redémarrage

Passons en revue les motifs de conception pour construire une mise à jour automatique maison. Une seule prémisse fondamentale : on ne peut pas s’écraser soi-même (l’exe en cours d’exécution), donc le traitement de mise à jour doit, à un moment ou un autre, s’échapper vers « un autre processus » ou « un autre nom ».

Motif A : updater en processus séparé + attente puis remplacement. Lorsque l’application principale détecte une mise à jour, elle lance l’exe de l’updater (ou une copie temporaire de celui-ci) puis se termine elle-même. L’updater attend la fin du processus principal (Process.WaitForExit ou l’attente de la libération d’un Mutex), remplace le fichier, puis redémarre l’application principale. C’est simple et fiable, mais pour parer aux cas où « l’application principale met du temps à se terminer » ou « il y a de nombreux processus à attendre dans une configuration multi-processus », il est efficace de combiner cela avec RmGetList (section précédente) pour vérifier les occupants, puis RmShutdown pour les fermer.

Motif B : rename-then-replace (mise à jour sans arrêt). Comme vu à la section 2, on renomme l’exe/la DLL en cours d’exécution et on place la nouvelle version, pour obtenir « la nouvelle version dès le prochain démarrage ». L’avantage est de ne pas interrompre le travail de l’utilisateur, ce qui convient bien aux applications métier résidentes ou à fonctionnement continu. Des frameworks de mise à jour .NET comme Squirrel ou Velopack exploitent eux aussi cette propriété, sous la forme d’un dossier par version plus un remplacement de l’exe d’entrée, en fournissant tout le cadre pour « appliquer la mise à jour en coulisse, nouvelle version dès le prochain démarrage ». Pour une implémentation maison, cela se résume à : télécharger → vérifier → décompresser → renommer l’ancien en réserve → placer le nouveau → nettoyer le fichier de réserve au prochain démarrage.

Motif C : MoveFileEx + MOVEFILE_DELAY_UNTIL_REBOOT (remplacement au redémarrage). Pour des cas comme un processus hôte de service ou une extension shell, qu’« on ne peut vraiment pas arrêter et où même le renommage n’offre pas d’échappatoire », le dernier recours est la réservation d’un remplacement au redémarrage de l’OS. Le contenu de la réservation est enregistré dans le registre, sous HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations, et exécuté dans l’ordre d’enregistrement dès les premières étapes du prochain démarrage (juste après AUTOCHK, avant la création du fichier d’échange). Il faut utiliser cela en comprenant que l’appel n’est possible que par le groupe Administrateurs ou LocalSystem, qu’il ne peut pas être combiné avec MOVEFILE_COPY_ALLOWED (donc limité au même volume), que si le fichier de destination existe déjà, il faut combiner avec MOVEFILE_REPLACE_EXISTING (sans quoi la réservation elle-même peut réussir mais le remplacement au redémarrage n’aura pas lieu), et que le succès de la fonction correspond au « succès de la réservation », sans indication sur le résultat réel du remplacement. Lorsqu’un installateur annonce « un redémarrage est nécessaire », c’est précisément cela qui est empilé en coulisse.7

Deux précautions sont communes à tous ces motifs. La première concerne la mise à jour des services : un service Windows ne peut pas se remplacer lui-même, il est donc de règle de séparer un processus de mise à jour (ou un service dédié à la mise à jour) qui effectue « Stop via le SCM → remplacement → Start » (les bases de la conception des services sont couvertes dans « Créer et exploiter un service Windows »). La seconde concerne la vérification du fichier de mise à jour : concevoir soi-même un mécanisme de remplacement revient aussi à construire soi-même « un mécanisme qui distribue et exécute un exe arbitraire », et omettre la vérification de signature ou la protection du canal de téléchargement transforme le mécanisme de mise à jour lui-même en vecteur d’attaque. Ce sujet est traité séparément dans « Concevoir la sécurité de la mise à jour automatique - pourquoi HTTPS seul ne suffit pas ».

7. Tableau de décision pour le mode de mise à jour

Mode Situation adaptée Points de vigilance
MSI + tolérer le redémarrage de l’OS Fréquence de distribution faible (quelques fois par an), possibilité de prévoir une fenêtre de maintenance de nuit ou le week-end L’implémentation la plus simple. Mais cela signifie continuer à montrer « redémarrage nécessaire » à l’utilisateur. Repose sur le remplacement au redémarrage (PendingFileRenameOperations)7
MSI + intégration Restart Manager (MsiRMFilesInUse + les trois règles côté application) Améliorer l’expérience de mise à jour tout en conservant la distribution MSI. Application standard de distribution interne Le côté installateur est presque automatique. L’efficacité dépend de l’implémentation de RegisterApplicationRestart/WM_QUERYENDSESSION côté application45
Updater maison (processus séparé + rename-then-replace, y compris Squirrel/Velopack) Fréquence de mise à jour élevée (hebdomadaire ou plus), utilisateurs sans droits d’administrateur, ne pas vouloir interrompre le travail Il faut être prêt à développer soi-même le nettoyage, la vérification de signature et le retour en arrière en cas d’échec. Ajouter une protection « qui tient le fichier » via RmGetList/RmShutdown rend l’ensemble plus robuste3
ClickOnce Client Windows interne où l’on veut simplement « une mise à jour automatique au lancement » Avec un modèle de mise à jour au lancement, le problème du fichier en cours d’utilisation se pose rarement. Voir « Qu’est-ce que ClickOnce » pour les contraintes
Auto-mise à jour côté service (processus de mise à jour séparé) Impossible d’arrêter un service qui tourne 24 h/24 sur un environnement sans surveillance ou un PC d’équipement Le service lui-même ne peut pas se remplacer. Un processus séparé assurant Stop → remplacement → Start est indispensable. Attention aux frontières de session (LocalSystem ne peut pas fermer une application utilisateur)2

En cas d’hésitation, envisagez d’abord la « MSI + intégration Restart Manager». Comme elle s’appuie sur un mécanisme standard de l’OS, l’effort d’implémentation est minimal, et les trois règles côté application restent un atout même si vous migrez plus tard vers un updater maison.

8. Conclusion

  • Un exe/DLL en cours d’utilisation ne peut être ni écrasé ni supprimé, mais son renommage au sein du même volume est possible. Le rename-then-replace, qui exploite cette asymétrie, est la forme de base d’une « mise à jour sans arrêt ».
  • Restart Manager est une API standard de l’OS qui prend en charge l’énumération de « qui tient le fichier » (RmGetList), l’arrêt en douceur (RmShutdown) et jusqu’au redémarrage après la mise à jour (RmRestart), et les MSI de Windows Installer 4.0 et ultérieur l’utilisent automatiquement.
  • Les bonnes pratiques côté application tiennent en trois règles : enregistrer le redémarrage avec RegisterApplicationRestart, renvoyer TRUE à WM_QUERYENDSESSION (ENDSESSION_CLOSEAPP), et sauvegarder les données non enregistrées avant de se terminer sur WM_ENDSESSION. Cela suffit à obtenir une « application qui redémarre exactement comme avant après la mise à jour ».
  • La règle des 60 secondes (pas de redémarrage juste après le lancement), le délai avant l’arrêt forcé (30 secondes pour une application, 20 secondes pour un service) et les frontières de session (LocalSystem ne peut pas fermer une application utilisateur) sont les points d’achoppement les plus fréquents en exploitation réelle.
  • Lorsqu’il est vraiment impossible de remplacer le fichier, on réserve le remplacement au redémarrage de l’OS avec MoveFileEx + MOVEFILE_DELAY_UNTIL_REBOOT. Cela nécessite des droits d’administrateur, se limite au même volume, et le succès de l’appel correspond au succès de la réservation.
  • Concevoir soi-même un mécanisme de mise à jour, c’est concevoir soi-même « un mécanisme qui distribue un exe arbitraire ». Prenez en compte la vérification de signature, la protection du canal et le retour en arrière dans la conception.

Articles connexes

Domaines de conseil associés

合同会社小村ソフト (Komura Software LLC) prend en charge la conception et l’implémentation de mécanismes de mise à jour automatique pour les applications métier (intégration de Restart Manager, updater maison, mise en place de Squirrel/Velopack), l’amélioration d’une exploitation où « tout le monde doit fermer l’application à chaque mise à jour », ainsi que l’investigation et le traitement des problèmes de « fichier en cours d’utilisation » et de « redémarrage nécessaire » sur des installateurs existants.

Références

  1. Microsoft Learn, About Restart Manager. Sur l’objectif de Restart Manager (réduire ou éliminer les redémarrages dus aux fichiers en cours d’utilisation), l’arrêt dans l’ordre applications GUI → applications console → services → Explorateur et le redémarrage dans l’ordre inverse, le fait que l’arrêt au-delà des frontières de session ne soit pas pris en charge, le fait que Windows Installer 4.0 utilise automatiquement Restart Manager, et le fait que les services système critiques ne puissent être arrêtés qu’avec un redémarrage de l’OS.  2 3 4 5

  2. Microsoft Learn, RmShutdown function. Sur le fait que, même avec RmForceShutdown, les applications qui ne répondent pas sont arrêtées de force au bout de 30 secondes (20 secondes pour les services), le fait que RmShutdownOnlyRegistered permette de n’arrêter que si toutes les applications sont enregistrées pour le redémarrage, le fait qu’un service LocalSystem ne puisse pas arrêter ni redémarrer une application d’une autre session utilisateur, et sur des valeurs de retour comme ERROR_FAIL_NOACTION_REBOOT.  2 3 4 5 6 7 8 9

  3. Microsoft Learn, RmGetList function. Sur le fait que la fonction renvoie dans un tableau RM_PROCESS_INFO les applications et services qui utilisent les ressources enregistrées, la convention d’appel qui renvoie ERROR_MORE_DATA (234) et le nombre nécessaire en cas de buffer insuffisant, le fait que lpdwRebootReasons renvoie la raison (RM_REBOOT_REASON) pour laquelle un redémarrage de l’OS est nécessaire, et le fait que ce soit fourni par rstrtmgr.dll depuis Windows Vista.  2 3 4 5 6

  4. Microsoft Learn, Using Windows Installer with Restart Manager. Sur le fait que Windows Installer 4.0 utilise automatiquement Restart Manager et privilégie par défaut l’arrêt et le redémarrage de l’application plutôt que le redémarrage de l’OS, le fait que la boîte de dialogue MsiRMFilesInUse permette de proposer l’option de fermeture et de redémarrage automatiques en UI complète (avec repli sur FilesInUse dans les environnements plus anciens), le fait qu’en installation silencieuse Restart Manager soit toujours utilisé et l’application toujours fermée, le fait qu’une action personnalisée doive être placée avant InstallValidate et appeler RmJoinSession via MsiRestartManagerSessionKey plutôt que d’appeler directement RmShutdown ou équivalent, et sur des propriétés de contrôle comme MSIRESTARTMANAGERCONTROL.  2 3 4

  5. Microsoft Learn, Guidelines for Applications (Restart Manager). Sur le fait que WM_QUERYENDSESSION (lParam=ENDSESSION_CLOSEAPP) soit envoyé aux applications GUI, qui renvoient TRUE si elles sont prêtes sans se terminer à ce stade, le fait que la terminaison effective se fasse via WM_ENDSESSION, le fait que WM_CLOSE soit aussi envoyé aux applications qui ne répondent pas, le fait que CTRL_C_EVENT soit envoyé aux applications console, et le fait que l’enregistrement via RegisterApplicationRestart soit indispensable pour le redémarrage.  2 3 4 5 6 7

  6. Microsoft Learn, RegisterApplicationRestart function. Sur l’enregistrement de la ligne de commande de redémarrage (sans le nom de l’exe, longueur maximale RESTART_MAX_CMD_LINE), les drapeaux RESTART_NO_CRASH/NO_HANG/NO_PATCH/NO_REBOOT, le fait que le redémarrage dû à une mise à jour soit automatique alors que celui dû à un plantage ou un blocage nécessite le consentement de l’utilisateur, le fait qu’une application ne soit pas redémarrée si moins de 60 secondes se sont écoulées depuis son lancement (pour éviter les boucles), et le fait que la dernière occasion d’enregistrement lors d’une mise à jour se situe pendant le traitement de WM_QUERYENDSESSION.  2 3 4

  7. Microsoft Learn, MoveFileExW function. Sur le fait que MOVEFILE_DELAY_UNTIL_REBOOT écrive sa réservation dans PendingFileRenameOperations (REG_MULTI_SZ) sous HKLM\SYSTEM\CurrentControlSet\Control\Session Manager, exécutée dans l’ordre d’enregistrement après AUTOCHK et avant la création du fichier d’échange, le fait que les droits du groupe Administrateurs ou de LocalSystem soient requis, l’impossibilité de combiner avec MOVEFILE_COPY_ALLOWED, la nécessité de spécifier MOVEFILE_REPLACE_EXISTING si le fichier de destination existe déjà, le fait que la valeur de retour indique le succès de la réservation et non celui du déplacement réel, et le fait que passer NULL à lpNewFileName provoque une suppression au redémarrage.  2 3

  8. Microsoft Learn, WTSQueryUserToken function (wtsapi32.h). Sur le fait que ce soit une fonction qui obtient le jeton d’accès primaire d’un utilisateur connecté à partir d’un ID de session, le fait que son appel nécessite de s’exécuter dans le contexte du compte LocalSystem et de détenir le privilège SE_TCB_NAME, le fait qu’elle soit destinée aux services hautement fiables, avec la précaution de ne pas laisser fuiter le jeton et de toujours fermer le handle obtenu avec CloseHandle, et le fait que WTSEnumerateSessions puisse être utilisé pour énumérer les ID de session. 

  9. Microsoft Learn, RM_APP_TYPE enumeration (restartmanager.h). Sur les types d’application définis pour la structure RM_PROCESS_INFO : RmUnknownApp (0, non classable ailleurs, ne peut être arrêtée que de force), RmMainWindow (1, application Windows en processus unique possédant une fenêtre de premier niveau), RmOtherWindow (2, application Windows qui n’est ni en processus unique ni dotée d’une fenêtre de premier niveau), RmService (3, service Windows), RmExplorer (4, Explorateur Windows), RmConsole (5, application console unique), et RmCritical (1000, processus impossible à arrêter, nécessitant un redémarrage système pour terminer l’installation, pour l’une des raisons suivantes : processus critique, droits insuffisants, ou processus de l’installateur ayant lui-même démarré Restart Manager). 

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Pourquoi un exe en cours d'exécution ou une DLL chargée ne peuvent-ils pas être écrasés, alors qu'ils peuvent être renommés ?
Windows retient les exe en cours d'exécution et les DLL chargées sous forme de mappage mémoire (section image), si bien que la réécriture du contenu du fichier et sa suppression provoquent une erreur (violation de partage ou accès refusé). En revanche, le « nom » sur le répertoire est indépendant du contenu du fichier, si bien que le renommage (déplacement) au sein du même volume réussit même pendant l'exécution. C'est cette asymétrie qu'exploite le motif rename-then-replace : on renomme l'ancien exe sous un nom provisoire, puis on place le nouvel exe sous le nom d'origine, de sorte que la nouvelle version soit utilisée dès le prochain démarrage. Les mises à jour automatiques de Chrome ou des systèmes de type Squirrel/Velopack reposent elles aussi, fondamentalement, sur cette propriété.
Que fait l'API Restart Manager ?
C'est une API standard de Windows (à partir de Windows Vista) qui, lorsqu'on lui enregistre un fichier à mettre à jour, énumère « la liste des applications et services qui utilisent actuellement ce fichier », peut les arrêter si possible, et va jusqu'à les redémarrer après la mise à jour. Le déroulement est le suivant : RmStartSession crée une session, RmRegisterResources enregistre les fichiers cibles, RmGetList énumère les processus qui les occupent, RmShutdown les arrête, RmRestart les redémarre, et enfin RmEndSession ferme la session. L'arrêt se fait dans l'ordre applications GUI → applications console → services → Explorateur, et le redémarrage se fait dans l'ordre inverse. C'est un mécanisme destiné à réduire les demandes de « redémarrage nécessaire », et les MSI de Windows Installer 4.0 et ultérieur l'utilisent automatiquement.
Que se passe-t-il si l'on appelle RegisterApplicationRestart au préalable ?
L'application peut enregistrer auprès de l'OS sa propre ligne de commande de redémarrage, et après un arrêt provoqué par une mise à jour, Restart Manager redémarre automatiquement l'application avec cette ligne de commande (Restart Manager ne peut redémarrer que les applications enregistrées). L'application peut aussi être redémarrée en cas de plantage ou de blocage, mais dans ce cas le consentement de l'utilisateur est requis, alors que le redémarrage dû à une mise à jour se fait automatiquement. Attention : pour éviter les boucles, l'application n'est pas redémarrée si moins de 60 secondes se sont écoulées depuis son lancement, et la ligne de commande ne doit pas inclure le nom de l'exe. La bonne pratique consiste à réenregistrer l'état nécessaire à la restauration (comme les fichiers ouverts) sous forme d'arguments de ligne de commande.
Dans quels cas utilise-t-on MOVEFILE_DELAY_UNTIL_REBOOT de MoveFileEx ?
C'est le dernier recours lorsqu'on ne peut absolument pas arrêter le processus et que rename-then-replace n'est pas non plus utilisable : il réserve un déplacement ou une suppression de fichier au prochain redémarrage de l'OS. Cette réservation est écrite dans le registre, dans PendingFileRenameOperations (HKLM\SYSTEM\CurrentControlSet\Control\Session Manager), et elle est exécutée dans l'ordre d'enregistrement dès les premières étapes du prochain démarrage (après AUTOCHK, avant la création du fichier d'échange). L'appel nécessite les droits du groupe Administrateurs ou de LocalSystem, et comme il ne peut pas être combiné avec MOVEFILE_COPY_ALLOWED, il ne peut pas servir à déplacer un fichier vers un autre volume. Il faut aussi garder à l'esprit que le succès de la fonction correspond au succès de la réservation, pas au succès réel du remplacement.
Comment traiter en douceur les « fichiers en cours d'utilisation » avec un installateur MSI ?
À partir de Windows Installer 4.0, l'installateur se coordonne automatiquement avec Restart Manager et privilégie par défaut l'arrêt et le redémarrage de l'application plutôt que le redémarrage de l'OS. En ajoutant la boîte de dialogue MsiRMFilesInUse au paquet, l'utilisateur se voit proposer, lors d'une installation en UI complète, l'option « fermer automatiquement les applications et redémarrer ». Si l'installation s'exécute sous une version plus ancienne de Windows Installer, elle se replie sur la boîte de dialogue FilesInUse classique ; il est donc de bonne pratique d'inclure les deux. Le comportement peut être contrôlé par des propriétés comme MSIRESTARTMANAGERCONTROL, et en installation silencieuse, Restart Manager est toujours utilisé et l'application est fermée automatiquement. Si l'application implémente RegisterApplicationRestart et répond à WM_QUERYENDSESSION, on obtient une mise à jour « quasi sans interruption » où l'application redémarre exactement comme avant après la mise à jour.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog