rubrapack 매뉴얼←↑→

40 MSIX 패키지

Windows 가 MSIX 패키지를 읽고, 설치하고, 실행하게 하려면 프로그램이 무엇을 써야 하는가. 출처 표시는 구현하는 사람을 위한 패키지 형식와 같다: [spec] 은 Microsoft Learn(패키지 매니페스트와 블록 맵 스키마)과 ECMA-376 Part 2(Open Packaging Conventions), [observed] 는 Windows 자신의 패키징 API(Windows 의 일부인 AppxPackaging.dll 의 IAppxFactory/IAppxPackageWriter)가 쓴 패키지, 그리고 IAppxPackageReader 로 다시 읽고 Windows 11 에서 Add-AppxPackage 로 설치한 rubrapack 의 패키지다. rubrapack 은 여기 적은 것을 쓴다.

40.1 ZIP 압축 파일#

40.2 블록 맵(AppxBlockMap.xml)#

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<BlockMap xmlns="http://schemas.microsoft.com/appx/2010/blockmap"
          xmlns:b4="http://schemas.microsoft.com/appx/2021/blockmap" IgnorableNamespaces="b4"
          HashMethod="http://www.w3.org/2001/04/xmlenc#sha256">
  <File Name="data\text.txt" Size="218890" LfhSize="43">
    <Block Hash="(원 바이트 65536개의 base64 SHA-256)" Size="(이 블록의 압축 바이트 수)"/>
    ...
    <b4:FileHash Hash="(파일 전체의 base64 SHA-256)"/>
  </File>
  ...
</BlockMap>

40.3 [Content_Types].xml#

한 줄: 처음 쓰인 순서대로의 파일 확장자(소문자)마다 Default 하나, xml 은 application/vnd.ms-appx.manifest+xml, 그리고 application/vnd.ms-appx.blockmap+xml 인 Override PartName="/AppxBlockMap.xml". rubrapack 은 확장자 없는 파일마다 Override 를, 그리고 페이로드의 .xml 파일이 xml 기본을 차지했으면 /AppxManifest.xml 에 Override 를 더한다. [observed]

40.4 매니페스트(AppxManifest.xml)#

Windows 가 설치하고 시작하는 가장 작은 데스크톱 앱(네임스페이스 foundation/windows10, uap/windows10, restrictedcapabilities): [spec] [observed]

40.5 가상 레지스트리(Registry.dat, User.dat)#

패키지 뿌리의 레지스트리 하이브(REGF 형식: 레지스트리 하이브 파일(REGF)). Windows 11(26100)에서 패키지된 앱으로 잰 것: [observed]

40.6 가상 파일 시스템(VFS\...)#

VFS\<폴더> 아래의 파일은 앱에게 실제 자리에 있는 것으로 보이고, 실제 폴더는 바뀌지 않는다. 잰 것: ProgramFilesX64(%ProgramFiles%), SystemX64(System32), Common AppData(%ProgramData%) - 그리고 Microsoft Learn 이 말하는 대로 AppData 나 Local AppData 에는 없다. rubrapack 이 쓰는 다른 이름(ProgramFilesX86, ProgramFilesCommonX64/X86, SystemX86, Windows)은 Microsoft Learn 이 늘어놓은 것이다. [spec] [observed]

40.7 자원 색인(resources.pri) [observed]#

매니페스트의 ms-resource:Name 과, Assets\Logo.scale-200.png 로만 있는 로고 Assets\Logo.png 는 resources.pri 에서 찾는다. 공개된 명세는 없다. 여기 적은 것은 Windows SDK 의 makepri.exe 가 쓰는 배치 중 패키지에 필요한 만큼이고, makepri 자신의 덤프와 Windows 가 rubrapack 의 파일에서 그대로 읽어 낸 것이다. 숫자는 모두 리틀엔디언, 한정자·이름 표의 문자열은 따로 적지 않으면 UTF-16 이다.

40.8 확장#

rubrapack 이 앱의 <Extensions>(uap:VisualElements 뒤)에 쓰는 것, 그리고 이것뿐이다: 여기 행이 없는 원본 기능은 오류이고, 필요한 Windows 빌드가 패키지의 MinVersion 보다 높은 것은 min-version 을 올리라고 요구한다. [spec] Microsoft Learn, "Integrate your desktop app with Windows using packaging extensions" 와 요소 문서들. 마지막 열은 Windows 11(26100)에 패키지를 설치해 확인한 것이다. [observed]

원본요소(범주)네임스페이스최소 빌드기능Windows 11 에서 확인한 것
[assoc]uap:Extension windows.fileTypeAssociation > uap3:FileTypeAssociation(Name, Parameters) > uap:DisplayName, uap:SupportedFileTypes > uap:FileTypeuap, uap314393runFullTrust.rpx/.rpy 파일을 열면 프로그램이 그 파일과 함께 시작된다
[protocol]uap3:Extension windows.protocol > uap3:Protocol(Name, Parameters)uap314393runFullTrustscheme: URI 를 시작하면 프로그램이 그것과 함께 시작된다
[msix-extension] 별칭uap3:Extension windows.appExecutionAlias(Executable, EntryPoint) > uap3:AppExecutionAlias > desktop:ExecutionAlias(Alias)uap3, desktop14393runFullTrust별칭이 %LOCALAPPDATA%\Microsoft\WindowsApps 에 생기고 프로그램을 시작한다
[msix-extension] 시작 작업desktop:Extension windows.startupTask(Executable, EntryPoint) > desktop:StartupTask(TaskId, Enabled, DisplayName)desktop14393runFullTrust첫 시작 뒤에 등록된다
[shortcut] Desktopdesktop7:Extension windows.shortcut > desktop7:Shortcut(File $(Desktop)\<이름>.lnk, Icon, Arguments, Description)desktop719645runFullTrust사용자의 바탕화면에 바로가기가 생기고 프로그램을 시작한다
[shortcut] Programs, StartMenu없음: 앱 자신의 시작 메뉴 항목---시작 메뉴에 앱이 있다
[font]uap4:Extension windows.sharedFonts > uap4:SharedFonts > uap4:Font(File Fonts\<이름>), 첫 앱에uap415063-패키지가 설치되어 있는 동안 다른 프로그램이 글꼴을 보고, 제거하면 보지 못한다

40.9 묶음(.msixbundle)#

Windows 의 묶음 작성기(IAppxBundleWriter)가 만드는 대로, 패키지처럼 놓인 ZIP 압축 파일(데이터 서술자를 가진 ZIP64 항목): [observed]

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Bundle xmlns="http://schemas.microsoft.com/appx/2013/bundle" SchemaVersion="5.0" xmlns:b4="http://schemas.microsoft.com/appx/2018/bundle" xmlns:b5="http://schemas.microsoft.com/appx/2019/bundle" IgnorableNamespaces="b4 b5">
	<Identity Name="Example.App" Publisher="CN=Example" Version="1.0.0.0"/>
	<Packages>
		<Package Type="application" Version="1.0.0.0" Architecture="x64" FileName="Example.App_1.0.0.0_x64.msix" Offset="66" Size="61340">
			<Resources>
				<Resource Language="en-US"/>
			</Resources>
			<b4:Dependencies>
				<b4:TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.26100.0"/>
			</b4:Dependencies>
		</Package>
	</Packages>
</Bundle>

40.10 서명(AppxSignature.p7x)#

Windows 의 서명기(APPX_SIP_CLIENT_DATA 를 준 mssign32!SignerSignEx2; PowerShell 명령은 패키지에 서명하지 못한다)와 대조해 알아내고, rubrapack 이 서명한 패키지를 설치해 확인했다. [observed]

40.11 서명 없는 패키지 설치하기#

40.12 실제 예: 튜토리얼의 hello.msix#

튜토리얼 17장의 MSIX(--unsigned-test, x64)는 11119 바이트다. 첫 파일 hello.exe 의 로컬 머리로 시작한다:

오프셋바이트필드값
0x0050 4b 03 04서명PK\3\4
0x042d 00필요한 판45 (4.5, ZIP64)
0x0608 00플래그0x0008: 크기는 데이터 뒤에
0x0808 00방법8 (deflate)
0x0A00 00 21 00시각, 날짜1980-01-01 00:00
0x0E00 00 00 00 00 00 00 00 00 00 00 00CRC, 크기0 (데이터 기술자에)
0x1A09 00 00 00이름, 추가 필드 길이9, 0
0x1E68 65 6c 6c 6f 2e 65 78 65이름hello.exe

hello.exe 의 deflate 된 6706 바이트가 뒤따르고, 이어서 데이터 기술자가 온다: 50 4b 07 08 ac a3 c6 2e 32 1a 00 00 00 00 00 00 00 46 00 00 00 00 00 00 - PK\7\8, CRC-32 2EC6A3AC, 그리고 압축된 크기와 원래 크기를 8 바이트씩(6706, 17920).

AppxBlockMap.xml 에 있는 그 항목:

<File Name="hello.exe" Size="17920" LfhSize="39">
  <Block Hash="K7HbHzXLkhbwTo3dcUr0vs35DlYX27qMOexDOTR8e2k=" Size="6704"/>

파일이 64 KiB 보다 작아 블록은 하나다. Hash 는 원래 17920 바이트의 SHA-256 을 base64 로 쓴 것(여기서 계산한 값 K7HbHzXLkhbwTo3dcUr0vs35DlYX27qMOexDOTR8e2k=), Size 는 그 deflate 부분의 6704 바이트 - 압축된 크기 6706 에서 2 바이트 마지막 블록을 뺀 것 - 이고, LfhSize 는 로컬 머리의 39 바이트(30 + 이름 9 바이트)다.