Thủ thuật và mẹo về WiX


264

Chúng tôi đã sử dụng WiX được một thời gian và mặc dù có những cách hiểu thông thường về tính dễ sử dụng, nhưng nó sẽ hoạt động khá tốt. Những gì tôi đang tìm kiếm là lời khuyên hữu ích liên quan đến:

  • Thiết lập dự án WiX (bố cục, tham chiếu, mẫu tệp)
  • Tích hợp WiX vào các giải pháp và xây dựng / phát hành quy trình
  • Cấu hình trình cài đặt để cài đặt và nâng cấp mới
  • Bất kỳ hack WiX tốt nào bạn muốn chia sẻ

hãy xem gui4wix.codeplex.com
TarunG

10
Đóng như không xây dựng? Tôi đã học được rất nhiều từ việc đặt câu hỏi này! Một chút nhất quán từ StackOverflow cũng sẽ rất tuyệt ... ví dụ: stackoverflow.com/questions/550632/iêu
si618

15
Nó đã tăng '203', đủ để chứng minh tính hữu dụng của nó.
TarunG

Câu hỏi SO phải có câu trả lời dứt khoát, chính xác; câu hỏi mở làm cho câu hỏi mọi người hỏi về các vấn đề thực tế rơi ra khỏi trang nhất. faq @Si.: Chính sách đó đã luôn có AFAIK, nhưng giờ đây nó được thi hành tốt hơn; Câu hỏi đó đã gần ba tuổi.
Jim Dagg

Jim đủ công bằng, đó là một câu hỏi mở và tôi đoán đó là do cộng đồng SO quyết định, nhưng tôi phải nói rằng việc đóng nó là không mang tính xây dựng có vẻ kỳ quặc, vì tôi và bởi vẻ ngoài của nó, nhiều người khác đã tìm thấy câu hỏi này hữu ích (ví dụ: goo.gl/Zqp2X ) và nó rất phù hợp với practical, answerable questions based on actual problems that you facephần Câu hỏi thường gặp.
si618

Câu trả lời:


157
  1. Giữ các biến trong một wxitập tin bao gồm riêng biệt . Cho phép sử dụng lại, các biến được tìm thấy nhanh hơn và (nếu cần) cho phép thao tác dễ dàng hơn bằng một công cụ bên ngoài.

  2. Xác định các biến nền tảng cho các bản dựng x86 và x64

    <!-- Product name as you want it to appear in Add/Remove Programs-->
    <?if $(var.Platform) = x64 ?>
      <?define ProductName = "Product Name (64 bit)" ?>
      <?define Win64 = "yes" ?>
      <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
    <?else ?>
      <?define ProductName = "Product Name" ?>
      <?define Win64 = "no" ?>
      <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
    <?endif ?>
    
  3. Lưu trữ vị trí cài đặt trong sổ đăng ký, cho phép nâng cấp để tìm vị trí chính xác. Ví dụ: nếu người dùng đặt thư mục cài đặt tùy chỉnh.

     <Property Id="INSTALLLOCATION">
        <RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)"
                  Key="Software\Company\Product" Name="InstallLocation" />
     </Property>
    

    Lưu ý : Rob Mensch của WiX đã đăng một mục blog tuyệt vời để đi sâu vào chi tiết hơn và sửa trường hợp cạnh khi các thuộc tính được đặt từ dòng lệnh.

    Các ví dụ sử dụng 1. 2. và 3.

    <?include $(sys.CURRENTDIR)\Config.wxi?>
    <Product ... >
      <Package InstallerVersion="200" InstallPrivileges="elevated"
               InstallScope="perMachine" Platform="$(var.Platform)"
               Compressed="yes" Description="$(var.ProductName)" />
    

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
    
  4. Cách tiếp cận đơn giản nhất là luôn thực hiện các nâng cấp lớn , vì nó cho phép cả cài đặt và nâng cấp mới trong MSI đơn. UpgradeCode được cố định vào một Guid độc đáo và sẽ không bao giờ thay đổi, trừ khi chúng ta không muốn nâng cấp sản phẩm hiện có.

    Lưu ý : Trong WiX 3.5 có một yếu tố MajorUpgrad mới giúp cuộc sống trở nên dễ dàng hơn !

  5. Tạo một biểu tượng trong Thêm / Xóa Chương trình

    <Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" />
    <Property Id="ARPPRODUCTICON" Value="Company.ico" />
    <Property Id="ARPHELPLINK" Value="http://www.example.com/" />
    
  6. Khi phát hành bản dựng, chúng tôi phiên bản trình cài đặt của mình, sao chép tệp msi vào thư mục triển khai. Một ví dụ về điều này bằng cách sử dụng mục tiêu wixproj được gọi từ mục tiêu AfterBuild:

    <Target Name="CopyToDeploy" Condition="'$(Configuration)' == 'Release'">
      <!-- Note we append AssemblyFileVersion, changing MSI file name only works with Major Upgrades -->
      <Copy SourceFiles="$(OutputPath)$(OutputName).msi" 
            DestinationFiles="..\Deploy\Setup\$(OutputName) $(AssemblyFileVersion)_$(Platform).msi" />
    </Target>
    
  7. Sử dụng nhiệt để thu hoạch tệp với ký tự đại diện (*). Hữu ích nếu bạn muốn sử dụng lại các tệp WXS trên nhiều dự án (xem câu trả lời của tôi trên nhiều phiên bản của cùng một sản phẩm). Ví dụ: tệp bó này tự động thu hoạch đầu ra RoboHelp.

    @echo off  
    robocopy ..\WebHelp "%TEMP%\WebHelpTemp\WebHelp" /E /NP /PURGE /XD .svn  
    "%WIX%bin\heat" dir "%TEMP%\WebHelp" -nologo -sfrag -suid -ag -srd -dir WebHelp -out WebHelp.wxs -cg WebHelpComponent -dr INSTALLLOCATION -var var.WebDeploySourceDir 
    

    Có một chút gì đó đang diễn ra, robocopyđang loại bỏ siêu dữ liệu sao chép làm việc Subversion trước khi thu hoạch; các -drtài liệu tham khảo thư mục gốc được đặt tới vị trí lắp đặt của chúng tôi chứ không phải là TargetDir mặc định; -varđược sử dụng để tạo một biến để chỉ định thư mục nguồn (đầu ra triển khai web).

  8. Cách dễ dàng để đưa phiên bản sản phẩm vào tiêu đề hộp thoại chào mừng bằng cách sử dụng String.wxl để bản địa hóa. (Tín dụng: saschabeaumont . Đã thêm vì mẹo tuyệt vời này được ẩn trong một nhận xét)

    <WixLocalization Culture="en-US" xmlns="http://schemas.microsoft.com/wix/2006/localization">
        <String Id="WelcomeDlgTitle">{\WixUI_Font_Bigger}Welcome to the [ProductName] [ProductVersion] Setup Wizard</String>
    </WixLocalization>
    
  9. Hãy tiết kiệm cho mình một chút đau đớn và làm theo lời khuyên của Wim Coehen về một thành phần cho mỗi tệp. Điều này cũng cho phép bạn loại bỏ (hoặc thẻ đại diện *) thành phần GUID .

  10. Rob Mensching có một cách gọn gàng để nhanh chóng theo dõi các vấn đề trong tệp nhật ký MSI bằng cách tìm kiếm value 3. Lưu ý các ý kiến ​​liên quan đến quốc tế hóa.

  11. Khi thêm các tính năng có điều kiện, sẽ trực quan hơn để đặt mức tính năng mặc định thành 0 (bị tắt) và sau đó đặt mức điều kiện thành giá trị mong muốn của bạn. Nếu bạn đặt mức tính năng mặc định> = 1, mức điều kiện phải là 0 để tắt nó, nghĩa là logic điều kiện phải ngược lại với những gì bạn mong đợi, có thể gây nhầm lẫn :)

    <Feature Id="NewInstallFeature" Level="0" Description="New installation feature" Absent="allow">
      <Condition Level="1">NOT UPGRADEFOUND</Condition>
    </Feature>
    <Feature Id="UpgradeFeature" Level="0" Description="Upgrade feature" Absent="allow">
      <Condition Level="1">UPGRADEFOUND</Condition>
    </Feature>
    

Về việc thêm biểu tượng trong Thêm / Xóa Chương trình, nó chính xác là những gì tôi đang tìm kiếm. Nơi nào bạn dính ba dòng đó? +1 cho sự tuyệt vời.
Everett

Tôi có xu hướng đặt chúng ngay sau (và rõ ràng bên dưới) phần tử <Gói>. Hãy xem lược đồ về tính hợp lệ wix.sourceforge.net/manual-wix3/schema_index.htmlm
si618

+1, ước gì tôi có thể làm +100, đây là thông tin hữu ích nhất về thông tin Wix mà tôi đã vấp phải.
Tim Long

Cảm ơn Tim! Rob Mensching, Bob Arson, Wim Coehen và những người khác xứng đáng với danh tiếng vì đã chia sẻ kiến ​​thức của họ.
si618

38

Kiểm tra nếu IIS được cài đặt:

<Property Id="IIS_MAJOR_VERSION">
    <RegistrySearch Id="CheckIISVersion" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp" Name="MajorVersion" Type="raw" />
</Property>

<Condition Message="IIS must be installed">
    Installed OR IIS_MAJOR_VERSION
</Condition>

Kiểm tra nếu Tương thích Metabase IIS 6 được cài đặt trên Vista +:

<Property Id="IIS_METABASE_COMPAT">
    <RegistrySearch Id="CheckIISMetabase" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp\Components" Name="ADSICompatibility" Type="raw" />
</Property>

<Condition Message="IIS 6 Metabase Compatibility feature must be installed">
    Installed OR ((VersionNT &lt; 600) OR IIS_METABASE_COMPAT)
</Condition>

34

Giữ tất cả ID trong các không gian tên riêng biệt

  • Các tính năng bắt đầu với các F. ví dụ: F.Documentation, F.Binaries, F.SampleCode.
  • Các thành phần bắt đầu bằng C. Ex: C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry
  • CustomActions là CA. Ex: CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyX
  • Tập tin là Fi.
  • Thư mục là Di.
  • và như thế.

Tôi thấy điều này giúp ích rất nhiều trong việc theo dõi tất cả các id khác nhau trong tất cả các danh mục khác nhau.


Tôi không sử dụng không gian tên nhưng tôi thêm ID; ví dụ: exampleFeature, ChmFileComponent. Tôi đoán tôi thích đánh máy ;-)
dvdvorle

25

Câu hỏi tuyệt vời. Tôi muốn thấy một số thực hành tốt nhất được hiển thị.

Tôi đã có rất nhiều tệp mà tôi phân phối, vì vậy tôi đã thiết lập dự án của mình thành một số tệp nguồn wxs.

Tôi có một tệp nguồn cấp cao nhất mà tôi gọi là Product.wxs về cơ bản chứa cấu trúc để cài đặt, nhưng không phải là các thành phần thực tế. Tập tin này có một số phần:

<Product ...>
  <Package ...>
    <Media>... 
   <Condition>s ...
   <Upgrade ..>
   <Directory> 
        ...
   </Directory>
   <Feature>
      <ComponentGroupRef ... > A bunch of these that
   </Feature>
   <UI ...>
   <Property...>
   <Custom Actions...>
   <Install Sequences....
  </Package>
</Product>

Phần còn lại của các tệp .wix bao gồm các Đoạn có chứa Thành phần Nhóm được tham chiếu trong thẻ Tính năng trong Product.wxs. Dự án của tôi chứa một nhóm logic hợp lý của các tệp mà tôi phân phối

<Fragment>
   <ComponentGroup>
     <ComponentRef>
     ....
    </ComponentGroup>
    <DirectoryRef>
      <Component... for each file
      .... 
    </DirectoryRef>
</Fragment>

Điều này không hoàn hảo, cảm giác nhện OO của tôi hơi râm ran một chút vì các đoạn phải tham chiếu tên trong tệp Product.wxs (ví dụ: DirectoryRef) nhưng tôi thấy việc duy trì một tệp nguồn lớn duy nhất dễ dàng hơn.

Tôi rất thích nghe ý kiến ​​về điều này, hoặc nếu có ai có bất kỳ lời khuyên tốt quá!


Thiết lập của chúng tôi cũng rất giống với phương pháp này. Điều đó tốt bởi vì chúng tôi có thể sử dụng tương đương Products.wxs của chúng tôi làm thiết lập cơ sở cho nhiều loại sản phẩm.
si618

@Peter Tate: cảm giác nhện của bạn là chính xác. Xem câu trả lời của tôi về răng cưa thư mục.
Wim Coenen

Tôi có cùng một cách tiếp cận: Product.wxs với bố cục là tĩnh và tác vụ xây dựng (Heat.exe) tạo tệp Content.wxs của tôi
timvw

20

Thêm một hộp kiểm vào hộp thoại thoát để khởi chạy ứng dụng hoặc tệp trợ giúp.

...

<!-- CA to launch the exe after install -->
<CustomAction Id          ="CA.StartAppOnExit"
              FileKey     ="YourAppExeId"
              ExeCommand  =""
              Execute     ="immediate"
              Impersonate ="yes"
              Return      ="asyncNoWait" />

<!-- CA to launch the help file -->
<CustomAction Id         ="CA.LaunchHelp"
              Directory  ="INSTALLDIR"
              ExeCommand ='[WindowsFolder]hh.exe IirfGuide.chm'
              Execute    ="immediate"
              Return     ="asyncNoWait" />

<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
          Value="Launch MyApp when setup exits." />

<UI>
  <Publish Dialog  ="ExitDialog"
           Control ="Finish"
           Order   ="1"
           Event   ="DoAction"
           Value   ="CA.StartAppOnExit">WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT</Publish>
</UI>

Nếu bạn làm theo cách này, ngoại hình "chuẩn" không hoàn toàn đúng. Hộp kiểm luôn có nền màu xám, trong khi hộp thoại có màu trắng:

văn bản thay thế http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_1.gif

Một cách để giải quyết vấn đề này là chỉ định ExitDialog tùy chỉnh của riêng bạn, với hộp kiểm được định vị khác . Điều này hoạt động, nhưng có vẻ như rất nhiều công việc chỉ để thay đổi màu sắc của một điều khiển. Một cách khác để giải quyết điều tương tự là xử lý hậu kỳ MSI được tạo để thay đổi các trường X, Y trong bảng Điều khiển cho điều khiển CheckBox cụ thể đó. Mã javascript trông như thế này:

var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql = "UPDATE `Control` SET `Control`.`Height` = '18', `Control`.`Width` = '170'," +
          " `Control`.`Y`='243', `Control`.`X`='10' " +
          "WHERE `Control`.`Dialog_`='ExitDialog' AND " + 
          "  `Control`.`Control`='OptionalCheckBox'";
var view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();

Chạy mã này dưới dạng tập lệnh dòng lệnh (sử dụng cscript.exe) sau khi MSI được tạo (từ light.exe) sẽ tạo ra một ExitDialog trông chuyên nghiệp hơn:

văn bản thay thế http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_2.gif


Hà! Không phải blog của tôi. Tôi cũng đọc nó Và tôi có một liên kết đến mục blog trong văn bản trên. Nhưng họ đã làm nó khác với tôi. Tôi thích cách của tôi hơn. !!
Cheeso

1
Cảm ơn các js, rất hữu ích! Một điều mà tôi đã phải thay đổi trong wxs là thay thế WIXUI_EXITDIALOGOPTIONALCHECKBOXbằng WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installedbên trong<Publish>
Alexander Kojevnikov

Có cách nào để làm cho hộp kiểm được kiểm tra theo mặc định?
Davis

Để đánh dấu vào ô theo mặc định, tôi đã sử dụng này: <Mã bất động sản = "WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value = "1" />
Alek Davis

Có vẻ như là một giải pháp tiện lợi, nhưng làm thế nào để tôi sử dụng nó? Có cách nào để đặt js bên trong phần tử <AfterBuild> trong wixproj của tôi không? Hoặc vì bạn đề cập đến việc chạy nó từ dòng lệnh, tốt hơn là một sự kiện hậu xây dựng, trong trường hợp nào, trình thông dịch dòng lệnh js tốt cho Windows là gì?
vanmelle

18

Tạo các phiên bản Live, Test, Đào tạo, ... bằng các tệp nguồn giống nhau.

Tóm lại: Tạo Mã nâng cấp duy nhất cho mỗi trình cài đặt và tự động xác định ký tự đầu tiên của mỗi Hướng dẫn cho mỗi trình cài đặt, để lại 31 duy nhất còn lại.

Điều kiện tiên quyết

Giả định

  • Các biến WiX được sử dụng để xác định Nâng cấp Mã, ProductName, InstallName.
  • Bạn đã có một trình cài đặt làm việc. Tôi sẽ không thử điều này cho đến khi bạn làm.
  • Tất cả các Thành phần của bạn được giữ trong một tệp (Components.wxs). Quá trình này sẽ hoạt động nếu bạn có nhiều tệp, sẽ còn nhiều việc phải làm.

Cấu trúc thư mục

  • Setup.L Library
    • Tất cả các tệp wx (Thành phần, Tính năng, Hộp thoại UI, ...)
    • Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
  • Setup.Live (wixproj)
    • Liên kết tất cả các tệp Setup.L Library bằng cách sử dụng "Thêm tệp hiện có" -> "Thêm dưới dạng liên kết" (nút mũi tên xuống nhỏ ngay bên cạnh nút Thêm trong Visual Studio)
    • Config.wxi (có Nâng cấp mã độc đáo, ProductName, InstallName, ...)
  • Cài đặt.Test , ...
    • như trực tiếp nhưng Config.wxi được cấu hình cho môi trường Kiểm tra.

Quá trình

  • Tạo thư mục Setup.L Library và di chuyển tất cả các tệp wx và wxi của bạn (ngoại trừ Config.wxi) khỏi dự án hiện có.
  • Tạo Setup.Live, Setup.Test, v.v. theo wixproj bình thường.
  • Thêm mục tiêu BeforeBuild trong wixproj trong Setup.Live, v.v. để thực hiện MSBuild Community TaskUpdate để sửa đổi Hướng dẫn (Tôi đã sử dụng A cho Live, B cho Test và C để đào tạo)
  • Thêm mục tiêu AfterBuild để hoàn nguyên Thành phần.wxs Hướng dẫn trở về 0.
  • Xác minh với Orca rằng mỗi thành phần trong mỗi MSI có hướng dẫn sửa đổi.
  • Xác nhận rằng các hướng dẫn ban đầu được khôi phục.
  • Xác minh rằng mỗi MSI đang cài đặt (và nâng cấp) đúng sản phẩm và vị trí.

Ví dụ Config.wxi

<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Upgrade code should not change unless you want to install 
     a new product and have the old product remain installed, 
     that is, both products existing as separate instances. -->
<?define UpgradeCode = "YOUR-GUID-HERE" ?>

<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
  <!-- Product name as you want it to appear in Add/Remove Programs-->
  <?define ProductName = "Foo 64 Bit [Live]" ?>
<?else ?>
  <?define ProductName =  "Foo [Live]" ?>
<?endif ?>

<!-- Directory name used as default installation location -->
<?define InstallName = "Foo [Live]" ?>

<!-- Registry key name used to store installation location -->
<?define InstallNameKey = "FooLive" ?>

<?define VDirName = "FooLive" ?>
<?define AppPoolName = "FooLiveAppPool" ?>
<?define DbName = "BlahBlahLive" ?>
</Include>

Ví dụ Config.Common.wxi

<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Auto-generate ProductCode for each build, release and upgrade -->
<?define ProductCode = "*" ?>

<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define ProductVersion = "1.0.0.0" ?>

<!-- Minimum version supported if product already installed and this is an upgrade -->
<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define MinimumUpgradeVersion = "0.0.0.0" ?>

<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
   <?define Win64 = "yes" ?>
   <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
   <?define Win64 = "no" ?>
   <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>

<?define ProductManufacturer = "Foo Technologies"?>

<!-- Decimal Language ID (LCID) for the Product. Used for localization. -->
<?define ProductLanguage = "1033" ?>

<?define WebSiteName = "DefaultWebSite" ?>
<?define WebSitePort = "80" ?>

<?define DbServer = "(local)" ?>
</Include>

Thành phần ví dụ.wxs

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <!-- The pre-processor variable which allows the magic to happen :) -->
  <?include $(sys.CURRENTDIR)\Config.wxi?>
  <?include ..\Setup.Library\Config.Common.wxi?>
  <Fragment Id="ComponentsFragment">
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
          <Component Id="ProductComponent" Guid="0XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" KeyPath="yes">
          ...

Lưu ý: Bây giờ tôi sẽ đề xuất bỏ thuộc tính Guid ra khỏi Thành phần (tương đương *), sử dụng một tệp cho mỗi thành phần và đặt tệp làm đường dẫn khóa. Điều này loại bỏ sự cần thiết phải gọi ModifyComponentsGuidsRevertComponentsGuidscác mục tiêu được hiển thị dưới đây. Điều này có thể không thể cho tất cả các thành phần của bạn mặc dù.

Ví dụ Setup.Live.wixproj

<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="BeforeBuild">
  <CallTarget Targets="ModifyComponentsGuids" />
</Target>
<Target Name="AfterBuild">
  <CallTarget Targets="RevertComponentsGuids" />
</Target>
<!-- Modify the first character of every Guid to create unique value for Live, Test and Training builds -->
<Target Name="ModifyComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;0" />
</Target>

Suy nghĩ cuối cùng

  • Quá trình này cũng sẽ hoạt động để tạo các trình cài đặt khác nhau cho các mô-đun hợp nhất khác nhau (Live, Test, ... dưới dạng các tính năng) cho cùng một trình cài đặt. Tôi đã đi với các trình cài đặt khác nhau vì nó có vẻ là một tùy chọn an toàn hơn, có nhiều rủi ro hơn khi ai đó có thể nâng cấp Live thay vì Đào tạo nếu chúng ở trên cùng một hộp và bạn chỉ sử dụng các tính năng cho các mô-đun hợp nhất khác nhau.
  • Nếu bạn sử dụng MSI của mình để thực hiện nâng cấp cũng như cài đặt mới, tức là cách tiếp cận chỉ nâng cấp chính và bạn lưu vị trí cài đặt trong sổ đăng ký, hãy nhớ tạo một biến cho tên khóa cho mỗi lần cài đặt.
  • Chúng tôi cũng tạo các biến trong mỗi Config.wxi để cho phép các tên thư mục ảo, nhóm ứng dụng, tên cơ sở dữ liệu, et cetera cho mỗi trình cài đặt.

CẬP NHẬT 1: Hướng dẫn tạo thành phần tự động loại bỏ nhu cầu gọi tác vụ FileUpdate nếu bạn tạo thành phần với Guid = "*" cho mỗi tệp, đặt tệp làm đường dẫn khóa.

CẬP NHẬT 2: Một trong những vấn đề chúng tôi gặp phải là nếu bạn không tự động tạo thành phần Guid của mình và quá trình xây dựng không thành công, thì các tệp tạm thời cần phải được xóa thủ công.

CẬP NHẬT 3: Tìm thấy một cách để loại bỏ sự phụ thuộc vào svn: externals và tạo tập tin tạm thời. Điều này làm cho quá trình xây dựng trở nên linh hoạt hơn (và là tùy chọn tốt nhất nếu bạn không thể ký tự đại diện cho Hướng dẫn của mình) và ít giòn hơn nếu có lỗi xây dựng trong ánh sáng hoặc nến.

CẬP NHẬT 4: Hỗ trợ cho nhiều trường hợp sử dụng các biến đổi thể hiện trong WiX 3.0+, chắc chắn cũng đáng xem.


+1 cho tài liệu tham khảo Nhiệm vụ cộng đồng MSBuild, yêu gói đó
BozoJoe

17

Sử dụng ghi nhật ký Chẩn đoán Msi để nhận thông tin thất bại chi tiết

msiexec /i Package.msi /l*v c:\Package.log

Ở đâu

Gói.msi
là tên gói hàng của bạn và
c: \ Gói.log
là nơi bạn muốn đầu ra của nhật ký

Mã lỗi Msi

Video giới thiệu Wix
Oh và video giới thiệu ngẫu nhiên Wix có "Ông WiX" Rob Mensching là "bức tranh lớn về mặt khái niệm" hữu ích.


2
+1 Sẽ tốt hơn nhiều nếu chúng ta có thể cho phép đăng nhập từ bên trong Wix thay vì dòng lệnh.
si618

3
WiX nào. Đặt thuộc tính MsiLogging. Chỉ được hỗ trợ bởi Windows Installer 4.0+.
Rob Mensking

Cảm ơn bạn rất nhiều "Ông Wix". Hãy kiểm tra xem.
Terrance

17

Sử dụng Javascript CustomActions vì chúng rất dễ

Mọi người đã nói rằng Javascript là điều sai khi sử dụng cho MSI CustomActions . Lý do đưa ra: khó gỡ lỗi, khó làm cho nó đáng tin cậy. Tôi không đồng ý. Không khó để gỡ lỗi, chắc chắn không khó hơn C ++. Nó chỉ khác nhau. Tôi thấy việc viết CustomActions trong Javascript là siêu dễ, dễ hơn nhiều so với sử dụng C ++. Nhanh hơn nhiều. Và cũng đáng tin cậy.

Chỉ có một nhược điểm: Javascript CustomActions có thể được trích xuất thông qua Orca, trong khi đó C / C ++ CA sẽ yêu cầu kỹ thuật đảo ngược. Nếu bạn coi phép thuật trình cài đặt của mình là tài sản trí tuệ được bảo vệ, bạn sẽ muốn tránh tập lệnh.

Nếu bạn sử dụng tập lệnh, bạn chỉ cần bắt đầu với một số cấu trúc. Đây là một số để giúp bạn bắt đầu.


Mã "soạn sẵn" Javascript cho CustomAction:

//
// CustomActions.js 
// 
// Template for WIX Custom Actions written in Javascript.
// 
// 
// Mon, 23 Nov 2009  10:54
// 
// ===================================================================


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
};

var Icons = {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
};

var MsgKind = {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
};

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
};


function MyCustomActionInJavascript_CA() {
    try {
        LogMessage("Hello from MyCustomActionInJavascript");
        // ...do work here...
        LogMessage("Goodbye from MyCustomActionInJavascript");
    }
    catch (exc1) {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException(exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}

// Pop a message box.  also spool a message into the MSI log, if it is enabled. 
function LogException(exc) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}


// spool an informational message into the MSI log, if it is enabled. 
function LogMessage(msg) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction:: " + msg;
    Session.Message(MsgKind.Log, record);
}


// http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
var WindowStyle = {
    Hidden : 0,
    Minimized : 1,
    Maximized : 2
};

// http://msdn.microsoft.com/en-us/library/314cz14s(v=VS.85).aspx
var OpenMode = {
    ForReading : 1,
    ForWriting : 2,
    ForAppending : 8
};

// http://msdn.microsoft.com/en-us/library/a72y2t1c(v=VS.85).aspx
var SpecialFolders = {
    WindowsFolder : 0, 
    SystemFolder :  1, 
    TemporaryFolder : 2
};

// Run a command via cmd.exe from within the MSI
function RunCmd(command)
{
    var wshell = new ActiveXObject("WScript.Shell");
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder);
    var tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName());

    LogMessage("shell.Run("+command+")");

    // use cmd.exe to redirect the output
    var rc = wshell.Run("%comspec% /c " + command + "> " + tmpFileName, WindowStyle.Hidden, true);
    LogMessage("shell.Run rc = "  + rc);

    // here, optionally parse the output of the command 
    if (parseOutput) {
        var textStream = fso.OpenTextFile(tmpFileName, OpenMode.ForReading);
        while (!textStream.AtEndOfStream) {
            var oneLine = textStream.ReadLine();
            var line = ParseOneLine(oneLine);
                ...
        }
        textStream.Close();
    }

    if (deleteOutput) {
        fso.DeleteFile(tmpFileName);
    }

    return {
        rc : rc,
        outputfile : (deleteOutput) ? null : tmpFileName
    };
}

Sau đó, đăng ký hành động tùy chỉnh với một cái gì đó như thế này:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.MyCustomAction"
              BinaryKey="IisScript_CA"
              JScriptCall="MyCustomActionInJavascript_CA"
              Execute="immediate"
              Return="check" />
</Fragmemt>

Tất nhiên, bạn có thể chèn bao nhiêu chức năng Javascript tùy thích, cho nhiều hành động tùy chỉnh. Một ví dụ: Tôi đã sử dụng Javascript để thực hiện truy vấn WMI trên IIS, để lấy danh sách các trang web hiện có, có thể cài đặt bộ lọc ISAPI. Danh sách này sau đó được sử dụng để điền vào hộp danh sách được hiển thị sau trong chuỗi UI. Tất cả đều rất dễ dàng.

Trên IIS7, không có nhà cung cấp WMI cho IIS, vì vậy tôi đã sử dụng shell.Run()cách tiếp cận để gọi appcmd.exe để thực hiện công việc. Dễ dàng.

Câu hỏi liên quan: Giới thiệu về CustomActions Javascript


2
+1 Tôi thấy cách tiếp cận DTF dễ cài đặt, nhưng javascript cũng có thể hữu ích.
si618

12

Peter Tate đã chỉ ra cách bạn có thể định nghĩa các định nghĩa Thành phần nhóm có thể sử dụng lại trong các đoạn wix riêng biệt. Một số thủ thuật bổ sung liên quan đến điều này:

Bí danh

Các đoạn thành phần nhóm không cần biết về các thư mục được xác định bởi wxs của sản phẩm chính. Trong đoạn nhóm thành phần của bạn, bạn có thể nói về một thư mục như thế này:

<DirectoryRef Id="component1InstallFolder">
...
</DirectoryRef>

Sau đó, sản phẩm chính có thể đặt bí danh cho một trong các thư mục của nó (ví dụ: "productInstallFolder") như thế này:

<Directory Id="productInstallFolder" Name="ProductName">
   <!-- not subfolders (because no Name attribute) but aliases for parent! -->
   <Directory Id="component1InstallFolder"/> 
   <Directory Id="component2InstallFolder"/> 
</Directory>

Đồ thị phụ thuộc

Các phần tử thành phần có thể chứa các phần tử con của ElementgroupRef. Điều này thật tuyệt nếu bạn có một nhóm lớn các thành phần có thể tái sử dụng với biểu đồ phụ thuộc phức tạp giữa chúng. Bạn chỉ cần thiết lập một Nhóm thành phần trong đoạn riêng của mình cho từng thành phần và khai báo các phụ thuộc như thế này:

<ComponentGroup Id="B">
   <ComponentRef Id="_B" />
   <ComponentGroupRef Id="A">
</ComponentGroup>

Nếu bây giờ bạn tham chiếu nhóm thành phần "B" trong thiết lập của mình vì đây là phần phụ thuộc trực tiếp vào ứng dụng của bạn, nó sẽ tự động kéo vào nhóm thành phần "A" ngay cả khi tác giả ứng dụng không bao giờ nhận ra rằng đó là phần phụ thuộc của "B". Nó "chỉ hoạt động" miễn là bạn không có bất kỳ phụ thuộc vòng tròn nào.

Wixlib tái sử dụng

Ý tưởng biểu đồ phụ thuộc ở trên hoạt động tốt nhất nếu bạn biên dịch các thành phần nhóm lớn-o-tái sử dụng thành một wixlib có thể tái sử dụng với lit.exe. Khi tạo một thiết lập ứng dụng, bạn có thể tham chiếu wixlib này giống như một tệp wixobj. Trình liên kết cand.exe sẽ tự động loại bỏ bất kỳ đoạn nào không được "kéo vào" bởi (các) tệp wxs của sản phẩm chính.


12

Tôi ngạc nhiên không ai đề cập đến việc sử dụng T4 để tạo tệp WXS trong quá trình xây dựng. Tôi đã học được điều này bằng cách của Henry Lee @ New Age Solutions .

Về cơ bản, bạn tạo một tác vụ MSBuild tùy chỉnh để thực thi mẫu T4 và mẫu đó xuất ra WXS ngay trước khi dự án Wix được biên dịch. Điều này cho phép bạn (tùy thuộc vào cách bạn triển khai nó) tự động bao gồm tất cả đầu ra của hội đồng từ việc biên dịch một giải pháp khác (có nghĩa là bạn không còn phải chỉnh sửa wxs mỗi khi bạn thêm một cụm mới).


2
+1 điều đó thực sự tốt, tôi không lo lắng nhiều về các hội đồng, nhưng các dự án web của chúng tôi có thể gặp sự cố với các trang aspx và các tạo phẩm khác (hình ảnh, css) được thêm vào dự án nhưng không phải WiX.
si618

4
Đối với khách truy cập trong tương lai, Wix 3.5 có tiện ích Heat.exe thực hiện việc thu hoạch này tự động
Mrchief

@Mrchief - Tôi không tin Heat chọn các hội đồng tham chiếu được sao chép cục bộ - mặc dù điều này rõ ràng được lên kế hoạch cho 4.0. Tham khảo: sourceforge.net/tracker/ từ
Peter T. LaComb Jr.

Nhiệt không nhận các hội đồng tham khảo.
tofutim

Một số ví dụ hay về việc sử dụng T4 để tạo tệp WXS là gì?
tofutim

12

Sử dụng Heat.exe để đập vỡ mặt và tạo ra "Epic Pwnage" trên các bản cài đặt lớn

Mở rộng câu trả lời của SiRobert-P về nhiệt.

Dịch: (Sử dụng nhiệt để tránh nhập các tệp riêng lẻ vào dự án bằng tay và để tự động hóa các bản dựng cho quy trình dễ dàng hơn.)

WiX 2.0 Heat Syntax chi tiết

Đối với các phiên bản mới hơn (không khác tất cả các phiên bản cũ hơn nhưng có những thay đổi cú pháp có thể gây phiền nhiễu ....) hãy truy cập thư mục Heat nằm trong cmd.exe và chỉ cần nhập nhiệt nhưng tôi có một ví dụ ngay tại đây để được trợ giúp với các phiên bản mới hơn nếu cần.

Thêm phần sau vào Sự kiện Xây dựng của bạn trong studio trực quan năm 2010
(Nhấp chuột phải vào Dự án-> Thuộc tính -> Xây dựng sự kiện-> Sự kiện trước khi xây dựng)

$(WIX)bin\heat.exe" dir "$(EnviromentVariable)" -cg GroupVariable -gg -scom -sreg -sfrag - srd -dr INSTALLLOCATION -var env.LogicPath -out "$(FragmentDir)\FileName.wxs

-gg 

Tạo Hướng dẫn khi chạy nhiệt (như khi bạn thực hiện lệnh trên)

-có thể 

Đừng lấy "tập tin COM"

-sreg 

Đừng lấy "Tệp đăng ký"

-frag 

Đừng lấy "Mảnh vỡ"

-thứ 

Đừng lấy "thư mục gốc"

thư mục

dir cho biết bạn muốn Heat tìm trong một thư mục

"$ (EnviromentVariable)"

Tên của biến bạn sẽ thêm vào các biến Tiền xử lý trong thuộc tính dự án (Dự án nhấp chuột phải, Chuyển đến thuộc tính) -> Phần xây dựng có ghi Xác định biến tiền xử lý (giả sử studio trực quan 2010)

Thí dụ:
EnviromentVariable = C: \ Project \ bin \ Debug;
Không có dấu ngoặc kép nhưng kết thúc bằng dấu chấm phẩy

-cg Group Biến đổi 

Nhóm thành phần sẽ được tham chiếu từ đoạn được tạo đến tệp wxs chính

FragmentDir

Thư mục đoạn mà đoạn đầu ra wxs sẽ được lưu trữ

Tên tệp.wxs

Tên của tập tin

Hướng dẫn đầy đủ ở đây, vì vậy freakin hữu ích

Phần 1 Phần 2


Có một công cụ hữu ích khác cho các mục đích hơi khác nhau: Paraffin ( wintellect.com/CS/bloss/jrobbins/archive/2010/03/10/4107.aspx )
ralf.w.

9

Bao gồm các đối tượng COM:

heattạo ra hầu hết (nếu không phải tất cả) các mục đăng ký và cấu hình khác cần thiết cho chúng. Hân hoan!

Bao gồm các đối tượng COM được quản lý (còn gọi là các đối tượng COM .NET hoặc C #)

Sử dụng heattrên một đối tượng COM được quản lý sẽ cung cấp cho bạn một tài liệu wix gần như hoàn chỉnh.

Nếu bạn không cần thư viện có sẵn trong GAC (nghĩa là có sẵn trên toàn cầu: MOST của thời gian bạn không cần điều này với các hội đồng .NET của bạn - dù sao thì có lẽ bạn đã làm gì đó sai nếu không có ý định một thư viện dùng chung) bạn sẽ muốn đảm bảo cập nhật CodeBasekhoá đăng ký sẽ được đặt thành [#ComponentName]. Nếu bạn dự định cài đặt nó vào GAC (ví dụ: bạn đã tạo một số thư viện chung tuyệt vời mới mà mọi người sẽ muốn sử dụng), bạn phải xóa mục này và thêm hai thuộc tính mới vào Filephần tử: AssemblyKeyPath. Hội nên được đặt thành ".net" và KeyPathnên được đặt thành "có".

Tuy nhiên, một số môi trường (đặc biệt là bất cứ thứ gì có bộ nhớ được quản lý như ngôn ngữ kịch bản) cũng sẽ cần quyền truy cập vào typelib. Hãy chắc chắn để chạy heattrên typelib của bạn và bao gồm nó. heatsẽ tạo ra tất cả các khóa registry cần thiết. Làm thế nào là mát mẻ?


8

Cài đặt để C:\ProductName

Một số ứng dụng cần được cài đặt C:\ProductNamehoặc một cái gì đó tương tự, nhưng 99,9% (nếu không phải 100%) trong các ví dụ trong cài đặt mạng C:\Program Files\CompanyName\ProductName.

Mã sau đây có thể được sử dụng để đặt thuộc TARGETDIRtính vào gốc của C:ổ đĩa (được lấy từ danh sách người dùng WiX ):

<CustomAction Id="AssignTargetDir" Property="TARGETDIR" Value="C:\" Execute="firstSequence" />
<InstallUISequence>
    <Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallUISequence>
<InstallExecuteSequence>
    <Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallExecuteSequence>

LƯU Ý: Theo mặc định, TARGETDIR không trỏ đến C:\! Nó thay vào ROOTDRIVEđó lần lượt chỉ vào gốc của ổ đĩa có không gian trống nhất ( xem tại đây ) - và đây không nhất thiết là C:ổ đĩa. Có thể có một ổ đĩa cứng, phân vùng hoặc ổ USB khác!

Sau đó, ở đâu đó bên dưới <Product ...>thẻ của bạn , bạn cần các thẻ thư mục sau như bình thường:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
        <!-- your content goes here... -->
    </Directory>
</Directory>

Sẽ không đơn giản hơn nếu chỉ cài đặt WindowsVolume?
Wim Coenen

1
Có, nhưng bạn sẽ phải sử dụng một cách giải quyết vì thuộc WindowsVolumetính có thể không được sử dụng như một Directory(trình biên dịch đưa ra lỗi / cảnh báo), như được chỉ ra ở đâyđây . Cá nhân, tôi thấy rằng cách giải quyết khó hiểu.
gehho

7

Biến môi trường

Khi biên dịch tài liệu Wx của bạn thành mã wixobj, bạn có thể sử dụng các biến môi trường để xác định thông tin khác nhau. Ví dụ: giả sử bạn muốn thay đổi tập tin nào được đưa vào dự án. Hãy nói rằng bạn có một biến môi trường có tên là RELEASE_MODE, mà bạn đã đặt ngay trước khi bạn xây dựng MSI của mình (bằng tập lệnh hoặc bằng tay, không thành vấn đề) Trong nguồn wix của bạn, bạn có thể làm một cái gì đó như:

<define FILESOURCE = c:\source\output\bin\$(env.RELEASE_MODE) >

và sau đó trong mã của bạn, sử dụng nó tại chỗ để nhanh chóng thay đổi tài liệu wxs của bạn, ví dụ:

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />

1
Ngoài ra các biến biên dịch như $ (Cấu hình) và $ (Nền tảng) cũng có sẵn. Ngoài ra còn có nhiều hơn nữa tại msdn.microsoft.com/en-us/l
Library / aa302186.aspx

1
@Si - Đôi khi trước ngày hôm nay, liên kết đó không còn hoạt động. Tôi không thể tìm thấy cái mới nhất.
Peter M



7

Chỉnh sửa hộp thoại

Một khả năng tốt để chỉnh sửa các hộp thoại là sử dụng SharpDevelop trong phiên bản 4.0.1,7090 (hoặc cao hơn). Với sự trợ giúp của công cụ này, một hộp thoại độc lập (các tệp wx từ các nguồn WiX như vd InstallDirDlg.wxs) có thể được mở, xem trước và chỉnh sửa trong dạng xem Thiết kế.


Tuyệt vời, không biết SharpDevelop đã hỗ trợ điều này.
anton.burger

6

Đặt cờ IIS enable32BitAppOnWin64 http://trycatchfail.com/blog/post/WiX-Snippet-change-enable32BitAppOnWin64.aspx

<InstallExecuteSequence>
   <RemoveExistingProducts After="InstallFinalize" />
   <Custom Action="ConfigureAppPool" After="InstallFinalize" >
     <![CDATA[NOT Installed AND VersionNT64 >= 600]]>         
   </Custom>
</InstallExecuteSequence>

<CustomAction Id="ConfigureAppPool" Return="check" Directory="TARGETDIR" ExeCommand="[SystemFolder]inetsrv\appcmd set apppool /apppool.name:[APPPOOLNAME] /enable32BitAppOnWin64:false" />

5

Sửa đổi "Sẵn sàng để cài đặt?" hộp thoại (còn gọi là ConfirmReadyDlg) để cung cấp một bản tóm tắt các lựa chọn được thực hiện.

Nó trông như thế này:
văn bản thay thế http://i46.tinypic.com/s4th7t.jpg

Làm điều này với một CustomAction Javascript:


Mã Javascript:

// http://msdn.microsoft.com/en-us/library/aa372516(VS.85).aspx
var MsiViewModify = 
    {
        Refresh          : 0,
        Insert           : 1,
        Update           : 2,
        Assign           : 3,
        Replace          : 4,
        Merge            : 5,
        Delete           : 6,
        InsertTemporary  : 7,   // cannot permanently modify the MSI during install
        Validate         : 8,
        ValidateNew      : 9,
        ValidateField    : 10,
        ValidateDelete   : 11
    };


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = 
    {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
    };

var Icons= 
    {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
    }

var MsgKind =
    {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
    };

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = 
    {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
    };

function UpdateReadyDialog_CA(sitename)
{
    try 
    {
        // can retrieve properties from the install session like this:
        var selectedWebSiteId = Session.Property("MSI_PROPERTY_HERE");

        // can retrieve requested feature install state like this:
        var fInstallRequested   = Session.FeatureRequestState("F.FeatureName");

        var text1 = "This is line 1 of text in the VerifyReadyDlg";

        var text2 = "This is the second line of custom text";

        var controlView     = Session.Database.OpenView("SELECT * FROM Control");
        controlView.Execute();

        var rec             = Session.Installer.CreateRecord(12);
        rec.StringData(1)   = "VerifyReadyDlg";    // Dialog_
        rec.StringData(2)   = "CustomVerifyText1"; // Control - can be any name
        rec.StringData(3)   = "Text";              // Type
        rec.IntegerData(4)  = 25;                  // X
        rec.IntegerData(5)  = 60;                  // Y
        rec.IntegerData(6)  = 320;                 // Width
        rec.IntegerData(7)  = 85;                  // Height
        rec.IntegerData(8)  = 2;                   // Attributes
        rec.StringData(9)   = "";                  // Property
        rec.StringData(10)  = vText1;              // Text
        rec.StringData(11)  = "";                  // Control_Next
        rec.StringData(12)  = "";                  // Help
        controlView.Modify(MsiViewModify.InsertTemporary, rec);

        rec                 = Session.Installer.CreateRecord(12);
        rec.StringData(1)   = "VerifyReadyDlg";    // Dialog_
        rec.StringData(2)   = "CustomVerifyText2"; // Control - any unique name
        rec.StringData(3)   = "Text";              // Type
        rec.IntegerData(4)  = 25;                  // X
        rec.IntegerData(5)  = 160;                 // Y
        rec.IntegerData(6)  = 320;                 // Width
        rec.IntegerData(7)  = 65;                  // Height
        rec.IntegerData(8)  = 2;                   // Attributes
        rec.StringData(9)   = "";                  // Property
        rec.StringData(10)  = text2;               // Text
        rec.StringData(11)  = "";                  // Control_Next
        rec.StringData(12)  = "";                  // Help
        controlView.Modify(MsiViewModify.InsertTemporary, rec);

        controlView.Close();
    }
    catch (exc1)
    {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException("UpdatePropsWithSelectedWebSite", exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}


function LogException(loc, exc)
{
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "Exception {" + loc + "}: " + exc.number + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}

Khai báo CA Javascript:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.UpdateReadyDialog"
              BinaryKey="IisScript_CA"
              JScriptCall="UpdateReadyDialog_CA"
              Execute="immediate"
              Return="check" />
</Fragment>

Gắn CA vào một nút. Trong ví dụ này, CA được kích hoạt khi nhấp vào Tiếp theo từ Tùy chỉnhDlg:

<UI ...>
  <Publish Dialog="CustomizeDlg" Control="Next" Event="DoAction" 
           Value="CA.UpdateReadyDialog" Order="1"/>
</UI>

Câu hỏi SO liên quan: Làm thế nào tôi có thể thiết lập, trong thời gian chạy, văn bản sẽ được hiển thị trong ConfirmReadyDlg?


Chắc chắn đây không phải là JScript ngôn ngữ kịch bản lệnh windows chứ không phải là ngôn ngữ kịch bản lệnh DHTML. Có thể là một chút phạm vi, nhưng có thể hơi khó hiểu với một số người.
caveman_dick

5

Đặt các thành phần có thể được vá riêng lẻ trong các mảnh riêng của chúng

Nó dành cho cả việc tạo các trình cài đặt sản phẩm và các bản vá mà nếu bạn bao gồm bất kỳ thành phần nào trong một đoạn, bạn phải bao gồm tất cả các thành phần trong đoạn đó. Trong trường hợp xây dựng trình cài đặt, nếu bạn bỏ lỡ bất kỳ tham chiếu thành phần nào, bạn sẽ gặp lỗi liên kết từ light.exe. Tuy nhiên, khi bạn tạo một bản vá, nếu bạn bao gồm một tham chiếu thành phần duy nhất trong một đoạn, thì tất cả các thành phần đã thay đổi từ đoạn đó sẽ hiển thị trong bản vá của bạn.

như thế này:

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
            <File Id="SampleFile1" Source=".\$(var.Version)f\Sample1.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
           <File Id="SampleFile2" Source=".\$(var.Version)f\Sample2.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
           <File Id="SampleFile3" Source=".\$(var.Version)f\Sample3.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

thay vì điều này:

<Fragment>
    <DirectoryRef Id="SampleProductFolder">
        <Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
            <File Id="SampleFile1" Source=".\$(var.Version)\Sample1.txt" />
        </Component>

        <Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
           <File Id="SampleFile2" Source=".\$(var.Version)\Sample2.txt" />
        </Component>

        <Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
           <File Id="SampleFile3" Source=".\$(var.Version)\Sample3.txt" />
        </Component>
    </DirectoryRef>
</Fragment>

Ngoài ra, khi vá bằng chủ đề "Sử dụng hoàn toàn WiX" từ tệp trợ giúp WiX.chm, sử dụng quy trình này để tạo bản vá:

torch.exe -p -xi 1.0\product.wixpdb 1.1\product.wixpdb -out patch\diff.wixmst
candle.exe patch.wxs
light.exe patch.wixobj -out patch\patch.wixmsp
pyro.exe patch\patch.wixmsp -out patch\patch.msp -t RTM patch\diff.wixmst

Chỉ có phiên bản 1.1 của sản phẩm.wixpdb được xây dựng bằng cách sử dụng các thành phần trong các đoạn riêng biệt là không đủ. Vì vậy, hãy chắc chắn để phân chia chính xác sản phẩm của bạn trước khi vận chuyển.


5

In EULA từ Wix3.0 trở lên

1) Khi bạn biên dịch mã nguồn wix của mình, light.exe phải tham chiếu WixUIExtension.dll trong dòng lệnh. Sử dụng chuyển đổi dòng lệnh -ext cho việc này.

2) Nếu khi bạn thêm tham chiếu vào WixUIExtension.dll, dự án của bạn không biên dịch được, thì điều này rất có thể là do xung đột ID Dialog, tức là dự án của bạn đã sử dụng cùng một ID hộp thoại như một số hộp thoại tiêu chuẩn trong WixUIExtension.dll, cung cấp ID khác nhau cho các hộp thoại của bạn. Đây là vấn đề khá phổ biến.

3) Hộp thoại cấp phép của bạn phải có điều khiển ScrollableText với id "LicenseText". Wix tìm kiếm chính xác tên điều khiển này khi nó được in.

<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="160" Sunken="yes" TabSkip="no">
    <Text SourceFile="License.rtf" />
</Control>

và một PushButton đề cập đến hành động tùy chỉnh

<Control Type="PushButton" Id="PrintButton" Width="57" Height="17" X="19" Y="244" Text="Print">
    <Publish Event="DoAction" Value="PrintEula">1</Publish>
</Control>

4) Xác định CustomAction với Id = "PrintEula" như thế này:

<CustomAction Id="PrintEula" BinaryKey="WixUIWixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />

Lưu ý: BinaryKey khác với Wix3.0 so với Wix2.0 và phải chính xác là "WixUIWixca" (phân biệt chữ hoa chữ thường).

Khi người dùng nhấn nút, anh ấy / cô ấy sẽ được trình bày với Hộp thoại Chọn Máy in tiêu chuẩn và sẽ có thể in từ đó.


5
  • Chúng tôi hiển thị phiên bản sản phẩm ở đâu đó (nhỏ) trong màn hình đầu tiên của GUI. Bởi vì mọi người có xu hướng phạm sai lầm trong việc chọn đúng phiên bản mọi lúc. (Và giữ cho chúng tôi các nhà phát triển tìm kiếm lâu dài ..)

  • Chúng tôi đã thiết lập TFSBuild để tạo các biến đổi (tệp .mst) với cấu hình cho các môi trường khác nhau của chúng tôi. (Chúng tôi biết về tất cả các môi trường chúng tôi cần triển khai).

Vì bài đăng weblog gốc của Grant Holliday không hoạt động, tôi đã sao chép nội dung của nó ở đây:


Nhiệm vụ MSBuild để tạo các tệp Chuyển đổi MSI từ XMLMarch 11 2008

Trong bài viết trước của tôi, tôi đã mô tả cách bạn có thể sử dụng các tệp MSI Transform (* .mst) để tách các cài đặt cấu hình dành riêng cho môi trường khỏi gói MSI chung.

Mặc dù điều này cung cấp mức độ linh hoạt trong cấu hình của bạn, nhưng có hai mặt trái của tệp Transform:

  1. Chúng là một định dạng nhị phân
  2. Bạn không thể chỉnh sửa các chế độ khác Bạn phải áp dụng nó hoặc tạo lại nó để xem những thay đổi bao gồm.

May mắn thay, chúng ta có thể sử dụng Thư viện đối tượng Microsoft Windows Installer (c: windowssystem32msi.dll) để mở cơ sở dữ liệu MSI MSI và tạo các tệp biến đổi.

Tín dụng trở lại với Alex Shevchuk - Từ MSI đến WiX - Phần 7 - Tùy chỉnh cài đặt bằng Transforms để chỉ cho chúng tôi cách đạt được điều này với VbScript. Về cơ bản, tất cả những gì tôi đã làm là lấy ví dụ của Alex và sử dụng Interop.WindowsInstaller.dll Tôi đã triển khai một nhiệm vụ MSBuild. Nhiệm vụ MSBuild

Tải xuống mã nguồn và ví dụ Transforms.xml tại đây (~ 7Kb Giải pháp VS2008 đã nén)



2
Chúng tôi xác định lại WelcomeDlgTitle trong tệp bản địa hóa của tôi - hoạt động rất tốt! <String Id = "WelcomeDlgTitle"> {\ WixUI_Font_Bigger} Chào mừng bạn đến với Trình hướng dẫn cài đặt [ProductName] [ProductVersion] </ String>
saschabeaumont

5

Trước khi triển khai gói cài đặt, tôi luôn kiểm soát nội dung của nó.

Đó chỉ là một cuộc gọi đơn giản tại dòng lệnh (theo bài Terrences) mở dòng lệnh và nhập

msiexec /a Package.msi /qb TARGETDIR="%CD%\Extract" /l*vx "%CD\install.log%"

Điều này sẽ trích xuất nội dung gói vào một thư mục con 'Trích xuất' với đường dẫn hiện tại.


4

Thay vì ORCA sử dụng InstEd , đây là một công cụ tốt để xem các bảng MSI. Ngoài ra, nó có khả năng tìm hai gói khác nhau bằng cách Biến đổi -> So sánh với ...

Ngoài ra, phiên bản Plus với chức năng bổ sung có sẵn. Nhưng phiên bản miễn phí cũng cung cấp một sự thay thế tốt cho Orca.


4

Đăng ký hội đồng .NET cho COM Interop với khả năng tương thích x86 / x64

NB Đoạn này về cơ bản giống như REGASM hội.dll / codebase

Một vài điều đang diễn ra trong mẫu này vì vậy đây là mã và tôi sẽ giải thích nó sau đó ...

  <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <?include $(sys.CURRENTDIR)\Config.wxi?>
  <?if $(var.Win64) ?>
  <?define CLSIDRoots = "CLSID;Wow6432Node\CLSID"?>
  <?else ?>
  <?define CLSIDRoots = "CLSID"?>
  <?endif?>
  <!-- ASCOM Driver Assembly with related COM registrations -->
  <Fragment>
    <DirectoryRef Id="INSTALLLOCATION" />
  </Fragment>
  <Fragment>
    <ComponentGroup Id="cgAscomDriver">
      <Component Id="cmpAscomDriver" Directory="INSTALLLOCATION" Guid="{0267031F-991D-4D88-A748-00EC6604171E}">
        <File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />
        <RegistryKey Root="HKCR" Key="$(var.DriverId)"  Action="createAndRemoveOnUninstall">
          <RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
          <RegistryKey Key="CLSID">
            <RegistryValue Type="string" Value="$(var.DriverGuid)" />
          </RegistryKey>
        </RegistryKey>
        <?foreach CLSID in $(var.CLSIDRoots) ?>
        <RegistryKey Root="HKCR" Key="$(var.CLSID)" Action="none">
          <RegistryKey Key="$(var.DriverGuid)" Action="createAndRemoveOnUninstall">
            <RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
            <RegistryKey Key="InprocServer32">
              <RegistryValue Type="string" Value="mscoree.dll" />
              <RegistryValue Type="string" Name="ThreadingModel" Value="Both"/>
              <RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
              <RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
              <RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
              <RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
              <RegistryKey Key="!(bind.fileVersion.filDriverAssembly)" >
                <RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
                <RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
                <RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
                <RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
              </RegistryKey>
            </RegistryKey>
            <RegistryKey Key="ProgId" Action="createAndRemoveOnUninstall">
              <RegistryValue Type="string" Value="$(var.DriverId)" />
            </RegistryKey>
            <RegistryKey Key="Implemented Categories" Action="createAndRemoveOnUninstall" >
              <RegistryKey Key="{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Action="createAndRemoveOnUninstall" />
            </RegistryKey>
          </RegistryKey>
        </RegistryKey>
        <?endforeach?>
      </Component>
    </ComponentGroup>
  </Fragment>
</Wix>

Nếu bạn đang tự hỏi, đây thực sự là một Trình điều khiển Kính viễn vọng ASCOM .

Đầu tiên, tôi lấy lời khuyên từ phía trên và tạo một số biến platforma trong một tệp riêng biệt, bạn có thể thấy những biến đó nằm rải rác trong XML.

Phần if-then-other gần các giao dịch hàng đầu có khả năng tương thích x86 vs x64. Việc lắp ráp của tôi nhắm mục tiêu 'Bất kỳ CPU' nào trên hệ thống x64, tôi cần đăng ký hai lần, một lần trong sổ đăng ký 64 bit và một lần trong các Wow6432Nodekhu vực 32 bit . If-then-other thiết lập cho tôi điều này, các giá trị được sử dụng trong một foreachvòng lặp sau này. Bằng cách này, tôi chỉ phải tác giả các khóa đăng ký một lần (nguyên tắc DRY).

Phần tử tệp chỉ định dll lắp ráp thực tế đang được cài đặt và đăng ký:

<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />

Không có gì mang tính cách mạng, nhưng chú ý Assembly=".net"- chỉ riêng thuộc tính này sẽ khiến hội đồng được đưa vào GAC, đây không phải là điều tôi muốn. Sử dụng AssemblyApplicationthuộc tính để quay trở lại chính nó chỉ đơn giản là một cách ngăn chặn Wix đưa tệp vào GAC. Tuy nhiên, bây giờ Wix biết đó là một tập hợp .net, cho phép tôi sử dụng một số biến liên kết nhất định trong XML của mình, chẳng hạn như !(bind.assemblyFullname.filDriverAssembly)để lấy tên đầy đủ của tập hợp.


3

Đặt thuộc DISABLEADVTSHORTCUTStính để buộc tất cả các phím tắt được quảng cáo trong trình cài đặt của bạn trở thành các phím tắt thông thường và bạn không cần bao gồm khóa reg giả để được sử dụng làm phím tắt.

<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>

Tôi nghĩ Windows Installer 4.0 trở lên là một yêu cầu .


2

Đó là một cấu trúc đẹp nhưng dựa trên kinh nghiệm của tôi, tôi tự hỏi làm thế nào bạn giải quyết các điều kiện này:

A. Tất cả các bản cài đặt của bạn dường như hạ cánh ở cùng một đích. Nếu người dùng cần cài đặt cả 3 phiên bản cùng một lúc thì quy trình của bạn sẽ cho phép điều này. Họ có thể rõ ràng cho biết phiên bản nào của mọi thực thi mà họ đang kích hoạt không?

B. Làm thế nào để bạn xử lý các tệp mới tồn tại trong TEST và / hoặc ĐÀO TẠO nhưng chưa có trong LIVE?


Xin chào Blaine, A. Không họ không. InstallName nằm trong Config.wxi, đây là tệp duy nhất không được tham chiếu bởi svn: externals. Vì vậy, đây là duy nhất cho mỗi cài đặt, tức là mỗi sản phẩm. Đó cũng là lý do tại sao chúng tôi sửa đổi Hướng dẫn cho từng phiên bản. B. GOTO A. :) Chúng là các MSI riêng biệt với Mã nâng cấp riêng.
si618

1
Nhân tiện, tôi hiểu lý do tại sao bạn trả lời câu hỏi của tôi bằng một câu hỏi, nhưng một khi bạn nhận được đủ điểm rep, xin vui lòng chuyển câu hỏi của bạn sang câu trả lời, nếu không chủ đề sẽ khó theo dõi.
si618

2

Đây là một cách để giúp các dự án web lớn xác minh rằng số lượng tệp được triển khai khớp với số lượng tệp được tích hợp trong MSI (hoặc mô-đun hợp nhất). Tôi vừa mới chạy tác vụ MSBuild tùy chỉnh đối với ứng dụng chính của chúng tôi (vẫn đang trong quá trình phát triển) và nó đã nhặt được khá nhiều tệp bị thiếu, chủ yếu là hình ảnh, nhưng một vài tệp javascript đã bị trượt qua!

Cách tiếp cận này (nhìn vào bảng Tệp của MSI bằng cách nối vào mục tiêu AfterBuild của dự án WiX) có thể hoạt động cho các loại ứng dụng khác nơi bạn có quyền truy cập vào danh sách đầy đủ các tệp dự kiến.


2

Thực hiện cài đặt lại bắt buộc khi cài đặt không cho phép gỡ cài đặt hoặc cài đặt lại và không quay lại.

Tập lệnh VBscript được sử dụng để ghi đè cài đặt không gỡ cài đặt vì bất kỳ lý do gì ..

Dim objShell
set objShell = wscript.createObject("wscript.shell")

iReturn = objShell.Run("CMD /K MsiExec.exe /I ""C:\Users\TheUser\Documents\Visual Studio 2010\Projects\InstallationTarget\HelloInstaller\bin\Debug\HelloInstaller.msi"" REINSTALLMODE=vomus REINSTALL=ALL",,True)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.