56

Using Visual Studio 2008 to create an msi to deploy my program with a setup project. I need to know how to make the msi run the exe it just installed. A custom action? If so please explain where/how. Thanks.

Shawn
  • 2,356
  • 6
  • 48
  • 82

8 Answers8

81

This is a common question. I don't do it with just a custom action. The only way I know, is to modify the .msi after it has been generated. I run a Javascript script as a post-build event to do exactly that. It inserts a new dialog in the installer wizard, with a checkbox that says "Launch Application Foo?". And then there is a custom action to run the app, if the checkbox is checked.

It appears as the last screen in the install Wizard sequence. Looks like this:

alt text


This is the script I use to modify the MSI:

// 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 = "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();

        // 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', '9', '201', '343', '12', '3', 'LAUNCHAPP', '{\\VSI_MS_Sans_Serif13.0_0_0}" + checkboxText + "', 'CloseButton', '|')";
        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)
{
        // First, try to find the exact file name
        var sql = "SELECT `File` FROM `File` WHERE `FileName`='" + fileName + "'";
        var view = database.OpenView(sql);
        view.Execute();
        var 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);
}

I originally got this from Aaron Stebner's blog, and then modified it.

Save that Javascript file to the project directory (same dir as contains .vdproj), name it ModifyMsiToEnableLaunchApplication.js . For each unique setup project, you need to modify that script and put the proper exe name into it. And then, you need to set the post-build event in the Setup project to be this:

cscript.exe "$(ProjectDir)ModifyMsiToEnableLaunchApplication.js" "$(BuiltOuputPath)"

Be sure to type the name of the macro $(BuiltOuputPath) correctly. The word Ouput is misspelled by Microsoft, and Built is not spelled Build !

That oughtta do it.

See also: this modification which does not include the "run Foo.exe" checkbox on UNINSTALL.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Cheeso
  • 189,189
  • 101
  • 473
  • 713
  • 1
    like a little bit of magic. I don't know if MS added the capability into the setup project for VS2010. I think it's also possible to do this in WiX, but I've never used Wix, so this works for me. – Cheeso Nov 05 '09 at 19:22
  • 2
    If the interface is hidden (msi being pushed with silent install commands) will this still work? – Shawn Nov 06 '09 at 15:54
  • 1
    Sure, it will still work. Just keep the checkboxChecked var as true in the script. – Cheeso Nov 06 '09 at 16:56
  • 1
    I'm getting an Error: 'PostBuildEvent' failed with error code '1' 'Unspecified error' – Shawn Nov 19 '09 at 16:17
  • 1
    you have to look in the build output log, to discern the problem. First things to check: did you modify the name of the EXE at the top of the Javascript file? did you name the file properly and does the post-build event reference the proper name? – Cheeso Nov 19 '09 at 18:12
  • Not enough storage is available? – Shawn Nov 20 '09 at 14:35
  • And yes I've named the file correctly and have modified it to the proper exe. – Shawn Nov 20 '09 at 14:36
  • @Cheeso: Yes, one can do this with Wix. – Brian Jun 08 '10 at 13:10
  • 1
    Credits to the original post.. http://blogs.msdn.com/b/astebner/archive/2006/08/12/696833.aspx – Trainee4Life Aug 04 '10 at 11:41
  • Will this work with .bat? If so then i think i have the answer to two of my ongoing threads – Roast Sep 09 '10 at 18:09
  • Having issues when I have some self registering COM Dll's in my setup. They just don't register in time and my app can't use these dll's; the app works perfectly when launched again from start menu. Any help? – Trainee4Life Dec 09 '10 at 16:37
  • @Trainee4Life, sounds like you need to ask a new question. – Cheeso Dec 09 '10 at 20:33
  • @Cheeso: New question posted: http://stackoverflow.com/questions/4422828/com-dll-exception-in-application-when-it-auto-launches-after-setup-finishes-insta – Trainee4Life Dec 12 '10 at 17:09
  • I implemented this in a msi and it works perfectly. But when calling the msi as silent install: "msiexec /i setupFile.msi /qn", the application does not start after installation. And checkbox is set to true in the js. Any ideas? – Amc_rtty Sep 18 '11 at 07:40
  • 4
    Also this adds the screen for uninstallation process as well - at the end of uninstall process. – Amc_rtty Sep 21 '11 at 20:47
  • Thank you, this worked perfectly. It also ensures (somehow, the Type field in CustomActions?) that the exe is NOT run as elevated, which is a problem with other solutions I found. Email me or DM me @WindowTabs if you would like a free license, it's the least I can do. – Maurice Flanagan Dec 30 '11 at 23:46
  • 2
    Be sure to type the name of the macro $(BuiltOuputPath) correctly. It took me about half an hour to realize that the word Ouput is misspelled by Microsoft, and Built is not spelled Build ! – Mark Lakata Feb 17 '12 at 20:35
  • Yeah I was looking to do this myself and I used this guy's script which I found on his blog. I ran into an issue with it, but I resolved it. If anyone else is having issues please check here: http://stackoverflow.com/questions/11403627/vb-net-2005-launch-application-after-install – John Jul 11 '12 at 18:02
  • My code builds and installs . But i do not see any windows to launch app (VS 2010) at the end any help? – confusedMind Oct 29 '13 at 00:26
  • Can someone guide me how to hide 'Lanch application'checkbox in END screen of setup. I want to run post deployment scripts always – saTech Jan 10 '14 at 19:53
  • I know this is an old thread but I just want to ask if this can be edited to put two checkbox and run another installer? – ViFer Jun 16 '14 at 12:58
  • How to execute this script if we want to run app as administrator (as this solution doesn't work if we change app.manifest in our source code)? Thnx – emirkljucanin Nov 11 '14 at 14:10
  • @MarkLakata THANK YOU! In a million years I never would've noticed that on my own – McFixit Feb 12 '16 at 22:48
  • 1
    @McFixit - I edited the answer to make this horrible gotcha more visible – Mark Lakata Feb 12 '16 at 23:04
  • To avoid : "Microsoft JScript compilation error: Invalid character" and subsequent "'1' 'Unspecified error'" The file encoding for ModifyMsiToEnableLaunchApplication.js **must be ANSI**. (NOT UTF-8 Nor Unicode). – antonio Mar 08 '16 at 04:50
  • One more: After run the application, the current directory can be a different of FOO.exe.AssemblyName.CodeBase. – antonio Mar 08 '16 at 05:50
  • I can see the checkbox and it is checked but the program doesn't launch. I checked the EXE filename, even copied it from the bin folder. What may be the reason for the program not to run after installation? – Liran Friedman Oct 24 '17 at 08:25
  • Could it be because my app requires admin permission? ``? – Liran Friedman Oct 24 '17 at 08:34
  • 2
    This worked perfectly, but after some time i started getting "The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2810." (even though the installation finishes). After logging, i found this: "DEBUG: Error 2810: On the dialog FinishedForm the next control pointers do not form a cycle.There is a pointer from both CheckboxLaunch and CancelButton to CloseButton" Any help with this? – Filip5991 Dec 24 '19 at 10:59
  • I'm trying to modify and msi file genereated by jpackage but getting `[object error]` on the first `view = database.OpenView(sql);` line. – hrzafer Dec 17 '21 at 22:05
  • cscript.exe is your setup solution name correct? – Satyam Mishra Jan 27 '22 at 14:19
  • I am using sqlite for my wpf application , and I tried to use the above steps to run the exe after the installation , but my db files are generating in the msi path, Is this something we are manipulating in js file regarding the sqllite files ? Note I have checked the db creation path , it shows the exe path still it is creating in msi file path as well @Cheeso – Satyam Mishra Jan 30 '22 at 16:47
20

This seems to be a MUCH simpler solution: Visual Studio Installer > How To Launch App at End of Installer

Community
  • 1
  • 1
dlchambers
  • 3,511
  • 3
  • 29
  • 34
12

OK!!! Here is the code (without the 2 auxiliary functions 'FindFileIdentifier' & 'StringEndsWith' in the end - use the original ones instead) which gives us the ability to change the Ys and Heights of the controls, along with adding the Checkbox Control Visibility Conditions (see the 2 comments that are marked between 'NEW - START' to 'NEW - END'):


// EnableLaunchApplication.js 
// 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 = "*.exe";                     // The name of the executable to launch - change * to match the file name 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();

        // NEW - START
        // Insert the new CheckBox control
        // I changed the value for Y below from 201 to 191 in order to make the checkbox more obvious to the user's eye. In order to do so, and avoid the controls 'BodyText' & 'BodyTextRemove' in the same form to
        // overlap the checkbox, I added yet 2 more sql statements that change the values of the heights for the 'BodyText' & 'BodyTextRemove' from 138 to 128. This way I can play around with the values without using
        // the Orca msi editor.
        var CheckBoxY = 191; //This was initially set to 201
        var NewHeight = 128; //This was initially set to 138
        sql = "INSERT INTO `Control` (`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help`) VALUES ('FinishedForm', 'CheckboxLaunch', 'CheckBox', '9', '" + CheckBoxY + "', '343', '12', '3', 'LAUNCHAPP', '{\\VSI_MS_Sans_Serif13.0_0_0}" + checkboxText + "', 'CloseButton', '|')";
        view = database.OpenView(sql);
        view.Execute();
        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) = NewHeight;
        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` = 'BodyTextRemove'";
        view = database.OpenView(sql);
        view.Execute();
        record = view.Fetch();
        record.IntegerData(7) = NewHeight;
        view.Modify(msiViewModifyReplace, record);
        view.Close();
        // NEW - END

        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();
        }


        // NEW - START
        WScript.Echo("Updating the ControlCondition table...");
        // Insert the conditions where the Launch Application Checkbox appears
        sql = "INSERT INTO `ControlCondition` (`Dialog_`, `Control_`, `Action`, `Condition`) VALUES ('FinishedForm', 'CheckboxLaunch', 'Show', 'REMOVE=\"\"')";
        view = database.OpenView(sql);
        view.Execute();
        view.Close();

        sql = "INSERT INTO `ControlCondition` (`Dialog_`, `Control_`, `Action`, `Condition`) VALUES ('FinishedForm', 'CheckboxLaunch', 'Hide', 'REMOVE<>\"\"')";
        view = database.OpenView(sql);
        view.Execute();
        view.Close();
        //NEW - END


        database.Commit();
}
catch(e)
{
        WScript.StdErr.WriteLine(e);
        WScript.Quit(1);
}
Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164
akarkoulis
  • 201
  • 3
  • 3
8

Concerning the "hidden checkbox bug" I figured out the following which is not explained by Cheeso's and Muleskinner's answers above:

The change of the script (provided by Muleskinner) places the Y position of the checkbox to 201 (I guess top Y pixel for the control). If you change Y to, say, 151 (in order to kind of align it in the center vertically), the bug "suddenly" appears. The reason for that is that there is another control in the Control table of the msi, namely the 'BodyText' ('Dialog' field = 'FinishedForm') which its Y is set to 63 and its height to 138. That is 138 + 63 = 201. Therefore, if you change the Y value for the checkbox, the 'BodyText' control overlaps the newly added control and that's why the user needs to put their mouse over in order to show the checkbox. If you have no 'BodyText' or its number of characters is small enough you could change (by using Orca msi editor as I do, or by altering the script above) the Ys and Heights of these 2 controls in order to be able and accomodate a different Y position for the newly added checkbox. The same applies for the control: 'BodyTextRemove' in which again we should alter its height value (which appears during uninstall)

Hope that this helps all the users that had the same question as I had about this "bug"

Nevertheless, the script does a really good job!

Another question was how to make invisible the Checkbox during unistallation procedure. Using the Orca msi editor I added the following 2 rows in the ControlCondition table of the msi:

Row 1 (When control should be shown):

(Dialog)FinishedForm (Control)CheckboxLaunch (Action)Show (Condition)REMOVE=""

Row 2 (When control should be invisible):

(Dialog)FinishedForm (Control)CheckboxLaunch (Action)Hide (Condition)REMOVE<>""

P.S. I am using VS 2010, on windows 7 (x64), but I believe these should work with previous versions too.

akarkoulis
  • 201
  • 3
  • 3
6

This EnableLaunchApplication.js script has a small bug where the Control_Next tab sequence is incorrect. This will cause a error code 2810 when running the installation.

Change the following line to use the "Line1" control instead of "CloseButton" so that the tab sequence of the controls are all connected.

sql = "INSERT INTO `Control` (`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`,
   `Control_Next`, `Help`) VALUES ('FinishedForm', 'CheckboxLaunch',
   'CheckBox', '9', '201', '343', '12', '3', 'LAUNCHAPP',
   '{\\VSI_MS_Sans_Serif13.0_0_0}" + checkboxText + "', 'CloseButton',
   '|')";
Nick
  • 645
  • 5
  • 11
user3349200
  • 93
  • 1
  • 5
4

Regarding the 'PostBuildEvent' failed with error code '1' 'Unspecified error' error, change the PostBuildEvent from

cscript.exe \"$(ProjectDir)ModifyMsiToEnableLaunchApplication.js\" \"$(BuiltOuputPath)\"

to

cscript.exe "$(ProjectDir)ModifyMsiToEnableLaunchApplication.js" "$(BuiltOuputPath)"

Regarding the hidden checkbox bug you can edit line 54 of the script to become:

sql = "INSERT INTO `Control` (`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help`) VALUES ('FinishedForm', 'CheckboxLaunch', 'CheckBox', '9', '201', '343', '12', '3', 'LAUNCHAPP', '{\\VSI_MS_Sans_Serif13.0_0_0}" + checkboxText + "', 'CloseButton', '|')";
Yan Sklyarenko
  • 31,557
  • 24
  • 104
  • 139
Muleskinner
  • 14,150
  • 19
  • 58
  • 79
3

After referred user3349200's suggestion, here is a completed JS script without setup error 2810.

// post-build-script: CALL cscript.exe "$(ProjectDir)EnableLaunchApplication.js" "$(BuiltOuputPath)"
// 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 = "YourApp.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);
}
Shangwu
  • 1,440
  • 12
  • 12
  • I'm trying to use this script, but it still seems to have the issue where the run checkbox still appears during the uninstall phase. Also, I seem to be having a problem where my application doesn't actually run once the installer is finished. – Ryan Caskey May 29 '20 at 03:56
  • Correction, I is running my application (I just had an unexpected error), however the checkbox still does appear during the uninstall phase. – Ryan Caskey May 29 '20 at 04:15
  • for me it's work , I am able to see the checkbox but app is not auto launched after installation – Satyam Mishra Jan 28 '22 at 08:04
  • This script saved my day. – Haider Ali Wajihi Jun 23 '22 at 12:42
2

Yes.. I would write a custom action, and stick it at the end of the InstallExecutionSequence table

Nestor
  • 13,706
  • 11
  • 78
  • 119
  • 1
    I don't see an InstallExecutionSequence table? Under custom actions I see Install, Commit, Rollback and uninstall. Would the custom action point to the output of the main program? – Shawn Nov 04 '09 at 15:02
  • 1
    The InstallExecutionSequence table is internal to the MSI and not exposed via Visual Studio. You will need an MSI editor like Orca to edit it. Hand-editing MSIs is not a trivial task. – Dour High Arch Nov 05 '09 at 18:08
  • 1
    I agree - hand-editing MSIs is not easy. Not is it easy or trivial using Orca. You can use the script I provided below to automate the task. Quick and easy, tested and proven. – Cheeso Nov 05 '09 at 19:12
  • I know the question is for VS2008 but for VS2010 it looks fairly easy to create a custom Commit action. http://msdn.microsoft.com/en-us/library/d9k65z2d.aspx – Daniel Ballinger Feb 08 '11 at 20:56