많은 Windows API(푸시 알림, 백그라운드 작업, 공유 대상, 시작 작업, Windows AI API)는 앱에 패키지 ID 있어야 합니다. 개발 중에는 테스트할 때마다 전체 MSIX 설치 관리자를 빌드하지 않고도 애플리케이션에 실시간으로 ID를 부여할 수 있는 winapp의 두 가지 명령을 사용할 수 있습니다.
패키징 프로젝트에서 Visual Studio 사용하시겠습니까? 패키지된 프로젝트에 이미 Visual Studio 사용하고 있는 경우 디버깅을 위해 winapp이 필요하지 않을 수 있습니다. Visual Studio F5에서 패키지 등록, ID, AUMID 활성화, 디버거 첨부 파일 및 활성화 코드 디버깅을 이미 처리하고 있습니다. 또한 다양한 고급 시나리오에 대해 디버그 → 기타 디버그 대상 → 설치된 앱 패키지를 디버그 기능을 제공합니다. 아래 워크플로는 VS Code 사용자, 터미널 기반 워크플로 및 VS가 기본적으로 패키지하지 않는 프레임워크 (Rust, Flutter, Tauri, Electron, 일반 C++)에 가장 유용합니다.
두 가지 방법: winapp run vs create-debug-identity
winapp run |
create-debug-identity |
|
|---|---|---|
| 등록하는 내용 | 전체 분리 레이아웃 패키지(전체 폴더) | 스파스 패키지(단일 exe) |
| 앱 시작 방법 | winapp에서 시작됨(AUMID 활성화 또는 실행 별칭) | exe를 직접 시작합니다(명령줄, IDE 등). |
| MSIX 설치 시뮬레이션 | 예 - 프로덕션 동작에 가장 가깝습니다. | 아니요 - 스파스 ID만 |
| 파일이 제자리에 유지 | AppX 레이아웃 디렉터리에 복사됨 | 예 - exe가 원래 경로에 유지됩니다. |
| 정체성 범위 | 전체 폴더 내용(exe, DLL, 자산) | 단일 실행 파일 |
| 디버거 친화적 | 시작 후 PID에 연결하거나 --no-launch을 사용하여 별칭을 통해 실행하십시오. |
IDE의 디버거에서 직접 실행 - exe 파일은 항상 고유한 정체성을 가지고 있습니다. |
| 콘솔 앱 지원 |
--with-alias 터미널에서 stdin/stdout 유지 |
터미널에서 직접 exe 실행 |
| 최적입니다 | 대부분의 프레임워크(.NET, C++, Rust, Flutter, Tauri) | Electron 또는 전체 IDE 디버거 제어가 필요할 때(F5) |
어떤 것을 언제 사용할지
기본값: winapp run
대부분의 개발 워크플로에 사용합니다 winapp run . 실제 MSIX 설치를 시뮬레이션합니다. 앱은 프로덕션 환경에서와 동일한 ID, 기능 및 파일 연결을 가져옵니다.
# Build your app, then:
winapp run .\build\output
다음과 같은 경우 create-debug-identity를 사용합니다.
-
Exe 파일은 빌드 결과물과 별도로 존재합니다 — 예를 들어,
electron.exe이(가)node_modules/에 위치한 Electron 앱. - 시작 코드를 디버그해야 하며 AUMID 시작 후 디버거를 충분히 빠르게 연결할 수 없습니다.
-
AUMID를 사용하여 시작할 수 없고 시작된 프로세스
create-debug-identity에서 ID가 필요한 일부 디버거에서 exe를 등록하여 시작 방법에 관계없이 ID가 있도록 합니다. - 스파스 패키지 동작을 구체적으로 테스트하고 있습니다 (AllowExternalContent, TrustedLaunch)
# Register identity for an exe, then launch it however you want:
winapp create-debug-identity .\bin\Debug\myapp.exe
.\bin\Debug\myapp.exe # or F5 in your IDE
디버깅 시나리오
시나리오 A: ID를 사용하여 실행
가장 간단한 워크플로인 빌드, ID로 실행, 완료.
winapp run .\build\Debug
Winapp은 폴더를 느슨한 레이아웃 패키지로 등록하고 앱을 시작합니다. ID가 필요한 API는 즉시 작동합니다. 대부분의 개발 및 테스트 시나리오를 다룹니다.
현재 터미널에서 stdin/stdout이 필요한 콘솔 앱 의 경우 다음을 추가 --with-alias합니다.
winapp run .\build\Debug --with-alias
시나리오 B: 실행 중인 앱에 디버거 연결
winapp run로 시작하고 PID를 기록한 다음 IDE의 디버거를 연결합니다.
winapp run .\build\Debug
# Output: Process ID: 12345
그런 다음, IDE에서 다음을 수행합니다.
- VS Code: 실행 및 디버그 → "연결" 구성 선택(아래 IDE 설정 참조)
-
WinDbg:
windbg -p 12345
제한: 연결하기 전에 실행되는 코드가 누락됩니다. 시작 디버깅의 경우 시나리오 D(
create-debug-identity)를 사용합니다.
시나리오 C: ID를 등록한 다음, AUMID 또는 IDE의 별칭을 통해 시작합니다.
--no-launch 패키지를 등록한 다음 run에서 보고된 AUMID 또는 IDE의 실행 별칭을 통해 앱을 시작합니다.
1단계: 시작하지 않고 패키지를 등록합니다.
winapp run .\build\Debug --no-launch
2단계: AUMID 또는 실행 별칭 (exe 직접이 아님)을 통해 시작하도록 IDE를 구성합니다.
- AUMID를 사용하여 시작: 명령을
start shell:AppsFolder\<AUMID>사용합니다.winapp run는 앱이 등록될 때 AUMID를 출력합니다. - 별칭을 사용하여 시작: 별칭은 매니페스트에 정의되어야 합니다(
Package.appxmanifest기본 설정,appxmanifest.xml지원됨).
중요: 단순히 빌드 폴더에서 exe를 시작하면 ID가 부여 되지 않습니다 . AUMID 활성화 또는 실행 별칭을 통해 앱을 시작해야 합니다. 이것이 느슨한 레이아웃 패키지의 작동 방식입니다. ID는 exe 파일이 아닌 활성화 경로에 연결됩니다.
시나리오 D: ID를 사용하여 IDE에서 시작(시작 디버깅)
이는 전체 IDE 제어를 사용하여 시작 코드를 디버깅하는 가장 좋은 방법입니다. IDE의 디버거는 첫 번째 명령에서 프로세스를 제어하고 exe는 시작 방법에 관계없이 ID를 가집니다.
winapp create-debug-identity .\build\Debug\myapp.exe
이제 터미널, VS Code의 F5, 스크립트에서 원하는 방식으로 exe를 시작합니다. Windows가 exe 파일을 직접 가리키는 sparse 패키지를 등록했기 때문에 exe에 정체성이 부여됩니다.
winapp run의 차이점:create-debug-identity을 사용하면 ID는Add-AppxPackage -ExternalLocation를 통해 exe 파일 자체에 연결됩니다. IDwinapp run는 레이아웃 패키지와 느슨하게 연결되어 있으므로, 앱은 AUMID 또는 별칭을 통해 시작해야 합니다. 이렇게 하면create-debug-identityexe를 직접 시작하고 디버그하는 데 IDE가 필요할 때 더 나은 선택을 할 수 있습니다.
exe 경로가 원본 디렉터리와 다른 Electron 앱 에 가장 적합한 방법이기도 합니다.
시나리오 E: 디버그 출력 및 크래시 진단 캡처
OutputDebugString 메시지와 첫 기회의 예외를 인라인으로 캡처합니다. 프레임워크 노이즈(WinUI, COM, DirectX 내부 추적)는 콘솔에서 필터링되므로 앱의 디버그 메시지만 표시됩니다. 모든 항목이 전체 조사를 위해 로그 파일에 기록됩니다.
앱이 충돌하면 미니덤프가 자동으로 캡처되고 분석됩니다.
winapp run .\build\Debug --debug-output
충돌 시 출력에는 원본 파일 및 줄 번호가 포함된 예외 유형, 메시지 및 스택 추적이 포함됩니다(빌드 출력 폴더의 PDB에서 확인됨). 관리되는(.NET) 크래시는 외부 도구 없이 즉시 분석됩니다. 네이티브(C++/WinRT) 크래시가 모듈 이름과 오프셋을 표시합니다. 완전한 함수 이름을 얻으려면 PDB 심볼을 다운로드할 --symbols를 추가하세요.
winapp run .\build\Debug --debug-output --symbols
중요: 그러면 winapp이 디버거로 연결됩니다. Windows 프로세스당 하나의 디버거만 허용하므로 Visual Studio, VS Code 또는 WinDbg도 연결할 수 없습니다.
WinUI 저장된 예외 분류
대부분의 WinUI 충돌은 XAML 이벤트 처리기 내부에서 시작되며, 나중에 디스패처에서 다시 발생하는 보관된 예외(0xC000027B)로 나타나므로 일반적인 스택 추적은 더 이상 실제 원인을 가리키지 않습니다. 충돌한 앱이 로드되었을 때 Microsoft.UI.Xaml.dll, winapp은 저장된 예외와 네이티브 XAML 디스패치 체인(Microsoft.UI.Xaml → CXcpDispatcher → CoreMessagingXP → CLR 호스트)을 디코딩하는 추가 트리아지 단계를 자동으로 수행합니다. 결과는 디버그 로그에 추가됩니다. 플래그가 필요하지 않습니다. WinUI 덤프에 대해 자동으로 사용하도록 설정됩니다. 디스패치 체인에서 완전히 해석된 함수 이름에 --symbols를 추가합니다.
이를 가능하게 하려면, winapp은 첫 번째 기회(first-chance) 스레드 컨텍스트를 유지한 상태에서 종료를 유발한 stowed exception의 레코드(및 stowed-exception 배열을 가리키는 해당 매개 변수)와 함께 크래시 덤프를 캡처하므로, 표준 관리 코드 분석은 여전히 원래 사용자 프레임을 복구 하고 triage 단계는 stowed exception을 찾을 수 있습니다.
이 패스는 WinUI 팀의 WinDbg JavaScript 확장으로 DbgEng을 호스트합니다. 네이티브 디버깅 엔진(dbgeng.dll 및 친구)은 NuGet에서 제공되며, JsProvider.dll NuGet에 없는 JavaScript 스크립팅 호스트는 공식 WinDbg 다운로드에서 직접 처음 사용할 때 페치됩니다(전체 패키지가 아니라 필요한 수백 킬로바이트만 읽습니다).
JsProvider.dll는 엔진과 동일한 빌드여야 하므로(일치하지 않는 프로바이더를 로드하면 시작 시 디버거가 충돌하므로), 롤링 "current" 릴리스가 아니라 빌드가 NuGet 엔진과 일치하는 특정 WinDbg 번들에 고정되며, 가져온 후에는 두 빌드를 비교합니다. 빌드가 일치하지 않으면 이를 거부하고, 아무런 이유 표시 없이 조용히 충돌하는 대신 명확한 사유와 함께 트리아지를 건너뜁니다. 디버거 패키지는 특정 버전에 고정되어 있으며, 해당 네이티브 DLL이 추출되고 로드되기 전에 컴파일 시 내장된 SHA-512 콘텐츠 해시와 대조해 검증됩니다(확장은 고정된 해시와 대조해 검증됨). 또한 JsProvider.dll에는 유효한 Microsoft Authenticode 서명이 있어야 하며, 이 서명은 전체 인증서 체인의 해지 여부를 확인하는 방식으로 검사됩니다(오프라인 상태에서 해지 데이터에 접근할 수 없을 때는 서명만 확인하는 방식으로 대체되지만, 해지된 인증서는 항상 거부함). 다운로드는 임시 파일로 준비되고, 확인된 다음, 캐시에 원자성으로 게시되므로 동시 실행은 부분적으로 작성되거나 확인되지 않은 DLL을 관찰하지 않습니다. 실행할 때마다 캐시된 바이너리를 다시 검사합니다(JsProvider.dll 서명과 해당 엔진 빌드의 일치 여부, 그리고 엔진 DLL이 유효한 PE 이미지인지 확인). 따라서 잘렸거나 내용이 어긋난 캐시는 실행할 때마다 실패하는 대신 다시 받아 자체 복구됩니다. 즉, 미러된 또는 침해된 피드는 변경된 바이너리로 대체될 수 없으며, 검증에 실패하면 확인되지 않은 코드를 로드하는 대신 선별 처리를 건너뜁니다. 모든 항목이 winapp 전역 디렉터리 아래에 캐시되므로 후속 실행은 오프라인 상태입니다. 환경에서 이러한 다운로드를 차단하는 경우 Windows SDK를 통해 Windows 디버깅 도구를 설치하거나 환경 변수를 이미 포함된 WINAPP_DBGTOOLS_DIR 디버거 디렉터리로 dbgeng.dll설정합니다JsProvider.dll.
WINAPP_DBGTOOLS_DIR이 설정되면 그 설정이 기준이 되며 해당 디렉터리만 참조됩니다. 따라서 해당 디렉터리가 불완전하면 로그에는 이미 설정된 변수를 설정하라고 제안하는 대신 누락된 구체적인 구성 요소(dbgeng.dll 및/또는 JsProvider.dll)의 이름이 기록됩니다. 트리아지가 성공하면 콘솔에 한 줄짜리 판정(스토우드 예외의 오류 코드/메시지)이 표시됩니다. 이진 파일을 가져올 수 없으면 트리아지 단계는 건너뛰며(표준 관리 코드/네이티브 분석은 계속 실행됨), 콘솔에 그 사실이 표시되고 로그에는 그 이유가 설명됩니다.
트리아지 단계는 단기간 실행되는 자식 프로세스에서 실행됩니다. 이는 필수입니다: winapp의 주 프로세스는 덤프를 캡처하고 분석하는 동안 시스템 dbghelp.dll을 로드하고, 최신 엔진 dbgeng.dll은 이미 상주 중인 이전 버전에 바인딩할 수 없습니다. 따라서 새 프로세스는 엔진에 깨끗한 로더 상태를 제공합니다. 또한 stowed exception 구조를 디코딩하려면 운영 체제 기호(combase.dll)도 필요하며, 이 기호는 --symbols Microsoft 공용 기호 서버에서 다운로드합니다. 해당 서버에 기호가 게시되지 않은 빌드에서는 분류 패스가 여전히 stowed exception을 식별하지만 이를 완전히 풀어 내지는 못합니다.
IDE 설정
VS Code
WinApp VS Code 확장은 패키지 ID로 앱을 시작하고 디버거를 연결할 수 있는 사용자 지정 winapp 디버그 유형을 제공하며, 이 모든 작업을 F5 키 한 번으로 수행할 수 있습니다.
Visual Studio Marketplace에서 설치합니다. 원본 및 문제 추적기는 microsoft/WinAppVSCE 리포지토리에 라이브로 저장됩니다.
ID를 사용하여 F5 디버깅 한 번 누르기
시작 구성을 winapp에 추가합니다..vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "winapp",
"request": "launch",
"name": "WinApp: Launch and Attach"
}
]
}
F5 키를 누르면 다음을 수행합니다.
- 확장은 작업 영역을 검색하여
.exe파일이 포함된 빌드 출력 디렉터리를 찾습니다. - 실행할 빌드 폴더를 선택하거나 프롬프트를 건너뛰도록 설정합니다
inputFolder. -
winapp run를 통해 앱을 시작하여 패키지 ID를 제공합니다. - 자식 디버그 세션은 지정한 디버거를 사용하여 실행 중인 프로세스에 연결됩니다.
디버거가 연결되면 전체 VS Code 디버깅 환경을 얻게 됩니다. 즉, 여백을 클릭하여 중단점을 설정하고, 코드를 한 줄씩 실행하고(F10), 함수를 한 단계씩 실행(F11), 변수 창에서 변수를 검사하고, 디버그 콘솔에서 식을 평가합니다. 앱은 전체 패키지 ID를 사용하여 실행되므로 ID 종속 API는 프로덕션에서와 동일하게 작동합니다.
중요: 디버그 형식은
winapp프로젝트를 자동으로 빌드 하지 않습니다. 코드를 변경한 후 F5 키를 누르기 전에 다시 빌드합니다.
preLaunchTask를 사용하여 빌드를 자동화하십시오
다시 빌드하는 것을 잊으려면 모든 디버그 세션 전에 프로젝트를 빌드하는 항목을 추가 preLaunchTask 합니다.
-
.vscode/tasks.json(예: .NET)에서 빌드 작업을 정의합니다.{ "version": "2.0.0", "tasks": [ { "label": "build", "command": "dotnet", "type": "process", "args": ["build", "${workspaceFolder}"], "problemMatcher": "$msCompile" } ] } - 다음에서 이를 참조하세요
launch.json.{ "type": "winapp", "request": "launch", "name": "WinApp: Launch and Attach", "preLaunchTask": "build" }
구성 속성
| 재산 | Type | Default | Description |
|---|---|---|---|
inputFolder |
string | 앱 이진 파일이 포함된 빌드 출력 폴더의 경로입니다(예: ${workspaceFolder}/bin/Debug/net8.0-windows10.0.22621). 설정하지 않으면 폴더를 선택하라는 메시지가 표시됩니다. |
|
manifest |
string | AppX 매니페스트 파일(예: AppxManifest.xmlPackage.appxmanifestappxmanifest.xml또는)에 대한 경로입니다. 설정하지 않으면 CLI가 입력 폴더 또는 현재 디렉터리에서 자동으로 검색됩니다. |
|
debuggerType |
string | coreclr |
사용할 기본 디버거(coreclr또는cppvsdbgnode)입니다. |
workingDirectory |
string | 작업 영역 폴더 | 애플리케이션의 작업 디렉터리입니다. |
args |
string | 애플리케이션에 전달할 명령줄 인수입니다. | |
outputAppxDirectory |
string | 느슨한 레이아웃 패키지의 출력 디렉터리입니다. 기본값은 입력 폴더 내의 AppX 폴더입니다. |
|
port |
number | 9229 |
(node 만) Node.js --inspect 수신기 및 연결 연결에 사용되는 포트입니다. 기본 포트가 이미 사용 중인 경우 무시합니다. |
지원되는 디버거
debuggerType |
Language | 필수 확장 기능 |
|---|---|---|
coreclr(기본값) |
C# / .NET | C# 개발 키트 |
cppvsdbg |
C/C++ | C/C++ |
node |
Node.js / 전자 | 기본 제공 |
C++ 프로젝트의 예:
{
"type": "winapp",
"request": "launch",
"name": "WinApp: Launch C++ App",
"debuggerType": "cppvsdbg"
}
디버그 ID 만들기를 사용한 시작 디버깅
첫 번째 명령에서 시작 코드를 디버그해야 하는 경우 F5 연결 접근 방식이 초기 코드를 놓칠 수 있습니다. 대신 명령 팔레트()에서 Ctrl+Shift+P 명령을 사용하여 실행 파일에 대한 스파스 패키지를 등록한 다음, 표준 디버거로 시작합니다.
{
"name": "Launch (with identity)",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/bin/Debug/net8.0-windows10.0.22621/myapp.exe"
}
create-debug-identity exe 자체에 ID를 등록하므로 앱에는 표준 VS Code 시작 구성을 포함하여 앱이 어떻게 시작되었는지에 관계없이 ID가 있습니다.
실행 중인 프로세스에 연결
터미널에서 winapp run로 시작한 후 연결하려면 표준 연결 구성을 사용하세요.
{
"name": "Attach to Process",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
C++/Rust의 경우 "type": "cppvsdbg" (MSVC)을 사용하거나 "type": "lldb" (LLDB)을 사용하십시오.
{
"name": "Attach (C++)",
"type": "cppvsdbg",
"request": "attach",
"processId": "${command:pickProcess}"
}
정리 중
테스트가 완료되면 명령 팔레트에서 WinApp: 패키지 등록 취소 를 실행하여 VS Code를 종료하지 않고 테스트용으로 로드된 개발 패키지를 제거합니다.
Windows developer