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.
Yes.. I would write a custom action, and stick it at the end of the InstallExecutionSequence table
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:
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)
{
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);
}
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)"
That oughtta do it.
Thanks for that great script, but i got 2 questions/problems with it:
- Is this approach also valid for launching a msi after the setup has completed?
- I can't manage to make any file run after setup has completed, simply nothing happens, in Process Mon I see my .exe is tried to be executed, but a "File Locked with only readers" occurs and the .exe is closed afterwards, is there any solution to this? (i use windows vista/windows 7)
Hi cheeso, Thanks for the scripts.I facing one problem,after successfull install of application,in the final wizard,checkbox gets enabled only after i click on form.it doesnot get enabled automatically @ the end(final wizard).
is ther any thing i m missing.
With regards, mahens
While after using the js, the Checkbox was shown only after i move mouse onto it, why? I'm using VC++, but I don't think it will matter.
another question, how to change to the TargetDIR to run the application? now i do it in the application initialize routine.
Greetings all
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', '|')";
Happy coding
The dialog is created successfully but the program I want to run needs to be run as the currently logged on user and not as the "msisetupuser"...any ideas?
You can use a little-known program to create these install packages that have the the install forms built in. When you create an .SED self-extracting directive file (installer) it provides a way to choose "run the program after installing" as an option for the user.
To get this thing going you just run the Windows XP cmd line program : iexpress Just type that at your cmd line and follow the screens to create your install pkg. I actually used this program to create a self-extracting .exe that when run extracts an msi and starts installing it. Good luck.
I successfully applied this script in my code. Setup is working fine in XP and launching exe but it is not launching exe in Vista or Win7. Where I need to make changes? I also created one new question for this, you can also give an answer here My Question
Hello,
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.
Hello guys,
Is there a way to execute the application even in /quiet or /passive mode?
I logged the difference between a normal installation, a /quiet and a /silent, but I can't see why it is not launching my application. The property LAUNCHAPP is always set to 1. But the CustomAction is not triggered. Is it because there is no FinishedForm to trigger it or something like that?
I also noticed that even when uninstalling, the setup tries to launch my application by returns an error code of:
Action ended 17:08:17: VSDCA_Launch. Return value 1631.
Action ended 17:08:17: FinishedForm. Return value 1.
Jeff
Thanks Cheeso! The jscript works for me. The only problem is that when I load the msi from 'Change' in 'Add or Remove Programs' in Control Panel, check 'Remove XXX', click 'finish' and the last dialog is remove finished that the 'Launch XXX' should not appear on that dialog.
Is there any way to detect whether it's an remove finished dialog and hide the checkbox?