51

Sử dụng Visual Studio 2008 để tạo một msi để triển khai chương trình của tôi bằng một dự án thiết lập. Tôi cần phải biết làm thế nào để làm cho các msi chạy exe nó chỉ cần cài đặt. Một hành động tùy chỉnh? Nếu vậy hãy giải thích ở đâu/như thế nào. Cảm ơn.Chạy exe sau khi cài đặt msi?

Trả lời

75

Đây là một câu hỏi phổ biến. Tôi không làm điều đó với chỉ một hành động tùy chỉnh. Cách duy nhất tôi biết, là sửa đổi .msi sau khi nó đã được tạo ra. Tôi chạy một kịch bản Javascript như là một sự kiện sau xây dựng để làm chính xác điều đó. Nó chèn một hộp thoại mới trong trình hướng dẫn cài đặt, với hộp kiểm cho biết "Khởi chạy ứng dụng Foo?". Và sau đó có một hành động tùy chỉnh để chạy ứng dụng, nếu hộp kiểm được chọn.

Nó xuất hiện dưới dạng màn hình cuối cùng trong chuỗi Trình tự cài đặt. Trông như thế này:

alt text


Đây là kịch bản tôi sử dụng để sửa đổi 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); 
} 

tôi ban đầu nhận này từ Aaron Stebner's blog, và sau đó sửa đổi nó.

Lưu tệp Javascript đó vào thư mục dự án (cùng một thư mục có chứa .vdproj), đặt tên là ModifyMsiToEnableLaunchApplication.js. Đối với mỗi dự án thiết lập duy nhất, bạn cần phải sửa đổi tập lệnh đó và đặt tên exe thích hợp vào đó. Và sau đó, bạn cần phải thiết lập các sự kiện sau xây dựng trong dự án Setup để là thế này:

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

Hãy chắc chắn để gõ tên của macro $(BuiltOuputPath) một cách chính xác. Từ Ouput bị lỗi chính tả bởi Microsoft và Built không được viết là Build!

oughtta đó làm điều đó.

Xem thêm: this modification không bao gồm hộp kiểm "chạy Foo.exe" trên UNINSTALL.

+3

Wow Cheeso ... kịch bản đẹp – Nestor

+1

giống như một chút phép thuật. Tôi không biết nếu MS thêm khả năng vào dự án thiết lập cho VS2010. Tôi nghĩ rằng nó cũng có thể làm điều này trong WiX, nhưng tôi đã không bao giờ sử dụng Wix, vì vậy điều này làm việc cho tôi. – Cheeso

+2

Nếu giao diện bị ẩn (msi bị đẩy bằng lệnh cài đặt im lặng) thì điều này vẫn hoạt động? – Shawn

2

Có .. Tôi sẽ viết một hành động tùy chỉnh và dán vào cuối bảng InstallExecutionSequence

+1

tôi không thấy một InstallExecutionSequence bàn? Trong các hành động tùy chỉnh, tôi thấy Cài đặt, Cam kết, Phục hồi và gỡ cài đặt. Hành động tùy chỉnh có trỏ tới đầu ra của chương trình chính không? – Shawn

+1

Bảng InstallExecutionSequence nằm bên trong MSI và không được hiển thị qua Visual Studio. Bạn sẽ cần một trình soạn thảo MSI như Orca để chỉnh sửa nó. MSI chỉnh sửa tay không phải là một nhiệm vụ tầm thường. –

+1

Tôi đồng ý - MSI chỉnh sửa thủ công không dễ dàng. Không phải là dễ dàng hay tầm thường khi sử dụng Orca. Bạn có thể sử dụng tập lệnh mà tôi đã cung cấp bên dưới để tự động hóa tác vụ. Nhanh chóng và dễ dàng, thử nghiệm và chứng minh. – Cheeso

4

Về 'PostBuildEvent' thất bại với mã lỗi '1' 'lỗi Unspecified' lỗi, thay đổi PostBuildEvent từ

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

để

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

Về ẩn hộp kiểm lỗi bạn có thể chỉnh sửa dòng 54 của tập lệnh để trở thành:

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

Cảm ơn bạn đã cập nhật. – Cheeso

8

Liên quan đến "lỗi hộp kiểm ẩn" tôi đã tìm ra những điều sau đây mà không được giải thích bởi câu trả lời Muleskinner của trên của Cheeso và:

Sự thay đổi của kịch bản (cung cấp bởi Muleskinner) đặt vị trí Y của hộp kiểm để 201 (Tôi đoán điểm ảnh Y đầu cho điều khiển).Nếu bạn thay đổi Y thành, nói, 151 (để sắp xếp nó ở giữa theo chiều dọc), thì lỗi "đột nhiên" xuất hiện. Lý do cho điều đó là có một điều khiển khác trong bảng điều khiển của msi, cụ thể là 'BodyText' ('Dialog' field = 'FinishedForm') mà Y của nó được đặt là 63 và chiều cao là 138. Đó là 138 + 63 = 201. Do đó, nếu bạn thay đổi giá trị Y cho hộp kiểm, điều khiển 'BodyText' chồng chéo điều khiển mới được thêm vào và đó là lý do người dùng cần phải di chuột qua để hiển thị hộp kiểm. Nếu bạn không có 'BodyText' hoặc số ký tự của nó đủ nhỏ, bạn có thể thay đổi (bằng cách sử dụng trình soạn thảo Orca msi như tôi làm, hoặc bằng cách thay đổi tập lệnh ở trên) Ys và Heights của 2 điều khiển này để có thể và chứa một vị trí Y khác cho hộp kiểm mới được thêm vào. Điều tương tự cũng áp dụng cho điều khiển: 'BodyTextRemove' trong đó một lần nữa chúng ta nên thay đổi giá trị độ cao của nó (xuất hiện trong khi gỡ cài đặt)

Hy vọng điều này sẽ giúp tất cả người dùng có cùng câu hỏi như tôi gặp về lỗi này

Tuy nhiên, tập lệnh thực sự là một công việc tốt!

Một câu hỏi khác là làm thế nào để vô hình hộp kiểm trong quá trình unistallation. Sử dụng trình biên tập Orca msi Tôi được thêm vào 2 dòng sau trong bảng ControlCondition của msi:

Row 1 (Khi kiểm soát sẽ được hiển thị):

(Dialog) FinishedForm (Control) CheckboxLaunch (Action) Hiện (điều kiện) BỎ = ""

Row 2 (Khi kiểm soát nên vô hình):

(Dialog) FinishedForm (Control) CheckboxLaunch (Action) Hide 012.(Điều kiện) XÓA <> ""

P.S. Tôi đang sử dụng VS 2010, trên windows 7 (x64), nhưng tôi tin rằng những điều này nên làm việc với các phiên bản trước đó quá.

+0

Vì vậy, chúng tôi cần chỉnh sửa msi để chúng tôi định vị lại hộp kiểm? – ViFer

11

OK !!! Đây là mã (không có 2 hàm phụ 'FindFileIdentifier' & 'StringEndsWith' ở cuối - thay vào đó sử dụng các cái gốc), cho phép chúng ta thay đổi Ys và Heights của các điều khiển, cùng với việc thêm các Điều kiện Kiểm soát Khả năng Hiển thị (xem 2 bình luận được đánh dấu giữa 'NEW - START' để '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); 
} 
+0

Hộp kiểm ứng dụng khởi động này xuất hiện sau khi xóa! –

+0

Chính xác nó đã sửa chữa cái gì? Nó có thể chỉ là tôi đã quá trễ, nhưng tôi đang gặp rắc rối khi phân tích những gì bạn đã viết ở đó, @Fidelis. – Cheeso

19

Điều này dường như là một giải pháp đơn giản hơn NHIÊU: Visual Studio Installer > How To Launch App at End of Installer

+1

Giải pháp ban đầu được yêu cầu cho VS2005 (và có thể là VS2008)? –

+0

Rõ ràng giải pháp đơn giản này không hiển thị tùy chọn hộp kiểm. –

+0

trong vấn đề giải pháp này là thiết lập sẽ bị kẹt ngay trước khi nó gần hoàn thành ... gần như ở mức 98%. và nó chỉ kết thúc khi bạn thoát khỏi ứng dụng. – mhesabi

Các vấn đề liên quan