0

I'm trying to build an application with an auto / self updater. The file will check for updates then immediately download the files and replace the necessary files. I've been trying to put this into an installer package, but am running into a problem with the Application Folder being read only. I've tried removing the readonly parameter using code from multiple SO this one, but after the program is installed, the folder remains read only.

[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Install(IDictionary stateSaver)
{
    base.Install(stateSaver);
    try
    {
        string path = this.Context.Parameters["targetdir"];
        path = path.Substring(0, path.Length - 1);

        DirectoryInfo di = new DirectoryInfo(path);
        di.Attributes &= ~FileAttributes.ReadOnly;
        di.Refresh();
    }
    catch (Exception e)
    {
    }
}

I've tried putting this in the Commit method as well. The path is definitely being pulled (MessageBox.Show showed the correct path).

Do I need to be doing something different to change the application's main folder?


I don't see why the updater process matters in the context of this question, but here is how it is working:

  • User launches the updater app as a sort of "portal" to the main application.
  • The updater checks the server for an update specific to that device.
  • The new files are downloaded and all files are replaced that aren't currently locked.
  • The exe then calls a helper exe and closes itself. The helper exe updates the remaining files (IE the updater itself)
  • The helper then launches the main application.
Community
  • 1
  • 1
teynon
  • 7,540
  • 10
  • 63
  • 106
  • You can't overwrite your EXE while it's running. – SLaks Apr 22 '13 at 21:06
  • I'm not overwriting that file. I have a separate exe for updating. – teynon Apr 22 '13 at 21:09
  • @Tom even with a launcher you're still not looking for the readonly flag but for elevated permissions – Sten Petrov Apr 22 '13 at 21:15
  • Do you exit the original process first? – SLaks Apr 22 '13 at 21:15
  • SLaks, I added the update process so you can see what's going on. I don't see the relevance to the question, though. – teynon Apr 22 '13 at 21:19
  • 1
    There is no concept of a "read-only folder" in Windows. Your program will however typically not have write access to the install location without UAC elevation. You have to ask the user for permission with [a manifest](http://stackoverflow.com/a/2818776/17034). – Hans Passant Apr 22 '13 at 22:39

2 Answers2

0

You're not looking to remove the Read-only flag, you're looking for elevated permissions to write in that folder - it's not read only to begin with.

To that end you can run your installer app with "RunAs":

// launcher code
  if (CheckIfUpdateAvailable()){
    ProcessStartInfo startInfo = new ProcessStartInfo ("MyUpdater.exe");  
    startInfo.Verb = "runas";
    System.Diagnostics.Process.Start (startInfo); 
    Application.Quit();
  }

the process spawned by launcher will have the rights to write in your app folder

and your updater has to be an executable you deploy along with your app - you'll find it hard to overwrite a running executable's file

or you could switch to ClickOnce and this comes for free. Granted - there are some minor limitations on what you can do with a ClickOnce installer.

Sten Petrov
  • 10,943
  • 1
  • 41
  • 61
  • Our application is using some custom authentication methods with a server that are outside of my control. The installer app is also the launcher app. Similar to how you might see Blizzard's game updates. – teynon Apr 22 '13 at 21:10
  • @Tom I've heard many excuses about using MSI, this is a typical one. You can focus on the first part of the answer, which tells you how to deal with your current situation – Sten Petrov Apr 22 '13 at 21:14
  • I'd say "reason" is more of the correct word here, but both parts of your answer don't tell me how to deal with the situation in the question. – teynon Apr 22 '13 at 21:23
  • @Tom right, "reasons"... see if updated answer makes it clearer what you need to do, if not - tell me what's not clear – Sten Petrov Apr 22 '13 at 21:30
  • Since the launcher is the updater and not the main application, your solution wouldn't make sense. – teynon Apr 22 '13 at 21:42
  • @tom you need to split the updater out, you can't elevate the permissions for your current process (or at least not easily and reliably) – Sten Petrov Apr 22 '13 at 23:39
0

http://support.microsoft.com/kb/981778

I ended up doing a self-elevation (restart run-as) for the updater. This will only ask for permission if there is an update available.

// During update process:
if (!IsAdministrator()) 
{
    // Launch itself as administrator
    ProcessStartInfo proc = new ProcessStartInfo();
    proc.UseShellExecute = true;
    proc.WorkingDirectory = Environment.CurrentDirectory;
    proc.FileName = Application.ExecutablePath;
    proc.Verb = "runas";

    try
    {
        Process.Start(proc);
    }
    catch
    {
        // The user refused to allow privileges elevation.
        // Do nothing and return directly ...
        return false;
    }
    Application.Exit();
}



public static bool IsAdministrator()
{
    var identity = WindowsIdentity.GetCurrent();
    var principal = new WindowsPrincipal(identity);
    return principal.IsInRole(WindowsBuiltInRole.Administrator);
}

Another solution that is working, although not entirely how I would like it, is to go through each project's properties -> Security -> Enable ClickOnce security settings and then building the installer. This is annoying because it asks for permission every time the file runs on a UAC account. However, it is working and doesn't require some looping exe launching.

teynon
  • 7,540
  • 10
  • 63
  • 106