WiX Hello¶
はじめに¶
WiX導入の第一歩として、WiX toolsetを使い、ごく簡単な構成のプログラムをMSI形式インストーラーとして作成する方法を紹介します。
本記事は次の環境で動作確認しています。
| OS | Windows 7 SP1 64bit 日本語版 | 
| WiX toolset | 3.10.2 | 
本記事の構成¶
本記事では最終的にWindows上に次のようにファイルをインストールするインストーラーを作成します。
C:\Progra Files
  +-- HelloWix
        +-- HelloWix.cmd
スタートメニュー(全ユーザーから見える)
  +-- HelloWix
        +-- HelloWix
デスクトップ(全ユーザーから見える)
  +-- HelloWix
	ステップ1として、C:\Program Filesにコマンドを入れるインストーラーを作成します。
ステップ2として、ステップ1に対してスタートメニューにショートカットを追加します。
ステップ3として、ステップ2に対してデスクトップ上にショートカットを追加します。
ステップ1¶
ステップ1では、次の構成をインストールするインストーラーを作成します。
C:\Progra Files
  +-- HelloWix
        +-- HelloWix.cmd
	
WiX定義の作成¶
まず、インストーラの設定をXMLファイルとして記述します。ステップ1のXMLのエレメント構成(階層)は次のとおりです。
Wix
  +-- Product
        +-- Package
        +-- Media
        +-- Directory
        +-- DirectoryRef
        +-- Feature  
	
XMLの雛形¶
作業ディレクトリを任意の場所に作成し、拡張子.wxsのファイルを1つ新規に作成します。ここでは、hellowix.wxsとします。トップレベル要素Wixを持ち、その属性xmlnsでXML名前空間を指定します。
<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
</Wix>
	
Product要素¶
Wix要素の下にProduct要素を記述します。Product要素にはインストールするファイル名、バージョン番号、作成者、インストールするプログラムを一意に識別する識別子、バージョンアップ時に参照する同じプログラムの別バージョンかを判断するアップグレード識別子などを属性に指定します。
次は、Product要素の雛形です。
  <Product Id="*" 
           Name="########" 
           Language="1041" Codepage="932" 
           Version="########" 
           Manufacturer="########" 
           UpgradeCode="########">
	ここで、########の部分に作成するインストーラーの情報を記述します。
  <Product Id="*" 
           Name="Hello WiX" 
           Language="1041" Codepage="932" 
           Version="1.0.0.0" 
           Manufacturer="TAKAHASHI,Toru" 
           UpgradeCode="d617721c-688a-4031-88e4-4943160d0e67">
	- Idの"*"は、GUIDをWiX toolsetに自動生成されることを指定しています
 - Name、Version、Manufactureは、インストール後コントロールパネルのプログラムの一覧等に表示される文字列です
 - Languageは、インストーラー画面に表示するテキストの言語を指定し、日本語の場合は「1041」とします
 - Codepageは、インストーラー画面に表示するテキストの文字コードを指定し、日本語版Windowsの場合は「932」(CP932,シフトJIS系のMicrosoft定義)とします
 - Versionは、最大4つまでの数値の組合せで、ピリオドで結合します
 - UpgradeCodeは、今後このプログラムのアップデートバージョンを作成するときに常に同じGUIDを記述します。
 
Package要素¶
Product要素の下にPackage要素を記述します。Package要素には、インストーラーの圧縮有無、対応するWindowsインストーラーの最低バージョン、作成者、説明、コメントなどを属性に指定します。
次は、Package要素の雛形です。
    <Package Compressed="yes" 
             InstallerVersion="200" 
             Manufacturer="########" 
             Description="########" 
             Comments="########" />
	ここで、########の部分に作成するインストーラーの情報を記述します。
    <Package Compressed="yes" 
             InstallerVersion="200" 
             Manufacturer="TAKAHASHI,Toru" 
             Description="Installs Hello Wix" 
             Comments="(c) 2016 TAKAHASHI,Toru" />
	- Compressedをyesにすると、インストールファイルを圧縮します
 - InstallerVersionは、このインストーラーをインストールするWindowsインストーラーの最低バージョンを指定します。200は2.0を指定し、これはWindows XP、Windows Server 2003の標準搭載バージョンで64bitに対応したものです(通常これを指定しておけばよいかと)
 - Manufacturerは、Product要素の属性にもありましたが、こちらで指定した文字列は、ファイルエクスプローラからmsiファイルのプロパティを開き、詳細タブで作成者欄に表示されます
 - Descriptionは、ファイルエクスプローラーからmsiファイルのプロパティを開き、詳細タブで件名欄に表示されます
 - Commenctsは、ファイルエクスプローラーからmsiファイルのプロパティを開き、詳細タブでコメント欄に表示されます
 
Media要素¶
Product要素の下にMedia要素を記述します。インストーラーファイルを分割しない限り(CD/DVDで複数毎構成になる等)、お決まりの記述で済みます。
次は、Media要素の雛形です。
    <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
	
Directory要素¶
Product要素の下にDirectory要素を記述します。ファイルをインストールする場所の階層構造を定義します。
次は、Directory要素の雛形です。Directory要素のトップはId属性とName属性が決まっています。
その下に、インストーラーがインストールする先のディレクトリ階層をDirectory要素のネストで定義します。
    <Directory Id="TARGETDIR" 
               Name="SourceDir">
      <Directory Id="########">
        <Directory Id="########" 
                   Name="########">
          :
        </Directory>
      </Directory>
    </Directory>
	今回のサンプルでは次のようになります。
    <Directory Id="TARGETDIR" 
               Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="HelloWix" 
                   Name="HelloWix">
        </Directory>
      </Directory>
    </Directory>
	- 最上位のDirectory要素のId属性にTARGETDIR、Name属性にSourceDirを指定すると、インストール先のルートディレクトリとなるので、基本はこの指定をDirectory要素の最上位とする。
 - ProgramFilesFolder は既定のIdで、32bit OSでは%PROGRAMFILES%、64bit OSでは
%PROGRAMFILES(x86)%を指定します。既定のIdを使用する場合はName属性の指定は不要(既にディレクトリが存在するため) - HelloWixはこのインストーラーが作成しファイルを格納するディレクトリで、IdとNameの2つの属性を指定します。
 
DirectoryRef要素¶
Product要素の下にDirectoryRef要素を記述します。インストールするファイルを定義します。
次は、DirectoryRef要素の記述例です。DirectoryRef要素は子要素にComponent要素を持ち、Component要素がインストーラーでインストールするものを識別します。Component要素の子要素にFile要素などが置かれます。
    <DirectoryRef Id="HelloWix">
      <Component Id="CMP_HelloWixCmd" 
                 Guid="3fb5be79-decd-40f6-8810-05aa8c860ce3">
        <File Id="FILE_HelloWixCmd" 
              Source="HelloWix.cmd" 
              KeyPath="yes" />
      </Component>
    </DirectoryRef>
	- 先のDirectory要素で定義したディレクトリのIdと同じ識別子をDirectoryRefのId属性に記述します
 - Component要素のGuid属性にはユニークなGUIDを記述します。
 - Component要素のId属性には接頭辞(CMP_)を付けておくと便利です
 - File要素のSource属性にファイル名を記述します(Wixファイルからの相対パス)
 - Files要素のId属性には接頭辞(FILE_)を付けておくと便利です
 - File要素のKeyPath属性をyesにすると、インストーラーはそのファイルがインストール済みかどうかチェックします(存在すればインストールしない、バージョン管理ファイルの場合はさらにバージョンチェックをしてインストールするか否か判定される)
 
Feature属性¶
Product要素の下にFeature要素を記述します。インストールする際にユーザーがインストールする/しないかを選択できる構成単位を定義します。選択をさせない場合は1つだけFeatureを定義します。
次は、DirectoryRef要素の記述例です。
    <Feature Id="MainProduct" 
             Title="MainProduct" 
             Level="1">
      <ComponentRef Id="CMP_HelloWixCmd" />
    </Feature>
	- インストールするComponentは必ずFeatureに関連付けします
 
Wix定義の全体¶
<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" 
           Name="Hello WiX" 
           Language="1041" Codepage="932" 
           Version="1.0.0.0" 
           Manufacturer="TAKAHASHI,Toru" 
           UpgradeCode="d617721c-688a-4031-88e4-4943160d0e67">
    <Package Compressed="yes" 
             InstallerVersion="200" 
             Manufacturer="TAKAHASHI,Toru" 
             Description="Installs Hello Wix" 
             Comments="(c) 2016 TAKAHASHI,Toru" />
    <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
    <Directory Id="TARGETDIR" 
               Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="HelloWix" 
                   Name="HelloWix">
        </Directory>
      </Directory>
    </Directory>
    <DirectoryRef Id="HelloWix">
      <Component Id="CMP_HelloWixCmd" 
                 Guid="3fb5be79-decd-40f6-8810-05aa8c860ce3">
        <File Id="FILE_HelloWixCmd" 
              Source="HelloWix.cmd" 
              KeyPath="yes" />
      </Component>
    </DirectoryRef>
    <Feature Id="MainProduct" 
             Title="MainProduct" 
             Level="1">
      <ComponentRef Id="CMP_HelloWixCmd" />
    </Feature>
  </Product>
</Wix>
	
インストールするファイル¶
インストールするファイル HelloWix.cmdを作成します。今回は簡潔なバッチファイルとします。
@ECHO OFF echo Hello Wix World! pause
WiX toolsetでMSIファイルの生成¶
作業ディレクトリに、インストール対象ファイル(HelloWix.cmd)とWiX定義ファイル(hellowix.wxs)を置きます。
C:\Users\torutk\work> dir /b HelloWix.cmd hellowix.wxs
WiX定義ファイルを指定してcandleコマンドを実行します。すると拡張子wixobjの中間ファイルが生成されます。
C:\Users\torutk\work> candle hellowix.wxs Windows Installer XML Toolset Compiler version 3.10.2.2516 Copyright (c) Outercurve Foundation. All rights reserved. hellowix.wxs C:\Users\torutk\work> dir /b HelloWix.cmd hellowix.wixobj hellowix.wxs
candleコマンドで生成した中間ファイルを指定してlightコマンドを実行します。すると、MSI形式インストーラーファイルが生成されます。
C:\Users\torutk\work> light hellowix.wixobj Windows Installer XML Toolset Linker version 3.10.2.2516 Copyright (c) Outercurve Foundation. All rights reserved. C:\Users\torutk\work> dir /b HelloWix.cmd hellowix.msi hellowix.wixobj hellowix.wixpdb hellowix.wxs
インストーラーファイルのインストールと実行¶
インストーラーを実行すると、次の簡素なインストーラー画面が表示され(UACの昇格の後)ファイルがインストールされます。
インストール後、コントロールパネル > プログラム > プログラムと機能 > プログラムのアンインストールまたは変更 に次のように登録されます。
インストールしたディレクトリをエクスプローラーで開きます。
HelloWix.cmdを実行するとバッチファイルのコマンドプロンプトが表示されます。
ステップ2¶
ステップ2として、ステップ1に対してスタートメニューにショートカットを追加します。
スタートメニューのディレクトリは、ユーザー固有の場所とシステム共通の場所と2箇所あります。
今回は、システム共通のスタートメニューにショートカットを追加します。
Wix定義の変更¶
システム共通にインストールするため、Package要素にInstallScope属性 perMachine を追加します。
    <Package Compressed="yes" 
             InstallerVersion="200" 
             Manufacturer="TAKAHASHI,Toru" 
             Description="Installs Hello Wix" 
-            Comments="(c) 2016 TAKAHASHI,Toru" />
+            Comments="(c) 2016 TAKAHASHI,Toru" 
+            InstallScope="perMachine" />
	ショートカットを配置するディレクトリを、Directory要素の階層に追加します。
     <Directory Id="TARGETDIR" 
                Name="SourceDir">
       <Directory Id="ProgramFilesFolder">
         <Directory Id="HelloWix" 
                    Name="HelloWix">
         </Directory>
       </Directory>
+      <Directory Id="ProgramMenuFolder"/>
     </Directory>
	- ショートカットをスタートメニュー直下に置く場合、Directory要素に既定定義済みのProgramMenuFolderを定義します。
InstallScope属性がperMachineの場合、ProgramMenuFolderは、C:\ProgramData\Microsoft\Windows\Start Menu\Programs を指します。InstallScope属性 ProgramMenuFolderの指すディレクトリ perMachine C:\ProgramData\Microsoft\Windows\Start Menu\Programs perUser C:\Uesrs\<ユーザー名>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs  
ショートカットのリンク先となるファイルの子要素にShortcut要素を定義します。
   <DirectoryRef Id="HelloWix">
                 Guid="3fb5be79-decd-40f6-8810-05aa8c860ce3">
        <File Id="FILE_HelloWixCmd" 
              Source="HelloWix.cmd" 
-             KeyPath="yes" />
+             KeyPath="yes">
+         <Shortcut Id="HelloWixStartMenuShortcut" 
+                   Directory="ProgramMenuFolder" 
+                   Name="Hello Wix Command" 
+                   Description="Launch Hello Wix Command" 
+                   Advertise="yes" />
+       </File>
+       <RemoveFile Id="DeleteHelloWixStartMenuShortcut" 
+                    Name="Hello Wix Command" 
+                    On="uninstall" />
       </Component>
     </DirectoryRef>
	- システム共通のスタートメニューにショートカットを配置するために、先のPackage要素のInstallScope属性perMachine指定と、Shortcut要素のAdvertise属性yes指定を組合せます。
 - アンインストール時にショートカットを削除するために、Component要素の子要素RemoveFile要素を定義します。Idは任意ですがName属性はアンインストールするショートカットのName属性と一致させます。
 
WiX toolsetでMSIファイルの生成¶
ステップ1と同じです。
C:\Users\torutk\work> candle hellowix.wxs Windows Installer XML Toolset Compiler version 3.10.2.2516 Copyright (c) Outercurve Foundation. All rights reserved. hellowix.wxs C:\Users\torutk\work> light hellowix.wixobj Windows Installer XML Toolset Linker version 3.10.2.2516 Copyright (c) Outercurve Foundation. All rights reserved. C:\Users\torutk\work> dir /b HelloWix.cmd hellowix.msi hellowix.wixobj hellowix.wixpdb hellowix.wxs C:\Users\torutk\work>
インストール¶
インストールそのものはステップ1と同じです。ステップ2では、インストール後にスタートメニューにショートカットとして登録されます。

ステップ3¶
ステップ3として、ステップ2に対してデスクトップにショートカットを追加します。
デスクトップのディレクトリは、ユーザー固有の場所とシステム共通の場所と2箇所あります。
今回は、どのユーザーでも利用できるよう、システム共通のデスクトップにショートカットを追加します。
Wix定義の変更¶
共通のデスクトップ(ディレクトリ)の下にショートカットを配置するので、次の修正をします。
- Directory階層定義にデスクトップを追加
 - コマンドのショートカットをデスクトップに置く定義をコマンドのComponent定義に追加
 
Directory階層への追加
     <Directory Id="TARGETDIR" 
                Name="SourceDir">
       <Directory Id="ProgramFilesFolder">
         <Directory Id="HelloWix" 
                    Name="HelloWix">
         </Directory>
       </Directory>
       <Directory Id="ProgramMenuFolder"/>
+      <Directory Id="DesktopFolder" />^M
     </Directory>
	コマンドのコンポーネント定義に追加
      <Component Id="CMP_HelloWixCmd" 
                 Guid="3fb5be79-decd-40f6-8810-05aa8c860ce3">
        <File Id="FILE_HelloWixCmd" 
              Source="HelloWix.cmd" 
              KeyPath="yes">
          <Shortcut Id="HelloWixStartMenuShortcut" 
                    Directory="ProgramMenuFolder" 
                    Name="Hello Wix Command" 
                    Description="Launch Hello Wix Command" 
                    Advertise="yes" />
+         <Shortcut Id="HelloWixDesktopShortcut" 
+                   Directory="DesktopFolder" 
+                   Name="Hello Wix Command" 
+                   Description="Launch Hello Wix Command" 
+                   Advertise="yes" />
        </File>
        <RemoveFile Id="DeleteHelloWixStartMenuShortcut" 
                    Name="Hello Wix Command" 
                    On="uninstall" />
+       <RemoveFile Id="DeleteHelloWixDesktopShortcut" 
+                   Name="Hello Wix Command" 
+                   On="uninstall" />
      </Component>
	
WiX toolsetでMSIファイルの生成¶
ステップ1、2と同じです。
C:\Users\torutk\work> candle hellowix.wxs Windows Installer XML Toolset Compiler version 3.10.2.2516 Copyright (c) Outercurve Foundation. All rights reserved. hellowix.wxs C:\Users\torutk\work> light hellowix.wixobj Windows Installer XML Toolset Linker version 3.10.2.2516 Copyright (c) Outercurve Foundation. All rights reserved. C:\Users\torutk\work> dir /b HelloWix.cmd hellowix.msi hellowix.wixobj hellowix.wixpdb hellowix.wxs C:\Users\torutk\work>
補足¶
GUIDの生成¶
WixEditを使うと簡単に生成できますが、手でXMLを作成する場合は、自力で作成する必要があります。
- Visual Studio(Windows SDK)をインストールしているPCでは、guidgenコマンドが利用可能
	
- 同じコマンドは、Microsoftから個別にダウンロード可能
 
 - LinuxやCygwinをインストールしているPCでは、uuidgenコマンドが利用可能
 - http://uuid.jp のレスポンス(バージョン4で生成した文字列)
 - プログラミング言語のライブラリを使って作成する
	
- Javaならjava.util.UUIDクラス
 - Pythonならuuidパッケージのuuid1(), uuid3(), uuid4(), uuid5()
 - Rubyならuuidtools
 
 - UUID生成仕様に沿ってプログラムを作って作成する
 
GUID(UUID)は、RFC 4122で規定され、生成方法の違いにより「バージョン」が5つ定義されています。
衝突可能性が低いのはMACアドレスと時刻から生成するバージョン1と思われますが、この方法で生成したUUID文字列からはMACアドレスが抜き出せてしまいます。
JavaのUUIDクラスはバージョン4(乱数による生成)です。
uuidgenコマンドはバージョン1またはバージョン4が生成可能です(デフォルトはバージョン4でオプション指定でバージョン1を生成)。
UUIDのバージョンについて¶
| バージョン | 内容 | 
|---|---|
| 1 | 時刻(100ナノ秒単位)とMACアドレスから生成 | 
| 2 | DCEで用いる。バージョン1の一部分をPOSIXユーザーIDないしグループIDで置き換えたもの | 
| 3/5 | ドメイン名等の一意な文字列のハッシュから生成、ハッシュにMD5を用いたのがバージョン3でSHA1を用いたのがバージョン5 | 
| 4 | 乱数から生成 | 
Java JDK 9以降でコマンドライン上でUUIDを生成¶
C:\Users\torutk> jshell jshell> System.out.println(UUID.randomUUID()); 132d768e-7afd-4b37-9293-a3d144e07597
advertised shortcutにしない方法¶
advertiseにした理由は、ショートカットのKeyPathとするレジストリにHKCU(ユーザー固有のレジストリ)を指定しないとWiX toolsetがエラーを吐くからでした。しかし、Product要素にProperty要素を指定しadvertiseを打ち消すという方法もあるようです。
https://moated.wordpress.com/2011/10/03/wix-%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB%E3%83%83%E3%83%88%E3%81%AEkeypath/
バージョンアップできるようにする¶
今回作ったHelloWixインストーラーは、バージョン番号を上げて新しいインストーラーを作成し、古いバージョンのHelloWixが入ったマシンでアンインストールをせずにに新しいバージョンのインストーラーを実行した場合、前のバージョンが削除されず新しいバージョンが入れられてしまいます。
期待する振る舞いは、古いバージョンのHelloWixが入ったマシンに新しいバージョンのHelloWixを入れると、自動で古いバージョンがアンインストールされ、新しいバージョンがインストールされることです。
    <Package Compressed="yes" 
             InstallerVersion="200" 
             Manufacturer="TAKAHASHI,Toru" 
             Description="Installs Hello Wix" 
             Comments="(c) 2016 TAKAHASHI,Toru" 
             InstallScope="perMachine" />
+   <MajorUpgrade DowngradeErrorMessage="既に[ProductName]の新しいバージョンがインストールされています。インストールを中止します。" />
    <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
	これで、新しいバージョンのHelloWixをインストールすると、古いバージョンがアンインストールされてから新しいバージョンがインストールされるようになりました。
なお、古いバージョンをインストールしようとすると、次のエラーメッセージダイアログが表示されインストールが中断しました。
同じバージョンを入れると・・・¶
ここで、同じバージョンの異なるインストーラ(ProductIdが異なる)を作成してインストールしてみたところ、前のインストールが残ったまま、別インストールとしてインストールされました(ファイルは同じ場所、同じコンポーネントIDなので上書き)。
MajorUpgrade要素の説明 を調べると、AllowSameVersionUpgrades属性がno(デフォルト)の場合、同じバージョンとUpgradeCodeを持つがProductCodeが異なるもののインストールを許容し、これらは別製品とみなすとあります。