安装msi后运行exe?

Sha*_*awn 54 windows-installer custom-action setup-project visual-studio-2008 visual-studio

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

Che*_*eso 78

这是一个常见的问题.我不会使用自定义操作.我知道的唯一方法是在生成后修改.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);
}
Run Code Online (Sandbox Code Playgroud)

我最初是从Aaron Stebner的博客那里得到,然后修改了它.

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

cscript.exe "$(ProjectDir)ModifyMsiToEnableLaunchApplication.js" "$(BuiltOuputPath)"
Run Code Online (Sandbox Code Playgroud)

请务必$(BuiltOuputPath)正确键入宏的名称.这个词Ouput是由微软拼写错误的,并Built没有拼写Build!

那应该这样做.

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

  • 此外,这也会在卸载过程结束时添加卸载过程的屏幕. (4认同)
  • 哇Cheeso ...很好的剧本 (3认同)
  • 如果界面被隐藏(使用静默安装命令推送msi),这仍然有用吗? (2认同)
  • 请务必正确键入宏$(BuiltOuputPath)的名称.我花了大约半个小时才意识到Ouput这个词被微软拼错了,并且Built不是拼写Build! (2认同)
  • 这工作完美,但一段时间后我开始收到“安装程序在安装此软件包时遇到意外错误。这可能表明此软件包有问题。错误代码是 2810。” (即使安装完成)。记录后,我发现:“DEBUG:错误2810:在对话框FinishedForm上,下一个控制指针不形成循环。CheckboxLaunch和CancelButton都有一个指向CloseButton的指针”对此有帮助吗? (2认同)

dlc*_*ers 19

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

  • 在这个解决方案中,问题是设置将在即将完成之前卡住......几乎在 98% 点。当您退出应用程序时它就完成了。 (2认同)

aka*_*lis 11

好!!!这是代码(最后没有2个辅助函数'FindFileIdentifier'和'StringEndsWith' - 使用原始代码),这使我们能够更改控件的Y和高度,以及添加复选框控件可见性条件(参见"NEW - START"到"NEW - END"之间标记的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);
}
Run Code Online (Sandbox Code Playgroud)


aka*_*lis 8

关于"隐藏的复选框错误",我想出了以下内容,Cheeso和Muleskinner的答案没有解释如下:

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

希望这能帮助所有与我有同样问题的用户关于这个"bug"

不过,脚本做得非常好!

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

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

(对话框)FinishedForm(控制)CheckboxLaunch(动作)显示(条件)REMOVE =""

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

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

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


小智 6

此 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',
   '|')";
Run Code Online (Sandbox Code Playgroud)