Deskband11Lib
윈도우 11에서 사라진작업표시줄 데스크밴드,WinUI·WPF로 다시 만들기
이호원 · airtaxi · Dot4 밋업 with 마이크로소프트 코리아
Deskband11Lib
Dot4 밋업 with 마이크로소프트 코리아
WINDOWS 11 · TASKBAR · WIN32 × WINUI × WPF

윈도우 11에서 사라진작업표시줄 데스크밴드,WinUI·WPF로 다시 만들기

SetParent 하나로 시작해서 스토어 출시까지 간 라이브러리 개발기
H
이호원 · Howon Leeairtaxi · .NET 데스크톱 개발자
이런 걸 만듭니다
SPEAKER

이호원 · airtaxi

Windows를 좋아해서 C#과 .NET을 시작했고,
지금은 WinUI·Uno Platform으로 Windows 앱을 만듭니다.

게임 개발자 · 아크스타

인디 리듬게임 회사에서
게임 서버·클라이언트 개발

컴퓨터공학 박사

선문대학교 · 논문은 멀티모달 LLM과
RAG 기반 화재 대응 조언 시스템

지금 · 서울 현충원

박사 졸업 후 사회복무요원으로 복무 중
개발은 퇴근 후에 합니다

LIVE ON MY TASKBAR

지금 제 작업표시줄은 이렇습니다

PinStats와 CLI Account Switcher 위젯이 함께 떠 있는 작업표시줄
CLI Account Switcher · AI CLI 사용량PinStats · 시스템 모니터, 둘이 한 작업표시줄에 동시에
BarPlay 미디어 위젯이 떠 있는 작업표시줄
BarPlay · 지금 재생 중인 미디어와 컨트롤

전부 실제로 돌아가는 .NET 앱입니다. 오늘은 이걸 만드는 법을 이야기합니다.

PROJECTS

오늘 나오는 프로젝트는 전부 제가 만든 것들입니다

D

Deskband11Lib

오늘 소개할 작업표시줄 호스팅 라이브러리

BarPlay 로고

BarPlay

미디어 위젯 · 라이브러리 검증용 PoC → 스토어 출시

PinStats 로고

PinStats

시스템 모니터 · 기존 앱에 위젯 기능을 이식

CLI Account Switcher 로고

CLI Account Switcher

AI CLI 계정·사용량 관리 · 마찬가지로 위젯 이식

이 밖에도 AT-Installer · DeskBorder · LidGuard 등 오픈소스 Windows 앱을 15개 넘게 만들어 공개했습니다.

github.com/airtaxi

AGENDA

오늘 다룰 내용

01사라진 데스크밴드Windows 11이 없앤 것
02알고 보니 그냥 창이었다zadjii/Deskband11
03작업표시줄에 창 붙이기Win32 + UI Automation
04라이브러리로 만들기설계 · 복구 · 멀티 인스턴스
05요즘 .NET으로 Win32 다루기CsWin32 · 소스 생성 COM · NativeAOT
06PoC에서 스토어까지 + 라이브 데모BarPlay · PinStats · CAS
07한계와 배운 점그리고 Q&A
01
CHAPTER 01

사라진
데스크밴드

Windows 95부터 있던 작업표시줄 도구들은 어디로 갔나
WINDOWS 95 → 10

먼저, 데스크밴드가 뭐였는지부터

Windows 10까지의 작업표시줄
데스크밴드(도구 모음)가 있던 자리
한 A12:58 PM
  • IDeskBand COM 확장이 작업표시줄 안에 서드파티 UI를 넣는 공식 통로였습니다탐색기가 내 DLL을 로드해서 작업표시줄 안에 그려줬습니다
  • 미디어 컨트롤, 주소창, 시스템 모니터, 퀵런처가 전부 여기 있었습니다
WINDOWS 11

작업표시줄을 새로 만들면서 데스크밴드를 버렸습니다

Windows 11의 작업표시줄, XAML 기반으로 새로 작성
 
 
한 A12:58 PM
  • Deskband API는 제거, 공식 대체재는 없음도구 모음 메뉴 자체가 사라졌습니다
  • Windows 10에서 쓰던 서드파티 작업표시줄 도구는 전부 못 쓰게 됐습니다
그런데 항상 보이는 작은 자리
여전히 필요했습니다
미디어 컨트롤 시스템 모니터 빌드 · CI 상태 사용량 표시기 타이머 빠른 실행
02
CHAPTER 02

알고 보니
그냥 창이었다

zadjii/Deskband11에서 얻은 아이디어
INSPIRATION

GitHub에서 이런 걸 발견했습니다

  • zadjii/Deskband11, Windows 11 작업표시줄에 미디어 컨트롤을 얹은 해커톤 프로젝트
  • 데스크밴드 API 없이, 작업표시줄 안에 UI가 실제로 떠 있었습니다

"작업표시줄 위에 투명한 창을 얹으면 되잖아?"

원본 Deskband11이 작업표시줄에 미디어 컨트롤을 표시한 모습
원본 Deskband11 실행 모습, 지금 재생 중인 곡과 컨트롤
THE TRICK

필요한 건 평범한 Win32 호출 몇 개뿐입니다

① 투명한 창을 만든다

WinUI 3 · WPF의
평범한 Window

② SetParent

작업표시줄(Shell_TrayWnd)의
자식 HWND로 입양

③ 이동하고 클립

SetWindowPos + SetWindowRgn으로
빈 공간에 딱 맞춤

결과: 작업표시줄의 일부처럼 보이는 내 창
 
▷ 내 앱의 UI
한 A12:58 PM
BUT…

그대로 가져다 쓸 수는 없었습니다

  • 만든 사람 스스로 "해커톤급 코드"라고 했고, 지금 Windows 11 빌드에선 위치·크기가 어긋남
  • 시작 버튼 중앙 정렬도, 위젯 버튼도 고려되지 않음
  • 구조도 한 앱에 묶여 있어서 다른 앱에서 재사용 불가

그래서 dotnet add package 한 줄이면 되도록 라이브러리로 다시 만들었습니다. 그게 Deskband11Lib입니다

03
CHAPTER 03

작업표시줄에
창 붙이기

Win32로 창을 붙이고, UI Automation으로 자리를 잽니다
STEP 1 · WIN32

팝업 창을 자식 창으로 바꿉니다

TaskbarContentHostBase.cs─ ▢ ✕
// 1. 창 스타일 교체: 최상위 팝업 → 자식 창
var style = GetWindowLongPtr(hwnd, GWL_STYLE);
style &= ~(WS_POPUP | WS_CAPTION | WS_THICKFRAME);
style |= WS_CHILD;
SetWindowLongPtr(hwnd, GWL_STYLE, style);

// 2. 작업표시줄의 자식으로 입양
SetParent(hwnd, taskbarHwnd);  // Shell_TrayWnd

// 3. 빈 공간에 배치하고 클리핑
SetWindowPos(hwnd, x, y, width, height, ...);
SetWindowRgn(hwnd, CreateRectRgn(0, 0, width, height), true);

창은 그대로입니다

XAML 트리도, 입력도, 렌더링도 전부 살아있는 진짜 창입니다. 스타일 비트만 바뀝니다

되돌릴 수 있습니다

Detach하면 원래 스타일과 부모를 복원해서 평범한 창으로 돌아갑니다

STEP 2 · THE PROBLEM

그런데 버튼 폭을 알 수가 없습니다

Shell_TrayWnd작업표시줄 본체
├─TrayNotifyWnd알림 영역
└─ReBarWindow32
└─MSTaskSwWClass
└─MSTaskListWClass앱 버튼 리스트
시작 버튼? 앱 버튼 폭? 위젯 버튼?  →  어디에도 없음

버튼들은 XAML로 렌더링

Windows 11 작업표시줄의 콘텐츠는 XAML 컴포지션이라 자식 HWND가 버튼별로 존재하지 않습니다

그런데 폭은 알아야 합니다

시작 버튼·앱 버튼과 겹치지 않는 "빈 공간"을 계산해야 하니까요

STEP 2 · THE FIX

그래서 UI Automation으로 직접 쟀습니다

UIA가 읽어낸 실제 지오메트리 (중앙 정렬 기준)
위젯
왼쪽 빈 공간
시작
앱 버튼 그룹
오른쪽 빈 공간
알림 영역 · 시계
AutomationId = StartButton AutomationId = WidgetsButton ControlType.Button 연속 그룹 = 앱 버튼 SystemTrayIcon · NotifyItemIcon = 알림 영역
STEP 3 · ALIGNMENT

정렬은 레지스트리가 알려줍니다

registry─ ▢ ✕
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced

TaskbarAl = 0   → 왼쪽 정렬
TaskbarAl = 1   → 중앙 정렬 (기본값)

// 값이 없다면? 시작 버튼의 X 좌표로 추론
중앙 정렬: 빈 공간이 양쪽에 생김
 
 
12:58
왼쪽 정렬: 빈 공간은 오른쪽 하나
 
12:58

정렬에 따라 빈 공간이 생기는 위치가 완전히 달라집니다. 원본 Deskband11이 놓친 부분이었죠.

STEP 4 · PLACEMENT

빈 공간 고르기: Placement

Placement = Auto: 중앙 정렬이면 더 넓은 쪽을 고릅니다
위젯
좁은 쪽 ✕
시작
앱 버튼 그룹
더 넓은 쪽 ✓ 여기에 배치
알림 영역
Auto · 더 넓은 쪽 자동 선택 LeftEdge · 왼쪽 끝(위젯 버튼 옆) BeforeNotificationArea · 알림 영역 앞 BeforeStartButton · 시작 버튼 옆

시작 버튼·앱 버튼·위젯 버튼·알림 영역과는 절대 겹치지 않습니다

PERFORMANCE

측정이 UI를 막지 않게

UIA 스캔

ThreadPool에서 실행
느려도 괜찮은 곳으로 격리

지오메트리 캐시

측정 결과를 lock으로 보호해
스냅샷으로 보관

레이아웃 타이머 (500ms)

캐시만 읽어서 배치 계산
UI 스레드는 대기 없음

  • 스캔이 실패하면? 이전 측정값을 그대로 유지하고 위젯은 아무 일 없이 계속 동작측정 실패가 기능 정지로 이어지지 않게 설계했습니다
04
CHAPTER 04

라이브러리로
만들기

설계 · 자동 복구 · 멀티 인스턴스 조율
API DESIGN

목표: 설치 한 줄, 코드 세 줄

pwsh─ ▢ ✕
$ dotnet add package Deskband11Lib.WinUI
App.xaml.cs─ ▢ ✕
var window = new MainWindow();
var host = new TaskbarContentHost(window, rootElement,
new TaskbarContentHostOptions
{
    PreferredWidth  = 360,
    PreferredHeight = 48
});

await host.AttachWhenLayoutReadyAsync();
window.Activate();

WinUI = WPF

두 Facade의 API는 완전히 같고, Window와 FrameworkElement의 네임스페이스만 다릅니다

옵션은 한 곳에

폭·높이·배치·모니터·애니메이션 전부 TaskbarContentHostOptions 하나로

ARCHITECTURE

Core 하나, Facade 여럿

Deskband11Lib.WinUI

Windows App SDK · WinUI 3 Facade

Deskband11Lib.Wpf

WPF Facade

(미래) Avalonia…

Facade만 추가하면 확장

▲ 창 핸들 · 디스패처 · 타이머만 제공  |  ITaskbarHostPlatformAdapter  |  레이아웃 결과만 수신 ▼

Deskband11Lib.Core: UI 프레임워크 무관 엔진

작업표시줄 창 탐색레이아웃 계산UIA 측정 정렬 감지Explorer 재시작 감시멀티 인스턴스 슬롯 조율

Facade가 얇을수록 새 프레임워크 지원이 쉬워집니다. 엔진은 하나뿐입니다

LIFECYCLE

Explorer는 언젠가 재시작됩니다

Attach

작업표시줄에
창 입양

감시 (1초 폴링)

작업표시줄 HWND
생존 확인

재시작 감지

HWND가 통째로 교체됨
내 창은 이미 파괴된 뒤

이벤트 통지

TaskbarWindowRecreated
→ 앱이 창을 재생성

App.xaml.cs─ ▢ ✕
host.TaskbarWindowRecreated += async (_, _) =>
{
    await RecreateMainWindowAsync();  // 창을 새로 만들어 다시 Attach
};

보조 모니터가 분리됐다 재연결되어도 자동으로 원래 작업표시줄로 복귀합니다

MULTI-INSTANCE

여러 앱이 같은 자리를 원한다면?

BarPlay

폭 380 요청

PinStats

폭 260 요청

CLI Account Switcher

폭 200 요청

▼ 서로의 존재를 모르는 별개 프로세스들 ▼

공유 메모리(MMF) 슬롯 레지스트리 + Named Mutex

Local\Deskband11Lib.SlotRegistry에 최대 64슬롯, 슬롯마다 HWND · 요청 폭 · 우선순위 · 하트비트 기록

  • 중앙 서버도 IPC 채널도 없고, 죽은 프로세스는 IsWindow 검사로 자동 퇴출
SLOT ALLOCATION

Fixed-then-Stretch 배분

공간이 충분할 때: Fixed는 요청 폭 그대로, Stretch가 나머지를 나눠 가짐
CAS
Fixed 200
BarPlay
Fixed 380
Stretch (PreferredWidth = double.MaxValue), 남는 공간 전부
공간이 모자랄 때: 크기 조절을 허용한 Fixed는 요청 폭에 비례해 축소 (0이 되지는 않음)
CAS
200 → 150
BarPlay
380 → 285
앱 버튼이 늘어나 좁아진 공간…
  • ManualSlotPriority가 낮을수록 앞자리, 기본값 65535는 도착 순서대로
MOTION

애니메이션까지 신경 썼습니다

이징 25종 내장

Linear · Sine · Cubic · Expo · Circle… 기본은 CircleOut, Func<double,double>이면 뭐든 OK

144 Hz

고주사율 모드

애니메이션 타이머를 60FPS 고정 대신 모니터의 실제 주사율에 맞춰 구동

재배치도 부드럽게

옆 위젯이 등장·퇴장해 자리가 바뀌어도 순간이동 대신 슬라이드 애니메이션

작업표시줄의 일부처럼 보이려면, 움직임도 OS처럼 자연스러워야 합니다

05
CHAPTER 05

요즘 .NET으로
Win32 다루기

CsWin32 · 소스 생성 COM · NativeAOT
.NET INTEROP ①

P/Invoke를 손으로 쓰지 않습니다

NativeMethods.txt─ ▢ ✕
SetParent
SetWindowPos
FindWindowEx
GetWindowLongPtrW
SetWindowRgn
→ 소스 생성 →
생성된 코드 사용─ ▢ ✕
PInvoke.SetParent(childHwnd, taskbarHwnd);
// 시그니처·마샬링·SafeHandle까지
// Microsoft.Windows.CsWin32가 전부 생성
  • Win32 메타데이터에서 정확한 시그니처를 생성해 손으로 쓴 P/Invoke의 마샬링 실수를 차단
  • 생성된 코드는 분석 가능한 정적 코드라서 NativeAOT·트리밍과 궁합이 좋습니다
.NET INTEROP ②

UI Automation도 소스 생성 COM으로

GeneratedUIAutomation.cs─ ▢ ✕
[GeneratedComInterface]
[Guid("30cbe57d-d9d0-452a-ab13-7ac5ac4825ee")]
internal partial interface IUIAutomation
{
    IUIAutomationElement GetRootElement();
    IUIAutomationElement ElementFromHandle(nint hwnd);
    // COM vtable 호출 코드를 컴파일 타임에 생성
}

System.Windows.Automation 없음

무거운 관리 어셈블리 대신 UIA COM 인터페이스를 직접, 그것도 필요한 멤버만

DisableRuntimeMarshalling

런타임 마샬링을 제거해서 상호 운용이 전부 컴파일 타임에 결정됩니다

고전 COM 마샬러는 런타임에 코드를 만들어서 AOT와 같이 쓸 수 없습니다. 그래서 소스 생성을 택했습니다

NATIVEAOT

결과: NativeAOT로 컴파일되는 위젯

즉시 시작

JIT 없는 네이티브 바이너리라 부팅 직후 뜨는 위젯에 잘 맞습니다

가벼운 상주

항상 떠 있는 앱이라 메모리 사용량을 최대한 줄였습니다

IsAotCompatible

라이브러리 전체가 AOT 분석 경고 0, 쓰는 앱도 안심하고 PublishAot 가능

NativeAOT로 배포 중인 BarPlay가 데스크톱에서 실행 중인 모습
BarPlay: 실제로 NativeAOT로 빌드해 스토어에 배포 중
06
CHAPTER 06

PoC에서
스토어까지

BarPlay · PinStats · CLI Account Switcher, 그리고 라이브 데모
PROOF OF CONCEPT

BarPlay: 검증용으로 만든 미디어 위젯

  • SMTC 연동: Spotify, 브라우저, YouTube까지 시스템 미디어면 전부
  • 위젯 클릭 → 플라이아웃으로 탐색 슬라이더 · 설정까지
  • NativeAOT로 빌드해 Microsoft Store까지 출시, 라이브러리가 실전에서 쓸만한지 확인
BarPlay 위젯과 펼쳐진 플라이아웃
작업표시줄 위젯 + 플라이아웃, 전부 WinUI 3 컨트롤 그대로
ADOPTION

기존 앱에도 며칠 만에 붙습니다

PinStats 작업표시줄 위젯과 하드웨어 상세 팝업
PinStats: CPU·GPU·메모리 링이 작업표시줄에 상주
CLI Account Switcher 위젯과 활성 계정 할당량 팝업
CLI Account Switcher: AI CLI 사용량이 항상 눈앞에

두 앱 모두 통합 코드는 옵션 몇 개 + AttachWhenLayoutReadyAsync 한 줄이 전부였습니다.

SHOWTIME
LIVE DEMO
DEMO 01

Explorer 강제 종료

taskkill /f /im explorer.exe

작업표시줄이 죽어도 위젯은 스스로 돌아옵니다

DEMO 02

정렬 전환

시작 버튼을 중앙 ↔ 왼쪽으로 바꾸면, 위젯이 애니메이션과 함께 새 빈 공간으로 이사합니다

DEMO 03

슬롯 조정

위젯 두 개를 동시에 띄우면 서로 겹치지 않고 공간을 나눠 갖습니다

데모의 신이 노하시면 준비된 영상으로 대체합니다 :)

LIMITATIONS

한계와 배운 점

  • 여긴 문서화되지 않은 영역입니다. Windows 업데이트가 언제든 깨뜨릴 수 있습니다공식 API가 없는 곳이니 감수해야 하는 부분입니다
  • 그래서 감시와 복구를 처음부터 설계에 내장했습니다폴링 감시 · TaskbarWindowRecreated · 모니터 재연결 자동 복귀, 깨질 수 있다는 전제로 설계했습니다
  • UIA 기반 측정은 내부 구현 변화에 비교적 유연합니다AutomationId는 접근성 계약이라 픽셀 좌표 하드코딩보다 오래 살아남습니다
WRAP-UP
여러분도 작업표시줄 위젯
하나 만들어 보세요

감사합니다. 질문 받겠습니다.
이호원 · airtaxi

발표자료 GitHub QR 코드