MSI安装后运行exe文件?

使用Visual Studio 2008创build一个msi来部署安装项目的程序。 我需要知道如何使msi运行刚才安装的exe文件。 自定义操作? 如果是这样,请解释在哪里/如何。 谢谢。

这是一个常见的问题。 我不只是做一个自定义的行动。 我知道的唯一方法是在生成后修改.msi。 我运行一个JavaScript脚本作为后生成事件来做到这一点。 它会在安装向导中插入一个新的对话框,其中有一个checkbox,显示“启动应用程序Foo?”。 然后有一个自定义操作来运行应用程序,如果checkbox被选中。

它显示为安装向导序列中的最后一个屏幕。 看起来像这样:

替代文字


这是我用来修改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); } 

我最初从Aaron Stebner的博客中得到了这个,然后对它进行了修改。

将该文件保存到项目目录(与包含.vdproj相同的目录),将其命名为ModifyMsiToEnableLaunchApplication.js 。 对于每个独特的安装项目,您需要修改该脚本并将正确的exe名称放入其中。 然后,您需要将安装项目中的生成后事件设置为:

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

确保正确inputmacros$(BuiltOuputPath)的名称。 单词Ouput被微软拼写错误, Built不拼拼写!

这应该做到这一点。

另请参阅 : 此修改不包括UNINSTALL上的“运行Foo.exe”checkbox。

这似乎是一个更简单的解决scheme: Visual Studio安装程序>如何在安装程序结束时启动应用程序

好!!! 这里是代码(最后没有2个辅助函数“FindFileIdentifier”和“StringEndsWith” – 使用原来的),这使我们能够改变控件的Y和高度,同时添加checkbox控件可见性条件(请参阅在“新 – 开始”到“新 – 结束”之间标注的2条评论):

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

关于“隐藏的checkbox错误”,我想出了以下这些,不是由Cheeso和Muleskinner的答案解释的:

脚本的变化(由Muleskinner提供)将checkbox的Y位置设置为201(我猜控制顶部Y像素)。 如果将Y改为151(为了使其垂直居中),就会出现“突然出现”的错误。 原因是msi的Control表中有另外一个控件,也就是“BodyText”('Dialog'field ='FinishedForm'),它的Y被设置为63,高度为138.这就是138 +因此,如果您更改checkbox的Y值,则“BodyText”控件将与新添加的控件重叠,这就是为什么用户需要将鼠标hover以显示checkbox。 如果您没有“BodyText”或其字符数足够小,您可以改变(通过使用Orca msi编辑器,或通过更改上面的脚本)这两个控件的Y和高度,以便能够和容纳新添加的checkbox的Y位置不同。 这同样适用于控制:“BodyTextRemove”,我们应该再次改变其高度值(在卸载时出现)

希望这有助于所有与我有关于这个“bug”的问题的用户,

不过,这个剧本做得非常好!

另一个问题是如何在unistallation过程中使checkbox不可见。 使用Orca msi编辑器,我在msi的ControlCondition表中添加了以下两行:

第1行(当显示控件时):

(Dialog)FinishedForm(Control)CheckboxLaunch(Action)Show(Condition)REMOVE =“”

第2行(当控制不可见时):

(对话框)FinishedForm(控件)CheckboxLaunch(动作)隐藏(条件)REMOVE <>“”

PS我正在使用VS 2010,在Windows 7(64位),但我相信这些应该与以前的版本也工作。

关于'PostBuildEvent'失败,错误代码为'1''未指定的错误'错误,请将PostBuildEvent从

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

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

关于隐藏的checkbox错误,您可以编辑脚本的第54行成为:

 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', '|')"; 

是的..我会写一个自定义操作,并将其粘贴在InstallExecutionSequence表的末尾

Interesting Posts