How can a C++ binary replace itself?

2019-01-17 10:08发布

问题:

I asked this question in a more general design context before. Now, I'd like to talk about the specifics.

Imagine that I have app.exe running. It downloads update.exe into the same folder. How would app.exe copy update.exe over the contents of app.exe? I am asking specifically in a C++ context. Do I need some kind of 3rd mediator app? Do I need to worry about file-locking? What is the most robust approach to a binary updating itself (barring obnoxious IT staff having extreme file permissions)? Ideally, I'd like to see portable solutions (Linux + OSX), but Windows is the primary target.

回答1:

  1. Move/Rename your running app.exe to app_old.exe
  2. Move/Rename your downloaded update.exe to app.exe
  3. With the next start of your application the update will be used

Renaming of a running i.e. locked dll/exe is not a problem under windows.



回答2:

On Linux it is possible to remove the executable of a running program, hence:

  • download app.exe~
  • delete running app.exe
  • rename app.exe~ to app.exe

On Windows it is not possible to remove the executable of a running program, but possible to rename it:

  • download app.exe~
  • rename running app.exe to app.exe.old
  • rename app.exe~ to app.exe
  • when restarting remove app.exe.old


回答3:

It's an operating system feature - not a C++ one.
What OS are you on?

In Windows see the MoveFileEx() function, on linux simply overwrite the running app ( Replacing a running executable in linux )



回答4:

On Windows at least an application running is locking its own .exe file and all statically linked .dll files. This prevents an application from updating itself directly, at leads if it desires to prevent a re-boot (if re-boot is OK the app can pass in the MOVEFILE_DELAY_UNTIL_REBOOT flag to MoveFileEx and is free to 'overwrite' it's own .exe, as is delayed anyway). This is why typically applications don't check for updates on their own .exe, but they start up a shim that checks for updates and then launches the 'real' application. In fact the 'shim' can even be done by the OS itself, by virtue of a properly configured manifest file. Visual Studio built application get this as a prefab wizard packaged tool, see ClickOnce Deployment for Visual C++ Applications.

The typical Linux app doesn't update itself because of the many many many flavors of the OS. Most apps are distributed as source, run trough some version of auto-hell to self-configure and build themselves, and then install themselves via make install (all these can be automated behind a package). Even apps that are distributed as binaries for a specific flavor of Linux don't copy themselves over, but instead install the new version side-by-side and then they update a symbolic link to 'activate' the new version (again, a package management software may hide this).

OS X apps fall either into the Linux bucket if they are of the Posix flavor, or nowadays fall into the Mac AppStore app bucket which handles updates for you.

I would day that rolling your own self-update will never reach the sophistication of either of these technologies (ClickOnce, RPMs, AppStore) and offer the user the expected behavior vis-a-vis discovery, upgrade and uninstall. I would go with the flow and use these technologies in their respective platforms.



回答5:

Just an idea to overcome the "restart" problem. How about making a program, that does not need to be updated. Just implement it in a plugin structure, so it is only an update host which itself loads a .dll file with all the functionality your program needs and calls the main function there. When it detects an update (possibly in a seperate thread), it tells the dll handle to close, replaces the file and loads the new one. This way your application keeps running while it updates itself (only the dll file is reloaded but the application keeps running).



回答6:

Use an updater 3rd executable like many other apps.

  • Download new version.
  • Schedule your updater to replace the app with the new version.
  • Close main app.
  • Updater runs and does the work.
  • Updater runs new version of your app.
  • Updater quits.