Esegui exe dopo l’installazione di msi?

Utilizzo di Visual Studio 2008 per creare un msi per distribuire il mio programma con un progetto di installazione. Ho bisogno di sapere come fare msi eseguire l’exe appena installato. Un’azione personalizzata? Se è così, per favore, spiega dove / come. Grazie.

Questa è una domanda comune. Non lo faccio solo con un’azione personalizzata. L’unico modo che conosco è di modificare il .msi dopo che è stato generato. Eseguo uno script Javascript come evento post-build per fare esattamente questo. Inserisce una nuova finestra di dialogo nella procedura guidata di installazione, con una casella di controllo che dice “Avvia applicazione Foo?”. E poi c’è un’azione personalizzata per eseguire l’app, se la casella è selezionata.

Appare come l’ultima schermata nella sequenza di installazione guidata. Somiglia a questo:

alt text


Questo è lo script che uso per modificare l’MSI:

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

Originariamente l'ho preso dal blog di Aaron Stebner e poi l'ho modificato.

Salva il file Javascript nella directory del progetto (stessa directory che contiene .vdproj), ModifyMsiToEnableLaunchApplication.js . Per ciascun progetto di installazione univoco, è necessario modificare lo script e inserire il nome exe corretto. E quindi, è necessario impostare l'evento post-build nel progetto di installazione per essere questo:

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

Assicurati di digitare correttamente il nome della macro $(BuiltOuputPath) . La parola Ouput è scritta in modo errato da Microsoft e Built non è scritta Build !

Questo dovrebbe farlo.

Vedi anche : questa modifica che non include la casella "Esegui Foo.exe" su UNINSTALL.

Questa sembra essere una soluzione MOLTO più semplice: Visual Studio Installer> Come avviare l’app al termine dell’installazione

OK!!! Ecco il codice (senza le 2 funzioni ausiliarie ‘FindFileIdentifier’ e ‘StringEndsWith’ alla fine – usa invece quelle originali) che ci dà la possibilità di cambiare Ys e Heights dei controlli, insieme all’aggiunta delle condizioni di visibilità di Checkbox Control (vedi i 2 commenti che sono contrassegnati tra “NEW – START” e “NEW – END”):

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

Riguardo al “bug della casella di controllo nascosta” ho capito quanto segue che non è spiegato dalle risposte di Cheeso e Muleskinner sopra:

La modifica dello script (fornita da Muleskinner) posiziona la casella Y della casella di controllo su 201 (credo che il pixel Y in alto per il controllo). Se cambi Y per, diciamo, 151 (per poterlo allineare verticalmente al centro), appare l’errore “improvvisamente”. Il motivo è che c’è un altro controllo nella tabella di controllo della msi, vale a dire il ‘BodyText’ (‘Dialog’ field = ‘FinishedForm’) che la sua Y è impostata a 63 e la sua altezza a 138. Questo è 138 + 63 = 201. Pertanto, se si modifica il valore Y per la casella di controllo, il controllo “BodyText” si sovrappone al controllo appena aggiunto ed è per questo che l’utente deve posizionare il mouse sopra per mostrare la casella di controllo. Se non hai ‘BodyText’ o il numero di caratteri è abbastanza piccolo puoi cambiare (usando l’editor di msi di Orca come faccio io, o modificando lo script sopra) gli Y e gli Heights di questi 2 controlli per essere in grado e acconsentire una diversa posizione Y per la casella di controllo appena aggiunta. Lo stesso vale per il controllo: “BodyTextRemove” nel quale dovremmo modificare il suo valore di altezza (che appare durante la disinstallazione)

Spero che questo aiuti tutti gli utenti che hanno avuto la stessa domanda su questo “bug”

Tuttavia, la sceneggiatura fa davvero un buon lavoro!

Un’altra domanda era come rendere invisibile la casella di controllo durante la procedura di unistallazione. Usando l’editor di Orca msi ho aggiunto le seguenti 2 righe nella tabella ControlCondition di msi:

Riga 1 (quando deve essere mostrato il controllo):

(Finestra di dialogo) FinishedForm (Control) CheckboxLaunch (Action) Show (Condition) REMOVE = “”

Riga 2 (quando il controllo deve essere invisibile):

(Finestra di dialogo) FinishedForm (Control) CheckboxLaunch (Action) Hide (Condition) REMOVE <> “”

PS Sto usando VS 2010, su Windows 7 (x64), ma credo che dovrebbero funzionare anche con le versioni precedenti.

Riguardo a “PostBuildEvent” non riuscito con errore di codice “1” “Errore non specificato” , modificare PostBuildEvent da

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

a

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

Per quanto riguarda il bug della casella di controllo nascosta , puoi modificare la linea 54 dello script per diventare:

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

Sì. Scrivo un’azione personalizzata e la applico alla fine della tabella InstallExecutionSequence