visual-studio 安装msi后运行exe?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1668274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 11:11:11  来源:igfitidea点击:

Run exe after msi installation?

visual-studiovisual-studio-2008windows-installersetup-projectcustom-action

提问by Shawn

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.

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

回答by Cheeso

This is a common question. I don't do it with justa 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 actionto run the app, if the checkbox is checked.

这是一个常见的问题。我不只是通过自定义操作来做到这一点。我知道的唯一方法是在生成后修改 .msi。我运行一个 Javascript 脚本作为构建后事件来做到这一点。它会在安装程序向导中插入一个新对话框,其中有一个复选框,上面写着“Launch Application Foo?”。然后有一个自定义操作来运行应用程序,如果复选框被选中。

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:

这是我用来修改 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.

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

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:

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

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

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

确保$(BuiltOuputPath)正确键入宏的名称。这个词Ouput被微软拼错了,Built没有拼写Build

That oughtta do it.

那应该这样做。

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

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

回答by dlchambers

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

这似乎是一个更简单的解决方案: Visual Studio Installer > How To Launch App at End of Installer

回答by akarkoulis

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'):

行!!!这是代码(最后没有 2 个辅助函数 'FindFileIdentifier' 和 'StringEndsWith' - 使用原始函数代替),它使我们能够更改控件的 Y 和高度,以及添加复选框控件可见性条件(请参阅标记在“新 - 开始”到“新 - 结束”之间的 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);
}

回答by akarkoulis

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

关于“隐藏的复选框错误”,我想出了以下内容,上面 Cheeso 和 Muleskinner 的回答没有解释:

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)

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

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:

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

Row 1 (When control should be shown):

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

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

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

Row 2 (When control should be invisible):

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

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

(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.

PS 我在 Windows 7 (x64) 上使用 VS 2010,但我相信这些也适用于以前的版本。

回答by Muleskinner

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

关于'PostBuildEvent' failed with error code '1' 'Unspecified error'错误,将 PostBuildEvent 从

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

to

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

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

关于隐藏的复选框错误,您可以将脚本的第 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', '|')";

回答by user3349200

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.

此 EnableLaunchApplication.js 脚本有一个小错误,即 Control_Next 选项卡序列不正确。这将在运行安装时导致错误代码 2810。

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

更改以下行以使用“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',
   '|')";

回答by Nestor

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

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

回答by Shangwu

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

参考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);
}