How to Replace an exe or DLL That Is In Use — Restart Manager and the "File In Use" Problem in Auto-Update
· Go Komura · Restart Manager, Auto-Update, Installer, MSI, Windows API, C#, P/Invoke, Deployment
“Every time we update, we make an announcement over the building’s PA system asking everyone to close the app with the X button.” “We set up a batch job to replace the exe on the file server overnight, and in the morning it had failed with ‘the process cannot access the file’.” In consultations about distributing and updating business applications, this “file in use” problem comes up constantly. The “a restart is required” message an installer shows at the end has the same root: everything traces back to not being able to replace a file that someone is holding.
What makes it awkward is that the problem only happens sometimes. On a development machine you close the app yourself before updating, so it never reproduces; in a shared production environment, one person leaving the app open when they went home wipes out the entire overnight update. And the error message tells you nothing about who is holding it.
Windows does in fact have a standard OS mechanism for exactly this problem: the Restart Manager API, which enumerates “who is holding the file”, shuts them down politely, and even restarts them after the update. And by using the property that “a running exe can be renamed”, you can also design an update that switches to the new version from the next launch without stopping any process. This article is for developers wrestling with “the file is in use” in business application auto-updaters and installers, and it covers the mechanics of the lock, how to use Restart Manager, the etiquette the application itself should follow, and implementation patterns for auto-update — backed by the official documentation and with C# diagnostic code.
1. The Bottom Line First
- A running exe or a loaded DLL cannot be overwritten or deleted, but it can be renamed (moved) within the same volume. Renaming the old file to a retirement name and then putting the new file in place — rename-then-replace — is the basic form of a custom updater.
- Restart Manager is a standard OS API from Windows Vista onwards, and it exists for the “file in use” problem. Register the files you intend to update and it enumerates the applications and services holding them, taking care of shutdown and restart. Its purpose is to reduce or eliminate OS restarts.1
- The API flow is RmStartSession → RmRegisterResources → RmGetList → RmShutdown → (update) → RmRestart → RmEndSession. Shutdown proceeds in the order GUI apps → console apps → services → Explorer, and restart happens in reverse order.12
- RmGetList on its own is enough to build a diagnostic tool that reports “who is holding this”. A few dozen lines of P/Invoke from C# (see the code below).3
- MSI (Windows Installer 4.0 and later) uses Restart Manager automatically. Adding the MsiRMFilesInUse dialog to your package lets you offer users the option to “automatically close and attempt to restart applications”.4
- The application has its own etiquette to follow. Register a restart command line with RegisterApplicationRestart, respond to WM_QUERYENDSESSION (lParam=ENDSESSION_CLOSEAPP), and save unsaved data in WM_ENDSESSION before exiting. An app that implements these three things “gets closed for an update, and comes back up as it was afterwards”.56
- To prevent restart loops, an app that started less than 60 seconds ago is not restarted. Also, the timeout for a forced shutdown by Restart Manager is 30 seconds for applications and 20 seconds for services.62
- When replacement is truly impossible, the last resort is MoveFileEx + MOVEFILE_DELAY_UNTIL_REBOOT (scheduling the replacement for the next OS restart). It requires administrator privileges, and the reservation is recorded in the registry under PendingFileRenameOperations.7
2. Why a File In Use Can’t Be Replaced — And the Loophole That “Rename Still Works”
When Windows runs an exe, or loads a DLL, the OS maps that file as a memory-mapped file (an image section). Because the file’s contents continue to be referenced while it is running, as a consequence of how paging works, modification of the contents and deletion are blocked. That is the behavior you see as “another process is using it” when you try to copy in Explorer, an IOException (sharing violation) from File.Copy, or an UnauthorizedAccessException from File.Delete.
Here is the little-known property of Windows that matters. Even when the file’s “contents” are locked, the “name” in the directory can be changed. Even for a running exe, a rename (move) succeeds provided it stays within the same volume — because what the executing image holds is the file itself, not the path name.
From this asymmetry follows the basic form of custom updating, the rename-then-replace pattern.
1. Rename MyApp.exe (running) to MyApp.exe.old <- succeeds even while running
2. Place the new MyApp.exe at the original path <- succeeds because the name is now free
3. From the next launch, the new MyApp.exe is used
4. Delete the .old file at some point after the old process exits
(it cannot be deleted while running, so clean it up at the next update or at startup)
The value of this pattern is that it delivers “the new version from the next launch” without stopping the process, and update frameworks such as Chrome’s updater and Squirrel/Velopack are fundamentally built on this property. There are three caveats: renaming only works within the same volume (a move to another volume becomes a copy plus a delete, and the delete fails); cleaning up the .old files has to be part of the design; and because “the old process running right now” carries on as the old version, there is a window in which old and new processes coexist.
If what you want is to track down by hand, as part of a failure investigation, “who is holding it in the first place”, the quickest route is searching handles and loaded DLLs with Process Explorer or the Handle command. That procedure is written up in the sister article published the same day, “Chasing Hangs and Leaks with Process Explorer, Handle and VMMap”. In this article we do the same thing from code — that is, we use Restart Manager as a way to build it into the updater itself.
3. How Restart Manager Works — From RmStartSession to RmRestart
Restart Manager is a set of APIs (rstrtmgr.dll) shipped as standard from Windows Vista / Windows Server 2008 onwards, and its purpose is stated plainly at the top of the documentation: the main reason an installation or update requires a system restart is that the files being updated are in use by running applications and services, and Restart Manager reduces or eliminates that restart by shutting down and restarting all but the critical applications and services.1 You have surely seen the dialog listing processes during an MSI installation — “the following applications are using files that…” — and the installers and updaters of large products such as Visual Studio and Office are users of this mechanism too.
The API flow is a straight line.
| Step | Function | What it does |
|---|---|---|
| 1 | RmStartSession | Starts a session and returns a handle and a session key (a GUID string) |
| 2 | RmRegisterResources | Registers the file paths you want to update (process and service names are also accepted) |
| 3 | RmGetList | Enumerates the applications and services using the registered resources3 |
| 4 | RmShutdown | Shuts them down (politely by default, forcibly if specified)2 |
| 5 | — | Replace the files during this window |
| 6 | RmRestart | Restarts the applications that registered for restart |
| 7 | RmEndSession | Closes the session |
There are a few points of specification worth knowing.
- Shutdown order and restart order. Shutdown proceeds in the order GUI apps → console apps → services → Explorer, and after the update the registered applications are restarted in reverse order.1
- Shutdown is done “politely first”. GUI apps are sent WM_QUERYENDSESSION/WM_ENDSESSION (lParam=ENDSESSION_CLOSEAPP), and apps that do not comply are also sent WM_CLOSE. Console apps get CTRL_C_EVENT, and services are stopped via the SCM. Even when RmForceShutdown is specified, a graceful shutdown is attempted first, and unresponsive apps are then forcibly terminated after 30 seconds (20 seconds for services).52
- Only applications registered via RegisterApplicationRestart can be restarted. Specifying RmShutdownOnlyRegistered on RmShutdown gives you the safer behavior of “only shut down if everyone has registered for restart”.25
- Shutdown across sessions is not possible. An installer running as a LocalSystem service cannot shut down or restart applications running in a user session. This is the thing that trips people up most when designing unattended overnight updates, and it takes measures such as separately launching a process inside the target session to handle shutdown and restart.2
- Critical system services and critical processes are out of scope. In that case a determination that an OS restart is required (RM_REBOOT_REASON) is returned.13
It is worth being clear about the relationship with MSI as well. MSI packages under Windows Installer 4.0 and later use Restart Manager automatically. The default behavior is “rather than restarting the OS, shut down and restart applications where possible”. What a package author can do is: add the MsiRMFilesInUse dialog, which offers the user the “automatically close and attempt to restart applications” option in a full-UI install (falling back to the traditional FilesInUse dialog on older Installers); control behavior with properties such as MSIRESTARTMANAGERCONTROL; and register additional resources by calling RmJoinSession from a custom action via the MsiRestartManagerSessionKey property (sequencing that custom action before the InstallValidate action, where files in use are detected). In a silent install, Restart Manager is always used and applications are shut down automatically.4
In other words, if you distribute via MSI you rarely need to write “code that calls Restart Manager” yourself — the real work is getting the application’s etiquette (the next section but one) right. Calling the API directly only pays off when you are writing a custom updater. Choosing a distribution method in the first place is covered in “Choosing a Windows App Distribution Method - MSI/MSIX/ClickOnce/xcopy/Custom Updater”.
4. Reporting “Who Is Holding It” in C# — RmGetList Diagnostic Code
Within Restart Manager, the part that is useful even to people who never write update logic is RmGetList. Because you can obtain “the list of processes and services using this file” from an API, you can change your updater’s error message from “the file is in use” to “MyApp.exe (PID 4132) on the accounting terminal is holding it”. Written as P/Invoke from C#, it looks like this (it works on both .NET Framework 4.8 and .NET 8).
// WhoLocks.cs — enumerate "who is holding" a given file using Restart Manager
// Usage: 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; // the session key is a GUID string (32 chars + terminator)
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=GUI app, 2=other window, 3=service, 4=Explorer, 5=console, 1000=critical
[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;
}
// Restart Manager APIs return the Win32 error code as the return value, not 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("Usage: WhoLocks <file path> ...");
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, $"RmStartSession failed (rc={rc})");
try
{
// The API contract requires registered file names to be full paths.
// Normalize before registering so it still works when called with relative paths
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, $"RmRegisterResources failed (rc={rc})");
// Ask for the required count -> allocate the array -> retrieve. If processes have been
// added between the two calls, ERROR_MORE_DATA comes back again, so retry in a loop
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, $"RmGetList failed (rc={rc})");
count = needed;
apps = new RM_PROCESS_INFO[needed];
}
Console.WriteLine($"Processes/services in use: {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} " +
$"Restartable={a.bRestartable} Session={a.TSSessionId} " +
$"Name={a.strAppName} Service={a.strServiceShortName}");
}
}
finally
{
RmEndSession(session); // always release the session in a finally block
}
}
}
There are three key points. First, the Restart Manager APIs return the Win32 error code directly as their return value and do not use GetLastError (there is no need for SetLastError = true on DllImport). Second, RmGetList follows the calling convention “if the buffer is too small, return ERROR_MORE_DATA (234) along with the required count”, so you write it as an ask → allocate → retrieve loop.3 Third, if lpdwRebootReasons is anything other than 0 (RmRebootReasonNone), that is a determination that “shutting down applications is not enough — an OS restart is required”.3 For how to marshal strings in structures with ByValTStr, and for the general principles of writing this kind of P/Invoke safely, see “Safely Calling Win32 APIs from C# — A Practical P/Invoke Guide”.
Add two more P/Invokes for RmShutdown and RmRestart to this code and you have the skeleton of a custom updater: enumerate → shut down politely → replace → restart. Note, however, that RmShutdown may only be called by the process that called RmStartSession itself, and must not be called from an MSI custom action (because MSI itself manages the session).4
5. The Application’s Etiquette — The Three-Part Set for “Being Closed Politely and Coming Back Up as It Was”
Neither Restart Manager nor MSI kills processes outright with a terminate (unless you specify forced shutdown). If the application does not respond, you end up back at the “files in use” dialog bothering the user. To make a business application “update-friendly”, implement the following three-part set on the application side.5
| (1) Register for restart with RegisterApplicationRestart. Call it once immediately after startup. Restart Manager can only restart registered applications, and this is the one and only way to tell the OS the command line to use when restarting. The main specifications are: do not include the exe name in the command line (the OS adds it), the maximum length is RESTART_MAX_CMD_LINE, and the app is not restarted during the first 60 seconds after startup, to prevent restart loops. If you do not want restarts after a crash or hang, specify RESTART_NO_CRASH | RESTART_NO_HANG to narrow it down to “restart on update only”.6 |
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern int RegisterApplicationRestart(string commandLine, int flags);
// Register at startup. The "/restored" flag lets you branch into restore handling after a restart
// RESTART_NO_CRASH(1) | RESTART_NO_HANG(2) = restart only when shut down for an update
RegisterApplicationRestart("/restored", 1 | 2);
(2) Respond to WM_QUERYENDSESSION (lParam=ENDSESSION_CLOSEAPP). Restart Manager uses this message before shutdown to ask “may I close you?”. Do not exit yet at this point — return TRUE if you are ready (other applications may not be ready). Returning FALSE cancels the shutdown, but since you will be shut down anyway when forced shutdown is specified, avoid designs that lean on FALSE. This is also your last chance to call RegisterApplicationRestart again in the context of an update. Re-registering with the state needed for restoration (the ID of the report that was open, say) baked into the command line arguments lets you return all the way to “the original screen” after the restart.56
(3) Save unsaved data in WM_ENDSESSION and exit. The actual instruction to exit arrives as WM_ENDSESSION (wParam=TRUE, lParam=ENDSESSION_CLOSEAPP). Within the timeout (30 seconds for applications when forced), finish auto-saving unsaved data, serializing working state and closing connections, then exit. The official guidelines recommend “saving user data periodically in the first place”. For a console application, CTRL_C_EVENT arrives instead of the WM_ messages, so do the same thing via SetConsoleCtrlHandler (Console.CancelKeyPress in C#).52
In WPF/WinForms, Application.SessionEnding / Form.FormClosing (CloseReason.WindowsShutDown) are the entry points, but seeing the ENDSESSION_CLOSEAPP flag itself (the distinction between an OS restart and “just close the app and restart it later”) requires hooking the window procedure. Also, review your single-instance mutex design as part of the same exercise, so that the restarted process does not collide with “the previous instance of yourself that is still shutting down” (“Preventing Multiple Instances of a Windows App”).
Once this three-part set is in place, the MSI update experience changes from “announce it over the PA system and get everyone to close the app” to “the update runs, the app closes itself, and after the update it comes back automatically to the screen it was on”. It is the same mechanism behind Office’s behavior of reopening your documents after a restart.
6. Implementation Patterns for Auto-Update — A Separate Process, rename-then-replace, and Replace-on-Reboot
Here are the design patterns for building your own auto-update. There is one overriding premise: you cannot overwrite yourself (the running exe) from within yourself, so the update work has to escape somewhere — into “another process” or “another name”.
Pattern A: a separate updater process that waits and then replaces. When the main application detects an update, it launches the updater exe (or a copy of it in a temporary location) and exits. The updater waits for the main process to exit (Process.WaitForExit, or waiting for a mutex to be released), replaces the files and restarts the main application. It is naive and dependable, but for cases where “the main app takes a long time to exit” or “there are many processes to wait for in a multi-process architecture”, combining it with the previous section’s RmGetList to identify the holders and RmShutdown to close them works well.
Pattern B: rename-then-replace (updating without stopping anything). As in Section 2, rename the running exe/DLL, put the new version in place, and get “the new version from the next launch”. Its advantage is that it does not interrupt the user’s work, which suits resident and long-running business applications. .NET update frameworks such as Squirrel and Velopack also exploit this property — in the form of per-version folders plus swapping the entry exe — and provide “apply the update in the background, new version from the next launch” as a ready-made framework. Rolled by hand, it comes down to: download → verify → extract → rename aside → place → clean up the retired files at the next startup.
Pattern C: MoveFileEx + MOVEFILE_DELAY_UNTIL_REBOOT (replace on reboot). For things you truly cannot stop and cannot escape with a rename — a service’s host process, a shell extension — the last resort is scheduling the replacement for the next OS restart. The reservation is recorded in the registry under HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations and executed in registration order early in the next boot (immediately after AUTOCHK, before the page file is created). Use it with an understanding that it can only be called by a member of the administrators group or LocalSystem; that it cannot be combined with MOVEFILE_COPY_ALLOWED (so it is limited to the same volume); that if the destination file already exists you also need MOVEFILE_REPLACE_EXISTING (without it the reservation succeeds but the replacement at reboot does not happen); and that the function succeeding means “the reservation succeeded” and tells you nothing about the actual replacement. When an installer says “a restart is required”, this is precisely what is being queued up inside.7
Two cautions apply to every pattern. The first is updating services: a Windows service cannot replace itself, so the standard approach is to separate out an update process (or a dedicated update service) that performs “Stop via the SCM → replace → Start” (for the basics of service design, see “How to Build and Operate Windows Services”). The second is verifying the update files. Building your own replacement mechanism also means building your own “mechanism for distributing and executing arbitrary exes”, and skipping signature verification or protection of the download path turns the update mechanism itself into an attack path. That topic is dealt with separately in “Security Design for Auto-Update - Why HTTPS Alone Is Not Enough”.
7. Decision Table for Update Approaches
| Approach | When it suits | Caveats |
|---|---|---|
| MSI + accepting an OS restart | Low distribution frequency (a few times a year); a maintenance window is available overnight or at weekends | The easiest to implement. But you will keep showing users “a restart is required”, and you are relying on replace-on-reboot (PendingFileRenameOperations)7 |
| MSI + Restart Manager integration (MsiRMFilesInUse + the application’s three-part set) | You want to keep MSI distribution but improve the update experience; standard apps distributed internally | The installer side is almost entirely automatic. The effect depends on the application’s RegisterApplicationRestart/WM_QUERYENDSESSION implementation45 |
| Custom updater (separate process + rename-then-replace, including Squirrel/Velopack) | High update frequency (weekly or more); users do not have administrator privileges; you must not interrupt their work | Be prepared to build cleanup, signature verification and rollback-on-failure yourself. Adding RmGetList/RmShutdown to deal with “someone holding it” makes it solid3 |
| ClickOnce | Internal Windows clients where all you want is “auto-update at launch” | With an update-at-launch model, the in-use problem barely arises. For the constraints, see “What Is ClickOnce?” |
| Self-update on the service side (separate update process) | Unattended environments and device PCs where a 24/7 service cannot be kept running through the update | A service cannot replace itself. A separate process to handle Stop → replace → Start is mandatory. Mind the session boundary (LocalSystem cannot close user applications)2 |
If you are undecided, look first at “MSI + Restart Manager integration”. It rides on a standard OS mechanism, so the implementation effort is minimal, and the application’s three-part set carries over unchanged if you later migrate to a custom updater.
8. Summary
- An exe or DLL in use cannot be overwritten or deleted, but it can be renamed within the same volume. rename-then-replace, which uses this asymmetry, is the basic form of “updating without stopping anything”.
- Restart Manager is a standard OS API that handles enumerating who is holding a file (RmGetList), shutting them down politely (RmShutdown) and restarting them after the update (RmRestart), and MSI packages under Windows Installer 4.0 and later use it automatically.
- The application’s etiquette is a three-part set: register for restart with RegisterApplicationRestart, return TRUE to WM_QUERYENDSESSION (ENDSESSION_CLOSEAPP), and save unsaved data in WM_ENDSESSION before exiting. That alone gets you “an app that comes back up as it was after an update”.
- The 60-second rule (no restart immediately after startup), the forced-shutdown timeouts (30 seconds for applications, 20 for services) and the session boundary (LocalSystem cannot close user applications) are the things that trip you up in production.
- When replacement is truly impossible, schedule it for the next OS restart with MoveFileEx + MOVEFILE_DELAY_UNTIL_REBOOT. Administrator privileges are mandatory, it is limited to the same volume, and success means the reservation succeeded.
- Building your own update mechanism means building your own “mechanism for distributing arbitrary exes”. Design it all the way through signature verification, path protection and rollback.
Related Articles
- Choosing a Windows App Distribution Method - MSI/MSIX/ClickOnce/xcopy/Custom Updater
- Security Design for Auto-Update - Why HTTPS Alone Is Not Enough
- Chasing Hangs and Leaks with Process Explorer, Handle and VMMap
- Safely Calling Win32 APIs from C# — A Practical P/Invoke Guide (DllImport / LibraryImport / CsWin32)
- Preventing Multiple Instances of a Windows App — Named Mutexes and Activating the Existing Window on a Second Launch
- How to Build and Operate Windows Services — From Choosing Between Task Scheduler and Services to Turning a BackgroundService into a Windows Service
Related Consulting Areas
KomuraSoft LLC handles the design and implementation of auto-update mechanisms for business applications (Restart Manager integration, custom updaters, adopting Squirrel/Velopack), improving operations where “everyone has to be asked to close the app every time we update”, and investigating and fixing “file in use” and “a restart is required” problems in existing installers.
- Windows App Development
- Modernization & Maintenance of Windows Apps
- Technical Consulting & Design Review
- Contact Us
References
-
Microsoft Learn, About Restart Manager. On the purpose of Restart Manager (reducing or eliminating restarts caused by files in use); shutting down in the order GUI apps → console apps → services → Explorer and restarting in reverse order; shutdown across sessions being unsupported; Windows Installer 4.0 using Restart Manager automatically; and critical system services not being stoppable without an OS restart. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, RmShutdown function. On unresponsive applications being forcibly terminated after 30 seconds (20 seconds for services) even when RmForceShutdown is specified; RmShutdownOnlyRegistered allowing “only shut down if everyone has registered for restart”; a LocalSystem service being unable to shut down or restart applications in other user sessions; and return values such as ERROR_FAIL_NOACTION_REBOOT. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, RmGetList function. On returning the applications and services using the registered resources as an array of RM_PROCESS_INFO; the calling convention of returning ERROR_MORE_DATA (234) and the required count when the buffer is too small; lpdwRebootReasons returning the reason an OS restart is required (RM_REBOOT_REASON); and it being provided by rstrtmgr.dll from Windows Vista onwards. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Using Windows Installer with Restart Manager. On Windows Installer 4.0 using Restart Manager automatically and preferring shutting down and restarting applications over an OS restart by default; the MsiRMFilesInUse dialog offering the automatic close-and-restart option in a full-UI install (falling back to FilesInUse in older environments); Restart Manager always being used and applications being shut down in a silent install; custom actions needing to be sequenced before InstallValidate and to call RmJoinSession via MsiRestartManagerSessionKey rather than calling RmShutdown and the like; and control properties such as MSIRESTARTMANAGERCONTROL. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Guidelines for Applications (Restart Manager). On WM_QUERYENDSESSION (lParam=ENDSESSION_CLOSEAPP) being sent to GUI applications, which should return TRUE if ready without exiting at that point; the actual exit happening on WM_ENDSESSION; WM_CLOSE also being sent to applications that do not comply; CTRL_C_EVENT being sent to console applications; and registration via RegisterApplicationRestart being mandatory for restart. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Microsoft Learn, RegisterApplicationRestart function. On registering the command line for restart (excluding the exe name, up to RESTART_MAX_CMD_LINE); the RESTART_NO_CRASH/NO_HANG/NO_PATCH/NO_REBOOT flags; restarts caused by an update being automatic while crashes and hangs require the user’s consent; an application not being restarted unless at least 60 seconds have elapsed since startup, to prevent loops; and the last chance to register during an update being while handling WM_QUERYENDSESSION. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, MoveFileExW function. On MOVEFILE_DELAY_UNTIL_REBOOT writing the reservation into PendingFileRenameOperations (REG_MULTI_SZ) under HKLM\SYSTEM\CurrentControlSet\Control\Session Manager and it being executed in registration order after AUTOCHK runs and before the page file is created; the requirement for administrators group or LocalSystem privileges; it not being combinable with MOVEFILE_COPY_ALLOWED; MOVEFILE_REPLACE_EXISTING being required when the destination file already exists; the return value indicating the success of the reservation rather than of the actual move; and passing NULL for lpNewFileName resulting in deletion at reboot. ↩ ↩2 ↩3
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
CI/CD for WinForms / WPF Apps in Practice — Automating from Build to Signing and Distribution with GitHub Actions
A practical guide to setting up CI/CD for WinForms / WPF apps with GitHub Actions. Covers a minimal YAML for build+test on windows-latest...
Do Business Apps Run on Windows on Arm? — The Reality of x64 Emulation (Prism) and Native DLLs/COM
An answer, aimed at developers and IT staff, to 'will our business app run on Windows on Arm?' Covers how x64 emulation (Prism) works, th...
Safely Calling Win32 APIs from C# — A Practical P/Invoke Guide (DllImport / LibraryImport / CsWin32)
A practical rundown of what to watch for when calling Win32 APIs and native DLLs from C# via P/Invoke. Covers the differences between Dll...
Windows App Outsourcing and Contract Development: What to Sort Out Before You Ask
Before commissioning Windows app outsourcing or contract development, here is how to sort out existing software modification, device inte...
Choosing a Windows App Distribution Method - MSI/MSIX/ClickOnce/xcopy/Custom Updater
MSI vs MSIX vs ClickOnce vs xcopy vs custom updater: pick a Windows deployment method by OS integration and update ownership, with a deci...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Windows Software Maintenance & Modernization
We support staged upgrades, feature additions, 64-bit readiness, and maintainable restructuring for existing Windows software.
Frequently Asked Questions
Common questions about the topic of this article.
- Why can a running exe or a loaded DLL be renamed but not overwritten?
- Windows holds a running exe or a loaded DLL as a memory mapping (an image section), so modifying the file's contents or deleting it fails with an error (a sharing violation or access denied). The "name" in the directory, however, is independent of the file's contents, so a rename (move) within the same volume succeeds even while the file is running. The rename-then-replace pattern exploits this asymmetry: rename the old exe to a retirement name, put the new exe in place under the original name, and the new version is used from the next launch. Chrome's auto-update and frameworks such as Squirrel and Velopack are fundamentally built on this property too.
- What does the Restart Manager API actually do for me?
- You register the files you want to update, and it enumerates "the applications and services currently using those files", shuts them down where possible, and can even restart them after the update. It is a standard Windows API (Windows Vista and later). The flow is: create a session with RmStartSession, register the target files with RmRegisterResources, enumerate the holding processes with RmGetList, shut them down with RmShutdown, restart them with RmRestart, and finally RmEndSession. Shutdown proceeds in the order GUI apps → console apps → services → Explorer, and restart happens in reverse order. It exists to reduce "a restart is required" prompts, and MSI packages under Windows Installer 4.0 and later use it automatically.
- What happens if I call RegisterApplicationRestart?
- Your app registers its own restart command line with the OS, and after a shutdown caused by an update, Restart Manager restarts the app with that command line (Restart Manager can only restart registered apps). The app also becomes eligible for restart after a crash or hang, though in those cases the user's consent is requested; restarts caused by an update happen automatically. Note that, to prevent loops, an app is not restarted unless at least 60 seconds have elapsed since it started, and that the command line must not include the exe name. Good practice is to re-register with any state needed for restoration (such as which file was open) folded into the command line arguments.
- When would I use MoveFileEx with MOVEFILE_DELAY_UNTIL_REBOOT?
- It is the last resort when you simply cannot stop the process and rename-then-replace is not available either: it schedules a file move or delete for the next OS restart. The reservation is written to PendingFileRenameOperations in the registry (HKLM\SYSTEM\CurrentControlSet\Control\Session Manager) and is executed in registration order early in the next boot (after AUTOCHK, before the page file is created). The call requires membership of the administrators group or LocalSystem privileges, and it cannot be combined with MOVEFILE_COPY_ALLOWED, so it cannot be used to move across volumes. It is also worth remembering that the function's success or failure is the success or failure of the reservation, not of the actual replacement.
- How do I handle "files in use" gracefully in an MSI installer?
- Windows Installer 4.0 and later integrates with Restart Manager automatically, and by default prefers shutting down and restarting applications over restarting the OS. If you add the MsiRMFilesInUse dialog to your package, users installing with the full UI are offered the option to "automatically close and attempt to restart applications". When run under an older Windows Installer it falls back to the traditional FilesInUse dialog, so the standard practice is to include both. Behavior can be controlled with properties such as MSIRESTARTMANAGERCONTROL, and in a silent install Restart Manager is always used and applications are shut down automatically. If the application implements RegisterApplicationRestart and responds to WM_QUERYENDSESSION, you get an update experience that is close to seamless, with the app coming back up as it was afterwards.
Author Profile
Profile page for the article author.
Go Komura
Representative of KomuraSoft LLC
Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.
Public links