msi安装后运行exe?
Posted
技术标签:
【中文标题】msi安装后运行exe?【英文标题】:Run exe after msi installation? 【发布时间】:2010-12-12 16:50:05 【问题描述】:使用 Visual Studio 2008 创建一个 msi 以使用安装项目部署我的程序。我需要知道如何让 msi 运行它刚刚安装的 exe。自定义动作?如果是这样,请解释在哪里/如何。谢谢。
【问题讨论】:
【参考方案1】:这是一个常见的问题。我不使用只是自定义操作来做到这一点。我知道的唯一方法是在生成 .msi 后对其进行修改。我运行一个 javascript 脚本作为构建后事件来做到这一点。它在安装向导中插入一个新对话框,带有一个显示“Launch Application Foo?”的复选框。然后有一个自定义操作来运行应用程序,如果复选框被选中的话。
它显示为安装向导序列中的最后一个屏幕。看起来像这样:
这是我用来修改 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's blog 获得的,然后对其进行了修改。
将该 Javascript 文件保存到项目目录(与包含 .vdproj 的目录相同),将其命名为 ModifyMsiToEnableLaunchApplication.js
。对于每个独特的安装项目,您需要修改该脚本并将正确的 exe 名称放入其中。然后,您需要将 Setup 项目中的构建后事件设置为:
cscript.exe "$(ProjectDir)ModifyMsiToEnableLaunchApplication.js" "$(BuiltOuputPath)"
请务必正确输入宏 $(BuiltOuputPath)
的名称。 Ouput
这个词被微软拼错了,Built
不是拼写Build
!
应该这样做。
另请参阅:this modification,其中不包括 UNINSTALL 上的“运行 Foo.exe”复选框。
【讨论】:
有点神奇。我不知道 MS 是否将该功能添加到 VS2010 的安装项目中。我认为在 WiX 中也可以这样做,但我从未使用过 Wix,所以这对我有用。 如果界面被隐藏(msi 被静默安装命令推送)这仍然有效吗? 这也增加了卸载过程的屏幕 - 在卸载过程结束时。 请务必正确输入宏 $(BuiltOuputPath) 的名称。我花了大约半个小时才意识到 Ouput 这个词被 Microsoft 拼错了,而 Built 不是拼写 Build! 这很好用,但过了一段时间我开始收到“安装程序在安装此软件包时遇到意外错误。这可能表明此软件包有问题。错误代码是 2810。” (即使安装完成)。登录后,我发现:“调试:错误 2810:在 FinishedForm 对话框中,下一个控件指针不形成循环。CheckboxLaunch 和 CancelButton 都有一个指向 CloseButton 的指针”对此有何帮助?【参考方案2】:这似乎是一个更简单的解决方案: Visual Studio Installer > How To Launch App at End of Installer
【讨论】:
VS2005(也许是 VS2008)需要原始解决方案? 显然这个更简单的解决方案没有显示复选框选项。 在这个解决方案中的问题是设置将在接近完成之前卡住......几乎在 98% 点。当你退出应用程序时它就会结束。【参考方案3】:好的!!!这是代码(最后没有 2 个辅助函数 'FindFileIdentifier' 和 'StringEndsWith' - 使用原始函数),它使我们能够更改控件的 Y 和高度,以及添加复选框控件可见性条件(请参阅标记在“新 - 开始”到“新 - 结束”之间的 2 个 cmets):
// 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);
【讨论】:
这修复了删除后出现的启动应用程序复选框! 它到底修复了什么?可能只是我起得太晚了,但我无法解析你在那里写的内容,@Fidelis。【参考方案4】:关于“隐藏的复选框错误”,我发现了以下未解释的内容 上面 Cheeso 和 Muleskinner 的回答:
脚本的更改(由 Muleskinner 提供)将复选框的 Y 位置设置为 201(我猜是控件的顶部 Y 像素)。如果您将 Y 更改为 151(为了使其垂直居中对齐),则“突然”出现错误。原因是msi的Control表中还有另一个控件,即'BodyText'('Dialog'字段='FinishedForm'),其Y设置为63,高度设置为138。即138 + 63 = 201。因此,如果您更改复选框的 Y 值,“BodyText”控件会与新添加的控件重叠,这就是用户需要将鼠标悬停在上面才能显示复选框的原因。如果您没有“BodyText”或它的字符数足够小,您可以更改(通过像我一样使用 Orca msi 编辑器,或通过更改上面的脚本)这两个控件的 Ys 和 Heights 以便能够和适应新添加的复选框的不同 Y 位置。 这同样适用于控件:“BodyTextRemove”,我们应该在其中再次更改其高度值(在卸载期间出现)
希望这可以帮助所有与我对这个“错误”有相同问题的用户
不过,脚本确实做得很好!
另一个问题是如何在卸载过程中使复选框不可见。 使用 Orca msi 编辑器,我在 msi 的 ControlCondition 表中添加了以下 2 行:
第 1 行(应显示控件时):
(对话框)FinishedForm (控制)复选框启动 (动作)表演 (条件)REMOVE=""
第 2 行(当控件不可见时):
(对话框)FinishedForm (控制)复选框启动 (动作)隐藏 (条件)REMOVE""
附:我在 Windows 7 (x64) 上使用 VS 2010,但我相信这些也应该适用于以前的版本。
【讨论】:
【参考方案5】:此 EnableLaunchApplication.js 脚本有一个小错误,即 Control_Next 选项卡顺序不正确。这将导致运行安装时出现错误代码 2810。
更改以下行以使用“Line1”控件而不是“CloseButton”,以便控件的选项卡顺序全部连接。
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',
'|')";
【讨论】:
【参考方案6】:关于 'PostBuildEvent' failed with error code '1' 'Unspecified error' 错误,将 PostBuildEvent 从
cscript.exe \"$(ProjectDir)ModifyMsiToEnableLaunchApplication.js\" \"$(BuiltOuputPath)\"
到
cscript.exe "$(ProjectDir)ModifyMsiToEnableLaunchApplication.js" "$(BuiltOuputPath)"
关于hidden checkbox bug,您可以将脚本的第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', '|')";
【讨论】:
【参考方案7】:是的.. 我会编写一个自定义操作,并将其粘贴在 InstallExecutionSequence 表的末尾
【讨论】:
我没有看到 InstallExecutionSequence 表?在自定义操作下,我看到了安装、提交、回滚和卸载。自定义操作会指向主程序的输出吗? InstallExecutionSequence 表在 MSI 内部,不通过 Visual Studio 公开。您将需要像 Orca 这样的 MSI 编辑器来编辑它。手动编辑 MSI 并非易事。 我同意 - 手动编辑 MSI 并不容易。使用 Orca 并非易事。您可以使用我在下面提供的脚本来自动执行任务。快速简单,经过测试和验证。 我知道问题是针对 VS2008 的,但对于 VS2010,创建自定义提交操作看起来相当容易。 msdn.microsoft.com/en-us/library/d9k65z2d.aspx【参考方案8】:参考user3349200的建议,这里是一个完整的JS脚本,没有设置错误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);
【讨论】:
我正在尝试使用这个脚本,但它似乎仍然存在运行复选框在卸载阶段仍然出现的问题。此外,我似乎遇到了一个问题,即安装程序完成后我的应用程序实际上并没有运行。 更正,我正在运行我的应用程序(我只是遇到了一个意外错误),但是在卸载阶段仍然会出现复选框。 对我来说它的工作,我可以看到复选框,但安装后应用程序不会自动启动以上是关于msi安装后运行exe?的主要内容,如果未能解决你的问题,请参考以下文章
如何创建 MFC 应用程序来运行 setup.exe(msi 安装程序)?