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.
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:
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.
Dies scheint eine VIEL einfachere Lösung zu sein: Visual Studio Installer> So starten Sie die App am Ende des Installationsprogramms
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);
}
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.
Ä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', '|')";
Ja. Ich würde eine benutzerdefinierte Aktion schreiben und am Ende der InstallExecutionSequence-Tabelle einfügen