0

I have a windows CE 6.0 program that uses a .cab file for installation. it incorporates the guide from http://msdn.microsoft.com/en-us/library/aa446487.aspx to support automatic self-updates.

the program can check for updates just fine, it downloads the update like it should, but when I try to make it unpack the .cab file it just downloaded, it fails.

this is my code:

void ResponseReceived(IAsyncResult res)
{
    try
    {
        _mResp = (HttpWebResponse)_mReq.EndGetResponse(res);
    }
    catch (WebException ex)
    {
        MessageBox.Show(ex.ToString(), "Error");
        return;
    }
    // Allocate data buffer
    _dataBuffer = new byte[DataBlockSize];
    // Set up progrees bar
    _maxVal = (int)_mResp.ContentLength;
    pgbDownloadBar.Invoke(new EventHandler(SetProgressMax));
    // Open file stream to save received data
    _mFs = new FileStream(@"\Application\CCOptimizerSetup.cab",
      FileMode.Create);
    // Request the first chunk
    _mResp.GetResponseStream().BeginRead(_dataBuffer, 0, DataBlockSize,
      OnDataRead, this);
}

void OnDataRead(IAsyncResult res)
{
    // How many bytes did we get this time
    int nBytes = _mResp.GetResponseStream().EndRead(res);
    // Write buffer
    _mFs.Write(_dataBuffer, 0, nBytes);
    // Update progress bar using Invoke()
    _pbVal += nBytes;
    pgbDownloadBar.Invoke(new EventHandler(UpdateProgressValue));
    // Are we done yet?
    if (nBytes > 0)
    {
        // No, keep reading
        _mResp.GetResponseStream().BeginRead(_dataBuffer, 0,
          DataBlockSize, OnDataRead, this);
    }
    else
    {
        // Yes, perform cleanup and update UI.
        _mFs.Close();
        _mFs = null;
        Invoke(new EventHandler(AllDone));
    }
}

private void AllDone(object sender, EventArgs e)
{
    Cursor.Current = Cursors.Default;
    DialogResult dialogresult = MessageBox.Show(@"CCOptimizer has finished downloading. Open now?", "Download complete", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
    switch (dialogresult)
    {
        case DialogResult.OK:
            Application.Exit();
            Dispose();
            Close();
            Invoke(new EventHandler(StartProcess));
            break;
        case DialogResult.Cancel:
            Form1 oForm = new Form1();
            oForm.Show();
            Hide();
            break;
    }
}

private void StartProcess(object sender, EventArgs e)
{
    Process.Start(@"\Application\CCOptimizerSetup.cab", null);
}

It doesn't work though. wherever I place the invocation of StartProcess(), either it just shuts down with no message or I get an error during unpacking that one of the files i'm trying to update is still in use. During debugging, it just stops the program. If I try on an installed version, it very briefly flashes a window, then locks up the machine with the WaitCursor rotating and needs a reboot, although it can install just fine afterwards.

How can I close the current program and open a .cab file without having to reboot the machine?

Nzall
  • 3,439
  • 5
  • 29
  • 59

1 Answers1

1

You've got a flaw in your update logic, probably that the app you want to update is the thing checking for updates (it's not entirely clear from the question, but the behavior suggest this is the case).

An application cannot directly update itself because, for what should be fairly obvious reasons, you cannot replace the assemblies that are already loaded and running.

There are, generally speaking, two ways that I have used to get around this. Both require a second executable (which in many case I already have as a watchdog app anyway).

  1. Instead of directly launching your application, launch some "updater" application first. That application can check for updates, inform the user, download, and install/overwrite the target application because it's not yet running. The updater then runs the target application when it's done, or if no update steps are required.
  2. Instead of directly launching your application, launch some "bootstrap" application first. The bootstrap looks in local temporary storage for any update files, executes them if they exist, and then runs the normal application. Have the application itself check for updates and pull them to the temporary storage location. When updates have been pulled, have it shutdown and restart the bootstrap, which then will apply the update.
ctacke
  • 66,480
  • 18
  • 94
  • 155
  • Yes, the app tries to download and update itself to emulate a clickonce app, because I need the app version to check if there's an update. can I let the app check for an update, then launch a secondary updater app if there is a new version? that doesn't seem possible from your descriptions of the updater application and the bootstrap application. – Nzall Jun 11 '14 at 14:45
  • Yes, you're talking about #2. The app checks for updates, downloads them, but does not apply them. It then launches a bootstrap app and closes itself. The bootstrap waits for the main app to finish, applies the updates, then re-launches the main app. – ctacke Jun 11 '14 at 14:56
  • so to summarize: I just make a separate app that does Process.Start(@"\Application\CCOptimizerSetup.cab", null); and opens the main app when the unpacking finished. I then call this app from the main app after I have downloaded the update, and close the main app. Could you give some info on the correct order of steps to ensure that the current application is completely closed before I start the bootstrap app? – Nzall Jun 12 '14 at 08:43
  • You can't close the app before starting the bootstrap, the app has to start it, therefore must be running. You must start the bootstrap, and it must wait for the app to finish. The easiest way to do that is probably waiting on the process handle. – ctacke Jun 12 '14 at 13:46