locked
Self-updating application RRS feed

  • Question

  • I'm hoping someone here can give me ideas, or at least point me in the right direction.

    Our application (under XP) is capable of downloading and installing updates to itself. An XML manifest is stored on the server, which outlines all the updated files. However, since the format of this file might need to change over time, the application itself isn't qualified to read this file. Instead, it uses a DLL. The DLL is always up to date (i.e. able to read the file manifest) because the application first downloads the latest version of  the DLL from the server. It then invokes code in the DLL, which processes the manifest, creates a list of files that need to actually be downloaded, and returns control to the application.

    Next, the application needs to exit, and allow the files to be installed (one of which is invariably the application itself). This process is handed over to another exe, which the application downloads the latest version of as well. The installer reads the manifest, examines the downloaded files, installs everything, and then re-launches the main application.

    All of this is working under XP. When it comes to Vista, we obviously are going to encounter some challenges.

    The primary problem is that the application, which must run as a standard user, has to somehow download a DLL and an EXE, and place them into CSIDL_PROGRAM_FILES_COMMON.

    Is there any way to do this? (At this point I'm just assuming that I wouldn't be able to download them to anywhere outside of \Program Files and still be able to execute them.)

    Also, if I'm able to get the update installer exe written somewhere, will the application (which is currently marked as asInvoker) be able to execute the installer, which is marked requireAdministrator?

    Thanks,
    Dave


    Thursday, February 1, 2007 10:22 PM

All replies

  • It strikes me that I may need to run an external process to unzip the update tools and install them to CSIDL_PROGRAM_FILES_COMMON. This means, potentially, that the UX will involve two escalation prompts - one for installing the update tools, and one for actually installing the updated software.

    Any thoughts?

    Thanks,
    Dave

    Thursday, February 1, 2007 10:47 PM
  • If you want your app to download an update, install the update (elevating the updater), and relaunch as the same user who opened the app, something like this might work:

    1) mainapp.exe downloads update to some_temp_dir\downloadedupdate.exe  - manifested asInvoker

    2) mainapp.exe launches downloadedupdate.exe, exits

    3) downloadedupdate.exe extracts datafiles and updater.exe [manifested requireAdministrator]

    4) downloadedupdate.exe ShellExecute's updater.exe [manifested requireAdministrator] and waits for it to terminate.

    5) updater.exe updates the app, exits.

    6) downloadedupdate.exe relaunches mainapp.exe

    If you need your updater to run as standard user and can't have any elevations, the invoker of the updater user has to have write permission over any files you're going to modify; this is probably doable if the app is installed to the user profile, or if the installation directory has been ACL'd to allow standard users write access.

    Other solutions might call for an updater service.

    Thursday, February 1, 2007 11:26 PM
  • David,

    Thanks for the thoughtful reply. A couple of clarifications ...

    One thing that time has proven to us (we didn't always do things this way) is that it's Far Better (tm) for us if all our Internet access is done through a single application. This is due to firewalls, etc. With feedback from a decent sized customer base, we've found this to be the wisest solution. Therefore, all downloading must be done through the running application. That's why we have (names changed to protect the innocent):

    mainapp.exe - [asInvoker]
    updatescanner.dll - [called from mainapp.exe]
    updateinstaller.exe - [requireAdministrator]

    The updatescanner and updateinstaller get downloaded as updatetools.zip, and I thought, needed to be unzipped to CSIDL_PROGRAM_FILES_COMMON. Are you saying above that it's possible for a standard user to execute an application that doesn't reside in/under \program files? That seems like a huge security hole to my tired brain.

    If that's indeed possible, then I really don't have a problem, because I'll just unzip the updatetools.zip to a machine-specific folder (which is exactly where we download the updated files to before installing them), and execute them from there. A requirement here is that all users find the same versions of the updatetools - so they only need to get downloaded once.

    Thanks for any feedback,
    Dave

    Friday, February 2, 2007 12:44 AM
  • if you can read and write to a location, you can generally execute from it, stuff runs out of your temp folders most of the time when you run an application installer, for example.

    If one of your requirements is that you don't needlessly download the tools that perform the update every time, maybe you can keep a copy of them in the program directory, then copy the one or two files necessary to install the update to the temp directory you download and extract downloadedupdate.zip to.  If downloadedupdate.zip contains updated copies of the update tools, they'll then be able to write over the copies in the program directory, and every user will have the updated copies.

    basically:

    mainapp [asInvoker] running out of \programfiles\mainappfolder\

    finds an update, drops it to %temp%\downloadedupdate.zip, extracts to %temp%\new-well-chosen-temporary-directory-name\updatefiles\

    copies updateinstaller.exe [requireAdministrator] and updatehelper.exe [asInvoker]  to %temp%\new-well-chosen-temporary-directory-name\

    launches updatehelper.exe, running out of %temp%\new-well-chosen-temporary-directory-name\

    updatehelper.exe launches with ShellExecute updateinstaller.exe (running out of %temp%\new-well-chosen-temporary-directory-name\), which looks under .\updatefiles, installs all of those to the \programfiles\mainappfolder directory, exits

    updatehelper.exe relaunches mainapp.exe once updateinstaller.exe terminates.

    Sound sensible?

    Friday, February 2, 2007 5:05 AM
  • On the off-chance that anyone was actually following this thread, I thought I'd report back with what has turned out to be a working solution, and required the least amount of changes from our current system.

    Here's the workflow ...

    1. The main program (asInvoker) checks for an available update, and prompts the user for permission to download it. Given permission it continues ...

    2. The main program checks if there are new update tools. The update tools consist of a single zip file on the server. If new versions are available, the main app downloads the zip, and unzip it to the user's private folder (which is CSIDL_APPDATA + AppName), and makes a note that the update tools are found in that folder. Otherwise the update tools are assumed to be found in CSIDL_PROGRAM_FILES_COMMON.

    3. The main program decides what files within the application need to actually be updated, as compared to the manifest on the server, and downloads those files to a temp folder.

    4. The app the launches the update installer tool, either from the user folder, or the common program files folder, per above.

    5. The installer is marked requireAdministrator, and copies all the files from the temp folder to their final destination.

    6. As a last step, the installer also checks for a flag the main program left for it, saying whether or not the update tools were new. If they were new, the installer copies itself (and the rest of the update tools) to the shared program files folder. This allows our other applications to find the latest tools.

    There are still some glitches here and there (like the fact that a ShellExec'd program that requires escalation doesn't bring the escalation prompt to the front - it's just flashing on the task bar - something 99% of our users will not notice) ... but all in all, this approach is working so far. This presents only a single escalation prompt to standard users - something I obviously wanted to keep to a minimum.

    Dave

    Friday, February 2, 2007 10:51 PM
  • The flashing task-bar thing might be fixed if you can provide the HWND for the active window on the process that launches the tool which is requireAdministrator.  (using ShellExecuteEx rather than ShellExecute).

    Saturday, February 3, 2007 12:29 AM
  • Oh, that's an excellent suggestion. I'll look into that.

    Now onto my next stumper ... (another post!)

    Thanks again.

    Saturday, February 3, 2007 12:34 AM