[기술컬럼] Windows installer XML Toolset - 두 번째 이야기

Part 1을 통해 WIX의 정의, 특징, WIX의 장점과 단점에 대해 알아보았습니다.

이번 Part 2를 통해서는 실제 WIX를 통해 설치 패키지를 만들어 보도록 하겠습니다.
처음 보시는 분들에게는 다소 재미없고 지루할 수 있지만 즐거운 마음으로 시작해 보도록 하겠습니다.

Part 2


Part2에서는 아래의 것들을 다루려고 합니다.

  * Visual Studio Windows Installer XML 사용할  있도록 환경 구축하기
  * MSI 패키지 작성을 위한 스크립트 작성
  * Orca database table 조작하기
  * 마침표


1. Visual Studio  Windows Installer XML 사용할  있도록 환경 구축하기

Part1에서 말씀드렸다시피 WIX toolset은 Visual Studio에 Plug-in 형태로 추가가 가능합니다.
단, Wix 3.6 이상의 버전에서는 Visual Studio 2013로 사용이 가능합니다. 이전 버전에서는 Visual Studio 2013을 지원하고 있지 않습니다.

현재 WIX는 3.9 버전까지 릴리즈가 되어 있습니다.
전 3.8 버전 기준으로 환경을 구축하는 법을 설명하도록 하겠습니다.


http://wixtoolset.org/ 에서 WIX Toolset을 다운받습니다.


현재는 WIX v3.9가 최신 릴리즈입니다. 업데이트는 주기적으로 잘 되는것 같습니다.  
3.8버전을 다운받기 위해 See the latest release를 클릭합니다.


오른쪽에 공식 Release된 버전이 나열되어 있습니다. WiX v3.8을 클릭합니다.

맨위의 wix38.exe 설치파일을 클릭하여 다운받습니다.  Other Available downloads을 통해서는 binary  파일과 샘플 소스파일 등을 받아보실수 있습니다.

wix38.exe를 다운받았으면 더블클릭하여 설치를 진행해 봅니다.


















Wix Toolset 부분을 클릭하시면 wix3.8 버전이 설치됩니다.
설치가 완료되면 관련 구성파일은 모두 C:\Program Files\WiX Toolset v3.8 경로에 복사됩니다.

아래 bin 폴더를 보시면 Part1에서 설명드렸던 컴파일과 링크를 위한 candle.exe, light.exe 와 같은 파일들이 보입니다.


이제는 Visual Studio를 통해 새로운 프로젝트를 생성해 보도록 하겠습니다.

Visual Studio 2013을 실행합니다.
실행이 되었으면 정상적으로 Windows Intaller XML이 추가되어있는지 확인을 위해 
메뉴탭의 FILE -> NEW -> Project를 클릭합니다.

놀랍게도 왼쪽에 Windows Installer XML이라는 메뉴가 생성되었습니다.
그럼 기본적인 Setup Project를 만들어보겠습니다.
Name을 정하고 Setup Project를 선택하고 OK를 클릭합니다.

최초의 프로젝트가 만들어 졌습니다.  이제 설치패키지의 껍질은 만들어 진것입니다.



그런데, 빌드를 해보실까요? 빌드가 잘 되나요?
안타깝게도 빌드가 실패합니다. 새로 시작하는 것들은 항상 처음이 당황스럽게 마련이지
일단 Product Id와 Manufacturer 두가지를 확인하셔야 합니다,

1) Product Id

  * WIX는 xml기반의 markup language로 기술하게 되어 있습니다.
  * 각 부분은 속성(attribute)이 필요한데 일부 ID(Identification)은 GUID 형태의 특정 식별자가 필요합니다.
  * Visual Studio 에서 제공하는 TOOL로 Guid를 만들어 보겠습니다.
  * 메뉴 탭의 TOOLS -> Create GUID를 선택합니다.
  * GUID 한 개를 생성해서 Product Id 에 넣어줍니다.






















2) Manufacturer

  * 작성하는 사람의 식별이 필요합니다. 저는 “devguru co.,ltd”로 작성하도록 하겠습니다
    이 string은 패키지 설치 후 “프로그램 설치/삭제의 게시자”로 확인이 됩니다.

ICE validation

ICEs(Internal Consistency Evaluator)는 .msi 패키지를 생성하기 전에 merged module의 table을 검사하는 tool이라고 생각하시면 됩니다.

현재 merge module 없이 msi 패키지를 생성하므로 “Suppress ICE validation” 에 체크를 해 주셔야 합니다.

다시 빌드를 진행 해 보도록 하겠습니다.
빌드가 성공하고 SetupProject1.msi가 생성되었습니다.
SetupProject1.msi를 더블클릭하여 실행해 보겠습니다.

다음 창이 뜨고 빠르게 무언가 실행되는 것 같지만 바로 사라집니다.
우리가 만든 패키지는 UI없이 실행만 되는 패키지이기 때문에 
아무것도 하지 않고 사라지는 것처럼 보입니다.
시작이 반입니다. 이제 그 안에 내용을 채워 넣는 것이 우리가 해야 할 일이겠지요.



2. MSI 패키지 작성을 위한 스크립트 작성

특정 동작을 위한 패키지 작성을 위해서는 각 element 에 대한 이해를 해야합니다.
자세한 element 별 구성요소는 wix toolset의 manual을 보고 공부하셔야 합니다.
읽어 보셔야 할 것이 많은 것 같습니다. 
Wix Toolset에 관련된 sample code는 Wixtoolset 사이트의 소스 이외에 종류가 많지 않아 찾기는 쉽지는 않습니다. 그러므로 위의 manual을 열심히 이용하시는 것이 중요하다고 할 수 있겠습니다.


여기서는 기본적인 동작을 위한 스크립트 중심으로 작성을 해 보도록 하겠습니다.

msi 패키지를 빌드하기 위한 스크립트는 Product.wxs 입니다.
Product.wxs는 xml구조로 각 element가 Hierachy 구조로 작성되어 있습니다.
일종의 정보를 담고 있는 table 구조라고 보시면 될 것 같습니다.
기본 생성된 구조를 보면 다음과 같습니다.
<?xml version=”1.0″ encoding=”UTF-8″?>
    -> xml 의 버전을 명시하며 유니코드 기반의 문자 인코딩 방식을 사용합니다.
<Wix xmlns=”http://schemas.microsoft.com/wix/2006/wi“></Wix>
    -> 일종의 include와 같은 개념으로 이해하시면 됩니다. 웹상의 DATABASE를 통해 각 기능을 하는 메타데이터를 사용할 수 있습니다. Product.wxs의 스크립트는
<Wix>로 시작하여 </Wix>로 끝을 맺습니다.
<Product></Product>
    -> 현재 이 프로젝트 기능에 관련된 가장 상위의 element 입니다.
        이 아래에 Wix Schema인 다양한 element가 포함될 수 있습니다.
엄청난 양의 Wix Schema가 있네요.

Wix Schema를 다루기 전에 Wix의 UI 혹은 Util 관련 기능을 다를 수 있는 모듈을 먼저 프로젝트에 포함 시켜야 합니다.

왼쪽의 Solution Explorer를 보면 Reference 부분에 Wix의 UI및 특정 기능을 위한 Reference File을 추가해 보도록 하겠습니다.

우클릭 후 Add Reference를 클릭하면 Reference file을 선택할 수 있는 윈도우가 나타납니다.
여기서 기본적으로 WixUIExtension 과 WixUtilExtension을 검색하여 Add를 해줍니다.

개발하는 목적과 성격에 따라 다양한 Reference FIle을 선택하여 추가 할 수 있습니다. 자세한 내용은 Wix Toolset Manual을 참조하시면 되겠습니다.

Reference File이 추가되었습니다.

이제 Wix Schema의 element를 추가해 보도록 하겠습니다.

실제 스크립트 작성은 용도와 기능에 따라 많은 부분을 추가하여 사용할 수 있습니다. 자세한 사용 방법은 WixToolset Manual을 참조해야 하며 여기서는  중요한 element 몇 가지만 다루도록 하겠습니다.

1. Upgrade Id, Product Id
2. UI Control
3. Directory
4. Component
5. CustomAction

1. Product Id, Upgrade Id
  * Product Id
  * 현재의 설치 패키지를 구분하는 중요한 ID입니다. Id format은 GUID 형태이며, 해당 패키지의 수정사항이 발생하여 버전이 올라가야 할 경우에는 반드시 새로 GUID를 생성하여 빌드를 해야합니다.(필수)

1. Upgrade Id
  * 업그레이드를 위한 ID로서 한번 GUID가 정해졌으면 이후 변경하지 않습니다.

Product Id와 Upgrade Id를 추가해 보겠습니다.

<Sample Code>


  * Product Id와 Upgrade Id를 추가하였으며 각 sub element의 내용을 설정하였습니다.
  * Sub element는 일종의 condition이며 manual을 통해 자세한 내용을 확인하실 수 있습니다. 현재는 가장 낮은버전을 1.0.0.0으로 설정했고 max 버전을 9.9.9.9로 설정하였습니다.
  
2. UI Control
  * UI Control은 말 그대로 설치패키지의 UI를 커스터 마이징 하는 부분이라고 생각하시면 됩니다. UI의 크기, 형태, 종류를 설정하며 설치를 진행하는 페이지를 숨기거나 제거 할 수 있습니다.

  * WixUI Dialog
      - include 된 Wix Toolset을 사용하여 WixUI Dialog를 선택 할 수 있습니다.
      - http://wixtoolset.org/documentation/manual/v3/wixui/dialog_reference/
   * UI Dialog 순서 설정 및 Hide

<Sample Code>

<UI>
     <Publish Dialog=”WelcomeDlg” Control=”Next” Event=”NewDialog” Value=”InstallDirDlg” Order=”2″>1</Publish>
     <Publish Dialog=”InstallDirDlg” Control=”Back” Event=”NewDialog” Value=”WelcomeDlg” Order=”2″>1</Publish>      
     <!– Hide Repair sequence –>
     <Publish Dialog=”MaintenanceWelcomeDlg” Control=”Next” Event=”NewDialog” Value=”VerifyReadyDlg” Order=”2″>1</Publish>
     <Publish Dialog=”MaintenanceWelcomeDlg” Control=”Next” Event=”[WixUI_InstallMode]” Value=”Remove” Order=”2″>1</Publish>
     <Publish Dialog=”VerifyReadyDlg” Control=”Back” Event=”NewDialog” Value=”MaintenanceWelcomeDlg” Order=”2″>Installed</Publish>
    </UI>
-> NEXT 버튼 과 Back 버튼에 따른 Event로 발생하는 페이지 설정 가능


3. Directory
    * 패키지를 설치할 디렉토리, 경로 설정을 위한 element를 설정함.
<Sample code>

<Directory Id=”TARGETDIR” Name=”SourceDir”>
<Directory Id=”ProgramFilesFolder”>
<Directory Id=”INSTALLFOLDER” Name=”devguru” />
</Directory>
</Directory>
</Directory>
-> INSTALLFOLDER 속성으로 devguru라는 폴더 지정
-> 설치 경로는 “Program Files\devguru” 가 됨.



4.  Component

    * Directory element 내부에서 설정하는 element로 어떠한 컴포넌트를 설치할 것인가 지정하기 위한 element.

<Sample code>
<Component Id=”ProductComponent1″ Guid=”17e13748-8d44-47f6-b020-66d29f8a84fe”>
             <File Id=”devguru_launches_program” Name=”devguru_launches.exe”
                   Vital=”yes” DiskId=”1″ src=”..\..\bin\release\devguru_launches.exe”>
               <Shortcut Id=”desktopShortcut” Name=”Devguru Launches”  
         Directory=”DesktopFolder” />            
     </File>
</Component>
-> Directory 아래 설치할 컴포넌트 1번의 이름이 ProductComponent1이며 각 컴포넌트마다  고유의 GUID 가 필요
-> 해당 컴포넌트에서는 File Id로 명명된 파일을 실행시켜주며 해당 파일은 ..\..\bin\release\devguru_launches.exe 경로에 있는 것을 사용함.


5. CustomAction
    * Wix의 각 step 별로 수행되어야 하는 기능들을 순서에 따라 나열하며 실행 condition을 설정하는 부분입니다.
<Sample code>
<InstallExecuteSequence>
    <Custom Action =”InstallFiles” After=”InstallInitialize”>NOT REMOVE</Custom>
   <Custom Action=”TerminateApp” Before=”StopServices”>Installed OR REMOVE~=”ALL”</Custom>
</InstallExecuteSequence>
-> InstallExecuteSequence 의 element에서 CustomAction은 현재 설정할 Action을 어떤 동작 이전/이후에 수행할 것인가, 그리고 이미 설치된 상태에서만 설치할 것인가, 새로 설치할 때 동작을 수행할 것인가 condition을 정할 수 있음





기본 element 만 추가하여 만든 msi 패키지를 한번 실행해 보도록 하겠습니다.

 1)













2)











 3)




















 4)













그럴듯하게 설치 인스톨 패키지가 만들어 졌습니다.
인스톨의 UI 및 icon 변경도 사용자가 직접 커스터마이징 할 수 있습니다.
또한, merged module 작성을 위한 msm 파일 빌드와 외부의 모듈을 msi, msm내로 삽입하여 사용할 수 있습니다.(DLL, VScript, CScript) 세부적인 사용설명은 역시 WixToolset Manual을 참조하시면 됩니다.


3. Orca database table 조작하기

WIX로 작성한 Windows Installer는 xml 기반의 메타데이터 테이블로 구성된 집합체입니다. xml이기때문에 문서구조와 동일한 형태로 구성되어 있습니다. 이 구조 데이터 테이블을 조작할 수 있는 툴이 있는데 이것이 바로 Orca입니다.
현재 이 Orca 툴은 Windows SDK에 포함되어 배포되고 있으며 Windows Sdk를 설치하면 사용하실 수 있습니다.

<Windows SDKs>
http://www.microsoft.com/en-us/download/details.aspx?id=3138

Orca를 다운로드 받은 후 실제 우리가 만든 패키지를 orca로 들여다 보겠습니다.

  1. orca.exe 실행 

  1. orca의 File->Open 하여 우리가 작성한 패키지 불러오기
 
-> Dialog 탭을 선택하면 현재 우리 패키지에서 사용하는 각 Dialog를 볼 수 있습니다.






















-> InstallExecuteSequence의 컴포넌트 실행 순서와 각 Step의 Condition을 확인 할 수 있으며 변경도 가능합니다.



-> InstallUISequence의 Condition 및 Sequence 변경이 가능합니다.



4. 마침표

Windows Installer XML로 설치 패키지를 작성해 보았습니다.

Part2에서는 기본적인 Element 만을 사용하여 패키지를 작성했지만, 실제 Wix의 사용 범위는 생각보다 넓습니다. 무엇보다 가장 큰 장점은 무료로 개발 할 수 있다는 것이라고 할 수 있을 것 입니다. 하지만, 지원하는 여러 기능들을 직접 작성해야 하고 무엇보다 디버깅이 쉽지 않다는 단점 또한 존재합니다.

새로운 기술을 받아들여 사용하기 위해서는 무엇보다 사용하는 개발자의 의지와 노력이 무엇보다 중요하지 않을까 생각이 듭니다. 이 칼럼을 통해 Wix를 처음 접하는 개발자에게 조금이나마 도움이 되길 바라며 마지막으로 설치 로그를 통한 문제 확인 노하우를 잠깐 언급하고 마칠까 합니다.
감사합니다.

 msi 패키지로 로그를 생성하는 방법
  1. 실행패키지가 있는 곳에서 commandline 창을 실행2
  2. msiexec /i “Devguru_launches_utility.msi” -l*v aaa.txt 실행
  3. 동일 경로에 aaa.txt라는 로그 파일이 생성됨.(파일이름은 임의대로 사용)


<생성된 로그파일 일부 내용>

=== Verbose logging started: 2014-11-26  14:33:32  Build type: SHIP UNICODE 5.00.7601.00  Calling process: C:\Windows\system32\msiexec.exe ===
MSI (c) (4C:E0) [14:33:32:217]: Font created.  Charset: Req=129, Ret=129, Font: Req=굴림, Ret=굴림

MSI (c) (4C:E0) [14:33:32:217]: Font created.  Charset: Req=129, Ret=129, Font: Req=굴림, Ret=굴림

MSI (c) (4C:E8) [14:33:32:233]: Resetting cached policy values
MSI (c) (4C:E8) [14:33:32:233]: Machine policy value ‘Debug’ is 0
MSI (c) (4C:E8) [14:33:32:233]: ******* RunEngine:
          ******* Product: Devguru_launches_utility.msi
          ******* Action:
          ******* CommandLine: **********
MSI (c) (4C:E8) [14:33:32:249]: Machine policy value ‘DisableUserInstalls’ is 0
MSI (c) (4C:E0) [14:33:32:311]: Font created.  Charset: Req=0, Ret=0, Font: Req=, Ret=Arial

MSI (c) (4C:E0) [14:33:32:327]: Font created.  Charset: Req=0, Ret=0, Font: Req=, Ret=Arial

MSI (c) (4C:E8) [14:33:32:670]: Cloaking enabled.
MSI (c) (4C:E8) [14:33:32:670]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (4C:E8) [14:33:32:685]: End dialog not enabled
MSI (c) (4C:E8) [14:33:32:685]: Original package ==> D:\devguru\project\uml295_user_launches_utility\trunk\src\Installer\bin\Debug\Devguru_launches_utility.msi
MSI (c) (4C:E8) [14:33:32:685]: Package we’re running from ==> C:\Windows\Installer\615de40.msi
MSI (c) (4C:E8) [14:33:32:732]: APPCOMPAT: Uninstall Flags override found.
MSI (c) (4C:E8) [14:33:32:732]: APPCOMPAT: Uninstall VersionNT override found.
MSI (c) (4C:E8) [14:33:32:732]: APPCOMPAT: Uninstall ServicePackLevel override found.
MSI (c) (4C:E8) [14:33:32:748]: APPCOMPAT: looking for appcompat database entry with ProductCode ‘{76A1AD15-C01E-4A9E-8861-DF1927EFA5A0}’.
MSI (c) (4C:E8) [14:33:32:763]: APPCOMPAT: no matching ProductCode found in database.
MSI (c) (4C:E8) [14:33:32:966]: MSCOREE not loaded loading copy from system32
MSI (c) (4C:E8) [14:33:33:091]: Note: 1: 2205 2:  3: MsiFileHash
MSI (c) (4C:E8) [14:33:33:091]: Machine policy value ‘DisablePatch’ is 0
MSI (c) (4C:E8) [14:33:33:091]: Machine policy value ‘AllowLockdownPatch’ is 0
MSI (c) (4C:E8) [14:33:33:091]: Machine policy value ‘DisableMsi’ is 0
MSI (c) (4C:E8) [14:33:33:091]: Machine policy value ‘AlwaysInstallElevated’ is 0
MSI (c) (4C:E8) [14:33:33:091]: User policy value ‘AlwaysInstallElevated’ is 0
MSI (c) (4C:E8) [14:33:33:091]: Product {76A1AD15-C01E-4A9E-8861-DF1927EFA5A0} is admin assigned: LocalSystem owns the publish key.
MSI (c) (4C:E8) [14:33:33:091]: Product {76A1AD15-C01E-4A9E-8861-DF1927EFA5A0} is managed.



post by
이형석 연구원