Executar exe após a instalação do msi?

Usando o Visual Studio 2008 para criar um msi para implantar meu programa com um projeto de instalação. Eu preciso saber como fazer o msi executar o exe que acabou de instalar. Uma ação personalizada? Se sim, por favor explique onde / como. Obrigado.

Essa é uma pergunta comum. Eu não faço isso apenas com uma ação personalizada. A única maneira que conheço é modificar o .msi depois de gerado. Eu executo um script Javascript como um evento de pós-compilation para fazer exatamente isso. Ele insere um novo diálogo no assistente do instalador, com uma checkbox de seleção que diz “Iniciar aplicativo Foo?”. E, em seguida, há uma ação personalizada para executar o aplicativo, se a checkbox de seleção estiver marcada.

Aparece como a última canvas na seqüência do Assistente de instalação. Se parece com isso:

texto alternativo


Este é o script que eu uso para modificar o 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); } 

Eu originalmente consegui isso no blog de Aaron Stebner , e depois o modifiquei.

Salve esse arquivo Javascript no diretório do projeto (mesmo diretório que contém .vdproj), ModifyMsiToEnableLaunchApplication.js . Para cada projeto de configuração exclusivo, você precisa modificar esse script e colocar o nome exe adequado nele. E, em seguida, você precisa definir o evento de pós-compilation no projeto de instalação para ser isso:

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

Certifique-se de digitar o nome da macro $(BuiltOuputPath) corretamente. A palavra Ouput foi escrita incorretamente pela Microsoft e Built não está escrito Build !

Isso deve ser feito.

Veja também : esta modificação que não inclui a checkbox de seleção "run Foo.exe" no UNINSTALL.

Esta parece ser uma solução MUITO mais simples: Visual Studio Installer> Como iniciar o aplicativo no final do instalador

ESTÁ BEM!!! Aqui está o código (sem as 2 funções auxiliares ‘FindFileIdentifier’ e ‘StringEndsWith’ no final – use as originais em seu lugar) que nos permite alterar os Ys e Heights dos controles, além de adicionar as Condições de Visibilidade de Controle da Caixa de Seleção. (veja os 2 comentários que estão marcados entre ‘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); } 

Com relação ao “bug da checkbox de seleção oculto”, descobri o seguinte, que não é explicado pelas respostas de Cheeso e Muleskinner acima:

A mudança do script (fornecida por Muleskinner) coloca a posição Y da checkbox de seleção para 201 (eu acho que o pixel Y superior para o controle). Se você alterar Y para, digamos, 151 (para alinhar no centro verticalmente), o erro “repentinamente” aparece. A razão para isso é que há outro controle na tabela de controle do msi, ou seja, o ‘BodyText’ (‘Dialog’ campo = ‘FinishedForm’), que seu Y é definido como 63 e sua altura para 138. Isso é 138 + 63 = 201. Portanto, se você alterar o valor Y da checkbox de seleção, o controle ‘BodyText’ se sobrepõe ao controle recém-adicionado e é por isso que o usuário precisa colocar o mouse para mostrar a checkbox de seleção. Se você não tem ‘BodyText’ ou seu número de caracteres é pequeno o suficiente, você pode mudar (usando o editor orca msi como eu faço, ou alterando o script acima) os Ys e Heights destes 2 controles para poder e acomodar uma posição Y diferente para a checkbox de seleção recém-adicionada. O mesmo se aplica ao controle: ‘BodyTextRemove’, no qual novamente devemos alterar seu valor de altura (que aparece durante a desinstalação)

Espero que isso ajude todos os usuários que tiveram a mesma pergunta que eu tinha sobre esse “bug”

No entanto, o roteiro faz um ótimo trabalho!

Outra questão era como tornar invisível o Checkbox durante o procedimento de desinstalação. Usando o editor orca msi, adicionei as duas linhas a seguir na tabela ControlCondition do msi:

Linha 1 (quando o controle deve ser mostrado):

(Dialog) FinishedForm (Control) CheckboxLaunch (Ação) Mostrar (Condição) REMOVE = “”

Linha 2 (quando o controle deve ser invisível):

(Dialog) FinishedForm (Control) CheckboxLaunch (Ação) Esconder (Condição) REMOVE <> “”

PS Eu estou usando o VS 2010, no Windows 7 (x64), mas acredito que estes devem funcionar com versões anteriores também.

Em relação ao erro ‘PostBuildEvent’ falhou com o código de erro ‘1’ ‘Erro não especificado’ , altere o PostBuildEvent de

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

para

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

Em relação ao bug escondido na checkbox de seleção, você pode editar a linha 54 do script para se tornar:

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

Sim. Gostaria de escrever uma ação personalizada e colocá-la no final da tabela InstallExecutionSequence.