wake-up-neo.com

Exe nach MSI-Installation ausführen?

Verwenden von Visual Studio 2008 zum Erstellen einer MSI-Datei zum Bereitstellen meines Programms mit einem Setup-Projekt. Ich muss wissen, wie man das msi die exe laufen lässt, die es gerade anbrachte. Eine benutzerdefinierte Aktion? Wenn ja, bitte erläutern Sie wo/wie. Vielen Dank.

54
Shawn

Dies ist eine häufige Frage. Ich mache es nicht mit nur einer benutzerdefinierten Aktion. Die einzige Möglichkeit, die ich kenne, besteht darin, die .msi-Datei zu ändern, nachdem sie generiert wurde. Ich führe ein Javascript-Skript als Post-Build-Ereignis aus, um genau das zu tun. Es wird ein neues Dialogfeld in den Installationsassistenten eingefügt, dessen Kontrollkästchen "Launch Application Foo?" Lautet. Und dann es gibt eine benutzerdefinierte Aktion, um die App auszuführen, wenn das Kontrollkästchen aktiviert ist.

Es erscheint als letzter Bildschirm in der Sequenz install Wizard. Sieht so aus:

alt text


Dies ist das Skript, mit dem ich die MSI ändere:

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

Ich habe das ursprünglich von Aaron Stebners Blog bekommen und es dann modifiziert.

Speichern Sie diese Javascript-Datei im Projektverzeichnis (das gleiche Verzeichnis wie das mit .vdproj) und nennen Sie sie ModifyMsiToEnableLaunchApplication.js. Für jedes einzelne Setup-Projekt müssen Sie dieses Skript ändern und den richtigen Exe-Namen eingeben. Anschließend müssen Sie das Post-Build-Ereignis im Setup-Projekt wie folgt festlegen:

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

Achten Sie darauf, den Namen des Makros $(BuiltOuputPath) korrekt einzugeben. Das Wort Ouput wird von Microsoft falsch geschrieben, und Built wird nicht geschrieben Build!

Das sollte es tun.

Siehe auch : diese Änderung ohne das Kontrollkästchen "run Foo.exe" bei UNINSTALL.

78
Cheeso
19
dlchambers

IN ORDNUNG!!! Hier ist der Code (ohne die beiden Zusatzfunktionen 'FindFileIdentifier' und 'StringEndsWith' am Ende - verwenden Sie stattdessen die Originalfunktionen), mit dem wir die Ys und Heights der Steuerelemente ändern und die Sichtbarkeitsbedingungen für Kontrollkästchen hinzufügen können (Siehe die 2 Kommentare, die zwischen "NEW - START" und "NEW - END" markiert sind):


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

In Bezug auf den "versteckten Checkbox-Bug" habe ich Folgendes herausgefunden, was durch die obigen Antworten von Cheeso und Muleskinner nicht erklärt wird:

Durch die Änderung des Skripts (bereitgestellt von Muleskinner) wird die Y-Position des Kontrollkästchens auf 201 gesetzt (ich schätze, oberes Y-Pixel für das Steuerelement). Wenn Sie Y in 151 ändern (um es in der Mitte vertikal auszurichten), erscheint der Fehler "plötzlich". Der Grund dafür ist, dass es ein anderes Steuerelement in der Steuertabelle der msi gibt, nämlich den 'BodyText' ('Dialog' Feld = 'FinishedForm'), dessen Y auf 63 und dessen Höhe auf 138 gesetzt ist. Das ist 138+ 63 = 201. Wenn Sie den Y-Wert für das Kontrollkästchen ändern, überlappt das Steuerelement 'BodyText' das neu hinzugefügte Steuerelement. Daher muss der Benutzer die Maus darüber bewegen, um das Kontrollkästchen anzuzeigen. Wenn Sie keinen 'BodyText' haben oder die Anzahl der Zeichen klein genug ist, können Sie (indem Sie wie ich den Orca msi-Editor verwenden oder das obige Skript ändern) die Ys und Heights dieser 2 Steuerelemente ändern, um in der Lage und unterzubringen eine andere Y-Position für das neu hinzugefügte Kontrollkästchen. Gleiches gilt für das Steuerelement: 'BodyTextRemove', in dem wir seinen Höhenwert (der während der Deinstallation erscheint) erneut ändern sollten.

Hoffe, dass dies allen Benutzern hilft, die die gleiche Frage hatten, die ich zu diesem "Bug" hatte

Trotzdem macht das Drehbuch einen wirklich guten Job!

Eine weitere Frage war, wie die Checkbox während des Deinstallationsvorgangs unsichtbar gemacht werden kann. Mit dem Orca msi-Editor habe ich die folgenden 2 Zeilen in die ControlCondition-Tabelle der msi eingefügt:

Zeile 1 (wenn Kontrolle angezeigt werden soll):

(Dialog) FinishedForm (Control) CheckboxLaunch (Aktion) Show (Bedingung) REMOVE = ""

Zeile 2 (wenn die Kontrolle unsichtbar sein soll):

(Dialog) FinishedForm (Control) CheckboxLaunch (Aktion) Hide (Bedingung) REMOVE <> ""

P.S. Ich verwende VS 2010 unter Windows 7 (x64), aber ich glaube, dass dies auch mit früheren Versionen funktionieren sollte.

8
akarkoulis

Ändern Sie in Bezug auf den Fehler 'PostBuildEvent' ist mit Fehlercode '1' 'Nicht angegebener Fehler' fehlgeschlagen das PostBuildEvent von

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

zu

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

In Bezug auf das versteckte Kontrollkästchen können Sie Zeile 54 des Skripts bearbeiten, um:

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

Ja. Ich würde eine benutzerdefinierte Aktion schreiben und am Ende der InstallExecutionSequence-Tabelle einfügen

2
Nestor