oleg-shilo / wixsharp

Framework for building a complete MSI or WiX source code by using script files written with C# syntax.
MIT License
1.12k stars 175 forks source link

The best way to execute a process at the end of the installation #859

Closed WayneHiller closed 4 years ago

WayneHiller commented 4 years ago

I have a setup that runs with Elevated Privileges. InstallPrivileges = InstallPrivileges.elevated.

 var project = new ManagedProject("ABS",
...
new PathFileAction("[SETUPDIR]ABS Setup.exe", "", "SETUPDIR", Return.asyncNoWait,
                                                When.After, Step.InstallFinalize, Condition.NOT_Installed)
);

When running the installation it Prompts for Permission. The ABS Setup.exe seems to run with Elevated Privileges on all Windows except for Windows Server 2019. They must have changed something.

Is this the correct way to run the process? Do I need to Elevate the Privileges of the process somehow in the PathFileAction above?

oleg-shilo commented 4 years ago

My suggestion is to use events:

project.BeforeInstall += project_BeforeInstall;
. . .
static void project_BeforeInstall(SetupEventArgs e)
{
    if(e.IsInstalling)
    {
            var startInfo = new ProcessStartInfo();
            startInfo.UseShellExecute = true;
            startInfo.FileName = e.InstallDir.PathCombine("ABC.exe");
            startInfo.Verb = "runas"; // this will ensure elevation
            Process.Start(startInfo);
    }
}

If you want to run it after the install then even simpler as AfterInstall event handler is already elevated:

project.AfterInstall += 
    e =>
    {
        if(e.IsInstalling)
                Process.Start(e.InstallDir.PathCombine("ABC.exe"));
    };
WayneHiller commented 4 years ago

I will try the AfterInstall event, Thank you

WayneHiller commented 4 years ago
project.AfterInstall += e =>
            {
                if(e.IsInstalling)
                {
                    Process.Start(new ProcessStartInfo {
                        FileName = e.InstallDir.PathCombine(@"Setup\\AIS Manager Setup.exe"),
                        WorkingDirectory = e.InstallDir.PathCombine("Setup")
                    });
                }
            };

I have this working however I need to first check if the "Setup" feature was installed and get the SETUPDIR so I know where the ABS Setup.exe is located. Is there a way to get that info in AfterInstall?

WayneHiller commented 4 years ago

This seems to work fine:

            project.DefaultDeferredProperties += ",SETUPDIR";
            project.AfterInstall += e =>
            {
                if(e.IsInstalling)
                {
                    try
                    {
                        var path = e.Session.Property("SETUPDIR");
                        if(!string.IsNullOrEmpty(path))
                        {
                            Process.Start(new ProcessStartInfo
                            {
                                FileName = path.PathCombine("AIS Manager Setup.exe"),
                                WorkingDirectory = path
                            });
                        }
                    }
                    catch(Exception x)
                    {
                        Debug.WriteLine(x.Message);
                    }
                }
            };
oleg-shilo commented 4 years ago

And if your SETUPDIR is just a renamed INSTALLDIR then you can even simplify your approach a bit:

Compiler.AutoGeneration.InstallDirDefaultId = "SETUPDIR";

project.AfterInstall += e =>
{
    if(e.IsInstalling)
    {
        try
        {
            Process.Start(new ProcessStartInfo
            {
                FileName = e.InstallDir.PathCombine("AIS Manager Setup.exe"),
                WorkingDirectory = path
            });
        }
        catch(Exception x)
        {
            e.Session.Log(x.Message);
        }
    }
};

And on unrelated note it's arguably better if AIS Manager Setup.exe does rely on working dir, which you have no control over, but instead discover it's own location at startp with:

Path.GetDirectory(Assembly.GetExecutingAssembly().Location);

And then your custom action as:

if(e.IsInstalling)
{
    try
    {
        Process.Start(e.InstallDir.PathCombine("AIS Manager Setup.exe"));
    }
    catch(Exception x)
    {
         e.Session.Log(x.Message);
    }
}
WayneHiller commented 4 years ago

The naming is a bit confusing but AIS Manager Setup is actually a program that gets installed in a folder under the INSTALLDIR. Plus it is optional (In a Feature) along with other programs (Features).

oleg-shilo commented 4 years ago

OK, I see, then indeed you need to use SETUPDIR

WayneHiller commented 4 years ago

When I was using InstallAware I was building crazy complex installations with all the setting in it that the program needed to operate (stored in ...exe.config). After switching to dotnet core I decided to make the installation as simple as possible and create a dedicated setup program that can be run anytime to change the applications setting. This has turned out to be a far better way to do it, and makes it far simpler to go cross platform.

oleg-shilo commented 4 years ago

After switching to dotnet core I decided to make the installation as simple as possible and create a dedicated setup program that can be run anytime to change the applications setting.

Well done. That is exactly what an ideal setup should look like.

Your setup should only be responsible for deployment of the product but not for the configuration, which is a responsibility of the application itself. IE i should initiate the configuration setup at its first startup.

This deployment paradigm was captured and enforced a long time ago by the "Build for Vista" certification. Yet that long time ago and we still not always follow this healthy guideline. :) That sertification would fail any setup that deploys configuration instead creating it at startup.

WayneHiller commented 4 years ago

In my case the applications are all server side like websites and services so they can't really ask the user for initial settings like db connection strings, urls, task schedule times etc. This is where the standalone "Setup" app comes in. I run it after the installation to gather info and then it can be run anytime afterwards. Plus it is dotnet core so the exact same settings app can be run cross platform. The installation is just a simple way to get the files on the destination machine.

Now what would be cool is if I could build an installation tool that included WixSharp and tools to build installation for Mac and Linux in one master tool to rule them all :)

jjxtra commented 1 year ago

How to start the process as the user who ran the installer?

oleg-shilo commented 1 year ago

The answer is in #1250