Visual Studio Installer > How To Launch App at End of Installer
Asked Answered
I

8

63

This is probably a stupid question and my Googling just is not functioning today.

I have an application I added a Visual Studio Installer > Setup Wizard project to. I am wondering how to add a button or check box that would launch the application after successful install. This would be located on the last page of the MSI Installer Package. I am using Visual Studio 2010 Ultimate.

I am needing this so that when the application does an automatic update, it auto launches the installer. I just need the installer to relaunch the app after the update.

Indemnity answered 2/7, 2010 at 20:1 Comment(3)
I'd recommend using Cheeso's solution here.Maya
Documentation link: learn.microsoft.com/en-us/previous-versions/visualstudio/…Bemba
For Installer class override documentation: learn.microsoft.com/en-us/dotnet/api/…Bemba
M
104

Warning: The application will end up running as a high privilege account, which has security and user experience implications.

To run any application after the installation is complete,

  1. Right-click on your setup project, click on Custom Actions.
  2. Then right-click on Commit, Add Custom Action, and choose the file you would like to run. (Note that it has to be in your application folder already, which shouldn't be a problem in your case since you are running your program anyway.
  3. Simply choose the output of your project.
  4. Then, click on this added .exe, and change InstallerClass to false. This is crucial because it will look for an installer program otherwise.
  5. You could even pass parameters to your .exe by adding them to the Arguments property
Metropolis answered 2/7, 2010 at 20:51 Comment(14)
Excellent..one question, would the main app be considered 'Primary output from ApplicationName (Active)'?Indemnity
One problem. The install app starts my app but never closes until I exit my app. It gets stuck on "98%" and only finishes when I exit my app. Is there a way to shutdown the installer gracefully? I ensured I set InstallerClass to False. Is there something I am missing?Indemnity
aah yes that will happen...in my case that was acceptable. what you can do instead is run a script from your installer which will in turn run your app and exit. this way, your app will run, but the script will exit which will let your installer complete also.Metropolis
I just put "INSTALLER" in as the Arguments, and in my app entry point, did: if (args.Length == 1 && args[0] == "INSTALLER") { Process.Start(Application.ExecutablePath); return; } Note: Don't use Application.Restart(), as it passes the same arg in ;-)Trumpet
Or, to use Danny's solution in a WPF app: First create an entry point method: frankmao.com/2009/05/07/… Then add this to the method: if (e.Args.Length == 1 && e.Args[0] == "INSTALLER") { Process.Start(Assembly.GetExecutingAssembly().Location); Application.Current.Shutdown(); }Influence
Just an FYI, the exe will end up running as a high privilege account, not the "normal" account, may or may not be a problem for you (it is for me) #3940231Numerate
When does the "commit" fire? Is it after the installation part has completed?Sumac
I'm sorry but this answer is just wrong. For starters commit custom actions are only designed to cleanup rollback data. They don't execute when rollback is disabled. Then there is the security model problems discussed.Gangrel
An installer built by VS2010 can't run JS scripts if you're installing on Windows 8. "A script required etc. could not be run". That's the only case I tested; for all I know it never worked under any circumstances.Peplos
As Maurice Flanagan pointed out, the exe will end up running as a high privilege account. This made my application not work correctly unless ran as an administrator. Even removing sbenderli's solution didn't fix my issue. To fix my problem I had to remove sbenderli's solution from my project, then change my product code and my upgrade code. Hope this saves someone some time.Maya
In my scenario, I wanted to run an executable that installed a log into the Windows event viewer before the user got a chance to use the application. I created a "Primary output from WindowsEventLogMaker" in the Custom Actions > Install folder. Be sure to check the properties on the "Primary output from WindowsEventLogMaker", specifically change the InstallerClass setting to False. linkIrrecoverable
Hi, Please anyone will suggest me if required to code anything in installer class for this? Because, I'm getting this problem and not able to resolve without your help. So, Please help me. Thanks in advanceSadler
I've done the same. Issue is that before click on 'ok' button at last screen during installation, application got launched. Application launch only and only when I click on ok button. Please help usSadler
@Metropolis Hi,After completing installation of window form MSI setup, my raw files(Design and coding) are also showing at installation location. I've developed this application using visual studio 2010. Setup created successfully, but after installation that MSI this problem is happening. Please help me. Thanks in advance.Sadler
R
25

The solution from https://blogs.msdn.microsoft.com/astebner/2006/08/12/mailbag-how-can-i-customize-an-msi-in-the-visual-studio-setupdeployment-project/ adds a checkbox at the end of the setup to choose if you want to start the application or not. You can modify the script to be checked by default...or even hide it.

The big advantage here is, that the application won't run with elevated rights like Maurice Flanagan mentioned.

The required script you need is:

// EnableLaaunchApplication.js <msi-file>
// Performs a post-build fixup of an msi to launch a specific file when the install has completed


// Configurable values
var checkboxChecked = true;         // Is the checkbox on the finished dialog checked by default?
var checkboxText = "Launch [ProductName]";  // Text for the checkbox on the finished dialog
var filename = "WindowsApplication1.exe";   // The name of the executable to launch - change this to match the file you want to launch at the end of your setup


// Constant values from Windows Installer
var msiOpenDatabaseModeTransact = 1;

var msiViewModifyInsert         = 1
var msiViewModifyUpdate         = 2
var msiViewModifyAssign         = 3
var msiViewModifyReplace        = 4
var msiViewModifyDelete         = 6



if (WScript.Arguments.Length != 1)
{
    WScript.StdErr.WriteLine(WScript.ScriptName + " file");
    WScript.Quit(1);
}

var filespec = WScript.Arguments(0);
var installer = WScript.CreateObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);

var sql
var view
var record

try
{
    var fileId = FindFileIdentifier(database, filename);
    if (!fileId)
        throw "Unable to find '" + filename + "' in File table";


    WScript.Echo("Updating the Control table...");
    // Modify the Control_Next of BannerBmp control to point to the new CheckBox
    sql = "SELECT `Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help` FROM `Control` WHERE `Dialog_`='FinishedForm' AND `Control`='BannerBmp'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    record.StringData(11) = "CheckboxLaunch";
    view.Modify(msiViewModifyReplace, record);
    view.Close();

    // Resize the BodyText and BodyTextRemove controls to be reasonable
    sql = "SELECT `Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help` FROM `Control` WHERE `Dialog_`='FinishedForm' AND `Control`='BodyTextRemove'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    record.IntegerData(7) = 33;
    view.Modify(msiViewModifyReplace, record);
    view.Close();

    sql = "SELECT `Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help` FROM `Control` WHERE `Dialog_`='FinishedForm' AND `Control`='BodyText'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    record.IntegerData(7) = 33;
    view.Modify(msiViewModifyReplace, record);
    view.Close();

    // Insert the new CheckBox control
    sql = "INSERT INTO `Control` (`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help`) VALUES ('FinishedForm', 'CheckboxLaunch', 'CheckBox', '18', '117', '343', '12', '3', 'LAUNCHAPP', '{\\VSI_MS_Sans_Serif13.0_0_0}" + checkboxText + "', 'Line1', '|')";
    view = database.OpenView(sql);
    view.Execute();
    view.Close();



    WScript.Echo("Updating the ControlEvent table...");
    // Modify the Order of the EndDialog event of the FinishedForm to 1
    sql = "SELECT `Dialog_`, `Control_`, `Event`, `Argument`, `Condition`, `Ordering` FROM `ControlEvent` WHERE `Dialog_`='FinishedForm' AND `Event`='EndDialog'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    record.IntegerData(6) = 1;
    view.Modify(msiViewModifyReplace, record);
    view.Close();

    // Insert the Event to launch the application
    sql = "INSERT INTO `ControlEvent` (`Dialog_`, `Control_`, `Event`, `Argument`, `Condition`, `Ordering`) VALUES ('FinishedForm', 'CloseButton', 'DoAction', 'VSDCA_Launch', 'LAUNCHAPP=1', '0')";
    view = database.OpenView(sql);
    view.Execute();
    view.Close();



    WScript.Echo("Updating the CustomAction table...");
    // Insert the custom action to launch the application when finished
    sql = "INSERT INTO `CustomAction` (`Action`, `Type`, `Source`, `Target`) VALUES ('VSDCA_Launch', '210', '" + fileId + "', '')";
    view = database.OpenView(sql);
    view.Execute();
    view.Close();



    if (checkboxChecked)
    {
        WScript.Echo("Updating the Property table...");
        // Set the default value of the CheckBox
        sql = "INSERT INTO `Property` (`Property`, `Value`) VALUES ('LAUNCHAPP', '1')";
        view = database.OpenView(sql);
        view.Execute();
        view.Close();
    }



    database.Commit();
}
catch(e)
{
    WScript.StdErr.WriteLine(e);
    WScript.Quit(1);
}



function FindFileIdentifier(database, fileName)
{
    var sql
    var view
    var record

    // First, try to find the exact file name
    sql = "SELECT `File` FROM `File` WHERE `FileName`='" + fileName + "'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    if (record)
    {
        var value = record.StringData(1);
        view.Close();
        return value;
    }
    view.Close();

    // The file may be in SFN|LFN format.  Look for a filename in this case next
    sql = "SELECT `File`, `FileName` FROM `File`";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    while (record)
    {
        if (StringEndsWith(record.StringData(2), "|" + fileName))
        {
            var value = record.StringData(1);
            view.Close();
            return value;
        }

        record = view.Fetch();
    }
    view.Close();
    
}

function StringEndsWith(str, value)
{
    if (str.length < value.length)
        return false;

    return (str.indexOf(value, str.length - value.length) != -1);
}

Edit the file to show your desired name and the name of the executable, put that file beside your .vdproj Setup project and in the postbuild add following line:

CALL cscript.exe "$(ProjectDir)EnableLaunchApplication.js" "$(BuiltOuputPath)"

Rosanne answered 30/11, 2018 at 9:6 Comment(4)
This is really the proper answer, especially considering that my app utilizes AD logon info, and installer runs as SYSTEM user. Down side is that it doesn't work with a quiet or passive install. Now to tear apart that js and see if I can figure it out.Sandfly
This didn't work for me, I got a 2810 error. Which I was able to discover this SO answer https://mcmap.net/q/104730/-msi-installer-error-2810-interrupts-installation-but-still-let-39-s-it-finish-with-no-problems that led me to use Orca and modify the JS file where it inserts the new CheckboxLaunch control to make it's nextcontrol Line1 instead of CloseButton. Just a TIP to use Orca to debug if you have difficulties, because then the script worked perfect!Definition
On line 71 of the js file change from "CloseButton" to "Line1" and the error 2810 goes away as well as the form shows the checkbox with the Lauch AppName. However on windows 10 it does not seem to start the application. Any suggestions?Disulfiram
JayTee's script was the best solution.Harrison
N
7

Warning: The application will end up running as a high privilege account, which has security and user experience implications.

In Visual Studio 2010 here it is easy...

Step1: Add a new installer class to the application project that you wish to run after install, call it what you like.

Step2: Add the following code to the Installer class you just added replcaing MyApplication.exe with the name of yours.

Public Overrides Sub Commit(ByVal savedState As System.Collections.IDictionary)

    MyBase.Commit(savedState)
    System.Diagnostics.Process.Start(System.IO.Path.GetDirectoryName(Me.Context.Parameters("AssemblyPath")) + "\MyApplication.exe")

End Sub

Compile and go...

Nealey answered 14/3, 2013 at 16:5 Comment(2)
After completing installation of window form MSI setup, my raw files(Design and coding) are also showing at installation location. I've developed this application using visual studio 2010. Setup created successfully, but after installation that MSI this problem is happening. Please help me. Thanks in advance.Sadler
Using this method will launch the new program with Admin privileges which is likely undesirable for security.Bagatelle
F
6

In my case, I was fighting with this for a while and the solution was just there. The solution provided using the custom action directly to the application primary output wasn't good for me, since the install app remains until you leave the main app. So, the issue could be solved using the next approach:

  1. Add an Install class to your project;
  2. In the new class, override the Commit method, like Jarrod talked above.

    System.Diagnostics.Process.Start(System.IO.Path.GetDirectoryName(this.Context.Parameters["AssemblyPath"]) + @"\MyApplication.exe");

  3. Now the trick: go to the "Custom Actions" pane in the Installer project and add the primary output of your project to "Commit" and "Install" folders. Don't change anything in the properties. It will work just like that. As default, it will get the installer class where you inserted the code, in the previous point;
  4. Build your setup package and install it. You should notice that the app will start at the end;
  5. Just close the successful installation warning and go for it.

To know more about this and where I got it, please visit this.

PS.: I made it using VS2017 and Framework 2.0.

Flamethrower answered 26/4, 2017 at 18:29 Comment(15)
Hi @Artur, I've done the same. Issue is that before click on 'ok' button at last screen during installation, application got launched. Application launch only and only when I click on ok button. Please help us.Sadler
Hi @dilipkumar1007. Did you try overriding the OnAfterInstall method? I'm not sure about it. If you got any results, please share. Update: Nop... the behavior is the same...Flamethrower
Hi @dilipkumar1007. After my last answer, I did some searches and I think finally I got the behavior you need. I've tried the fire.eagle aproach below, using the steps in this link here. In my case, I had to remove all the quotes in the PostBuildEvent script. (...)Flamethrower
(...) After that and rebuilding my setup project, installation process will have a last page to "open the application?" which will be done after you terminate the installation app.Flamethrower
Another thing. Using this method you can get rid of those "Custom Actions" and "Setup classes", since the script will do everything for you. Hope this is what you're looking for.Flamethrower
Hi @Artur Gomes, Thanks for your suggestion. But its not working. Its remain same.Sadler
Hi @Artur Gomes, I've same code in PostBuildEvent and didn't remove quotes. But, problem is same. Launched app before click on close button.Sadler
Hi @dilipkumar1007. Did you copied the file name EnableLaunchApplication.jsto the Setup project folder? Please follow the link mentioned at the point 1 of this article. (...)Flamethrower
(...) after that, you have to replace the var filename in the top of the script with the name of your executable file (the file you want to run). Do not forget to fill the PostBuildEvent in the setup project properties with the mentioned string cscript.exe $(ProjectDir)EnableLaunchApplication.js $(BuiltOuputPath). For me, it works without any quotes. With quotes it will give an error building the msi package.Flamethrower
Hi @Artur Gomes Thanks for your kind support. But would like to say that I've add same things whatever suggest by given link and also, bind PostBuildEvent script with quotes because without quotes it raise error code 1.Sadler
Hi @dilipkumar1007. No prob. Strange indeed. In my case, with quotes it gives the code 1 error. Maybe the path string you're using has any spaces or invalid chars. Also, are you spelling correctly the name of your executable at the top of the script? (asking just to be sure, sorry). Can you try build your setup project in a path without any spaces?Flamethrower
Hi @Artur Gomes, I've tried all aspects u've mentioned but didn't get any succeed. Can we converse at [email protected] if you don't have any issue sir?Sadler
Hello @dilipkumar1007. Sorry i'm late answering. Did you tried the options mentioned in this post. They explain some changes on the script in order to workaround a few issues and maybe they have solved the code 1 error.Flamethrower
Hi @Artur , After completing installation of window form MSI setup, my raw files(Design and coding) are also showing at installation location. I've developed this application using visual studio 2010. Setup created successfully, but after installation that MSI this problem is happening. Please help me. Thanks in advance.Sadler
Hi @dilipkumar1007. Have you joined those files to the installation package? Unless you want it, you shouldn't. After all, basically you just need the primary output for your executable projects in the solution and any other extra files you gonna need in the production. Check what you've chosen to add to the installation project.Flamethrower
B
3

Try checking out this blog post: Link

I can't attest for if it works for 2010 or not; I'm still stuck using 2008 until my TFS server gets upgraded. Also, I use WiX for my installers. But, it's just a custom action, so I think it should still be supported.

Hope this helps!

(By the way, while practicing my googling to find this, your question was showing up on the first page of Google for this question.)

Berkelium answered 2/7, 2010 at 20:55 Comment(2)
Heh. It didn't give me that "Someone else has answered" popup at the top. I suggest using sbenderli's solution. Much simpler than the blog post.Berkelium
Thanks! I am thinking of looking into WiX as I have read some good stuff about it.Indemnity
M
1

Adding another answer because none of the previous answers address the checkbox or button question in the original post.

You'd add one of the canned dialogs to your setup project, something like CheckBoxes(A) by right-clicking the Start button in the User Interface view. Right-click the dialog after adding to move it up. You need only one checkbox for the question to ask about running the program so eliminate the others. The default property name is CHECKBOXA1, so add a condition to the custom action that fires off your code CHECKBOXA1=1 meaning it was checked.

Molybdate answered 6/12, 2015 at 21:27 Comment(0)
C
1

first generate installer class. in that override the install method. and paste the following command.

  public override void Install(IDictionary stateSaver) {
        
    System.Diagnostics.Process.Start(System.IO.Path.GetDirectoryName(this.Context.Parameters["AssemblyPath"]) + @"\windowsAgent.exe");   
              
 }
Chromatin answered 29/6, 2021 at 13:28 Comment(0)
H
0

JayTee's script was the best solution. However, the file name in the first line of the script is misspelled.

It should be the following: // EnableLaunchApplication.js <msi-file>

The correct name has to be use for it to work when following the instructions in the following link: customize msi

The sample-link in above link is bad.

Use the following script with the instructions in the above link.

// EnableLaunchApplication.js <msi-file>
// Performs a post-build fixup of an msi to launch a specific file when the install has completed


// Configurable values
var checkboxChecked = true;         // Is the checkbox on the finished dialog checked by default?
var checkboxText = "Launch [ProductName]";  // Text for the checkbox on the finished dialog
var filename = "TranslateFileNamesWin.exe";   // The name of the executable to launch - change this to match the file you want to launch at the end of your setup


// Constant values from Windows Installer
var msiOpenDatabaseModeTransact = 1;

var msiViewModifyInsert         = 1
var msiViewModifyUpdate         = 2
var msiViewModifyAssign         = 3
var msiViewModifyReplace        = 4
var msiViewModifyDelete         = 6



if (WScript.Arguments.Length != 1)
{
    WScript.StdErr.WriteLine(WScript.ScriptName + " file");
    WScript.Quit(1);
}

var filespec = WScript.Arguments(0);
var installer = WScript.CreateObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);

var sql
var view
var record

try
{
    var fileId = FindFileIdentifier(database, filename);
    if (!fileId)
        throw "Unable to find '" + filename + "' in File table";


    WScript.Echo("Updating the Control table...");
    // Modify the Control_Next of BannerBmp control to point to the new CheckBox
    sql = "SELECT `Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help` FROM `Control` WHERE `Dialog_`='FinishedForm' AND `Control`='BannerBmp'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    record.StringData(11) = "CheckboxLaunch";
    view.Modify(msiViewModifyReplace, record);
    view.Close();

    // Resize the BodyText and BodyTextRemove controls to be reasonable
    sql = "SELECT `Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help` FROM `Control` WHERE `Dialog_`='FinishedForm' AND `Control`='BodyTextRemove'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    record.IntegerData(7) = 33;
    view.Modify(msiViewModifyReplace, record);
    view.Close();

    sql = "SELECT `Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help` FROM `Control` WHERE `Dialog_`='FinishedForm' AND `Control`='BodyText'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    record.IntegerData(7) = 33;
    view.Modify(msiViewModifyReplace, record);
    view.Close();

    // Insert the new CheckBox control
    sql = "INSERT INTO `Control` (`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help`) VALUES ('FinishedForm', 'CheckboxLaunch', 'CheckBox', '18', '117', '343', '12', '3', 'LAUNCHAPP', '{\\VSI_MS_Sans_Serif13.0_0_0}" + checkboxText + "', 'Line1', '|')";
    view = database.OpenView(sql);
    view.Execute();
    view.Close();



    WScript.Echo("Updating the ControlEvent table...");
    // Modify the Order of the EndDialog event of the FinishedForm to 1
    sql = "SELECT `Dialog_`, `Control_`, `Event`, `Argument`, `Condition`, `Ordering` FROM `ControlEvent` WHERE `Dialog_`='FinishedForm' AND `Event`='EndDialog'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    record.IntegerData(6) = 1;
    view.Modify(msiViewModifyReplace, record);
    view.Close();

    // Insert the Event to launch the application
    sql = "INSERT INTO `ControlEvent` (`Dialog_`, `Control_`, `Event`, `Argument`, `Condition`, `Ordering`) VALUES ('FinishedForm', 'CloseButton', 'DoAction', 'VSDCA_Launch', 'LAUNCHAPP=1', '0')";
    view = database.OpenView(sql);
    view.Execute();
    view.Close();



    WScript.Echo("Updating the CustomAction table...");
    // Insert the custom action to launch the application when finished
    sql = "INSERT INTO `CustomAction` (`Action`, `Type`, `Source`, `Target`) VALUES ('VSDCA_Launch', '210', '" + fileId + "', '')";
    view = database.OpenView(sql);
    view.Execute();
    view.Close();



    if (checkboxChecked)
    {
        WScript.Echo("Updating the Property table...");
        // Set the default value of the CheckBox
        sql = "INSERT INTO `Property` (`Property`, `Value`) VALUES ('LAUNCHAPP', '1')";
        view = database.OpenView(sql);
        view.Execute();
        view.Close();
    }



    database.Commit();
}
catch(e)
{
    WScript.StdErr.WriteLine(e);
    WScript.Quit(1);
}



function FindFileIdentifier(database, fileName)
{
    var sql
    var view
    var record

    // First, try to find the exact file name
    sql = "SELECT `File` FROM `File` WHERE `FileName`='" + fileName + "'";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    if (record)
    {
        var value = record.StringData(1);
        view.Close();
        return value;
    }
    view.Close();

    // The file may be in SFN|LFN format.  Look for a filename in this case next
    sql = "SELECT `File`, `FileName` FROM `File`";
    view = database.OpenView(sql);
    view.Execute();
    record = view.Fetch();
    while (record)
    {
        if (StringEndsWith(record.StringData(2), "|" + fileName))
        {
            var value = record.StringData(1);
            view.Close();
            return value;
        }

        record = view.Fetch();
    }
    view.Close();
    
}

function StringEndsWith(str, value)
{
    if (str.length < value.length)
        return false;

    return (str.indexOf(value, str.length - value.length) != -1);
}
Harrison answered 2/8, 2022 at 5:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.