오늘날의 Windows 셸 통합 ── 컨텍스트 메뉴, 파일 연결, Windows 11에서 바뀐 것

· · Windows, 셸 확장, 컨텍스트 메뉴, 파일 연결, COM, Windows 11, 파일 탐색기, MSIX, Windows 개발

「Windows 11 PC로 바꿨더니, 예전에 만들어 주신 앱의 컨텍스트 메뉴가 사라졌습니다」라는 상담을 받았습니다. 자세히 들어 보니 사라진 것이 아니었습니다. 파일을 오른쪽 클릭하고 메뉴 맨 아래의 「더 많은 옵션 표시」를 고르면, 늘 보던 메뉴가 그대로 나옵니다. 즉 사내 앱의 메뉴 항목이 한 클릭 더 안쪽으로 숨겨진 것입니다. 현장에서는 「클릭이 한 번 늘었다」, 「항목을 못 찾는다는 문의가 늘었다」는 말이 들립니다.

이것은 고장도 설정 실수도 아니며, Windows 11의 설계 변경입니다. 파일 탐색기의 컨텍스트 메뉴가 새것과 옛것의 이층 구조가 되었고, 새 메뉴에 항목을 올리는 조건은 예전과 완전히 다른 것이 되었습니다.

한편 그 아래의 파일 연결과 셸 확장 기구는 지금도 COM과 레지스트리의 옛 세계입니다. 확장자 키가 ProgID를 가리키고, ProgID의 verb가 명령줄을 가지며, 더 복잡한 확장은 탐색기에 로드되는 프로세스 내 COM 서버(DLL)로 동작합니다. 이 구조는 이십 년이 넘도록 바뀌지 않았습니다. 바뀌지 않은 토대와 Windows 11이 둘로 나눈 메뉴를 둘 다 알지 못하면, 「메뉴가 안 나온다」, 「숨겨졌다」, 「두 번 나온다」를 가를 수 없습니다.

이 글은 중소기업의 IT 담당자와 업무 앱을 돌보는 Windows 개발자를 대상으로, 파일 연결의 삼층 구조, 고전 셸 확장의 주의점, Windows 11 새 컨텍스트 메뉴를 겨냥하는 방법, 설치 프로그램의 등록·정리·문제 해결을 한 장의 그림으로 묶습니다.

1. 먼저 결론

  • 컨텍스트 메뉴와 파일 연결의 토대는 「확장자 키 → ProgID → verb」라는 삼층 레지스트리 구조입니다. 확장자 키는 ProgID를 가리키는 포인터이고, ProgID가 실체이며, 그 아래의 shell\<verb>\command가 명령줄을 가집니다.1
  • HKEY_CLASSES_ROOT(HKCR)는 독립 하이브가 아니라 HKLM\Software\Classes와 HKCU\Software\Classes의 병합 뷰입니다. 전 사용자 등록은 HKLM에, 사용자별 등록은 HKCU에 쓰고, HKCR은 읽기 전용으로 취급합니다.2
  • 기본 앱(더블클릭으로 열리는 앱)은 사용자가 고르도록 설계되어 있으며, 프로그램이 가로챌 수 없습니다. OS가 사용자 선택을 보호하므로, 설치 프로그램이 할 수 있는 일은 후보로 등록하는 것입니다.3
  • 고전 셸 확장은 탐색기에 로드되는 프로세스 내 COM DLL입니다. 확장의 크래시나 지연은 탐색기 전체(그리고 셸을 쓰는 다른 앱)로 퍼지고, 64비트 환경에는 64비트 DLL이 필요하며, 관리 코드 구현은 지원되지 않습니다.45
  • Windows 11에서 컨텍스트 메뉴가 둘로 나뉘었습니다. 새 메뉴에 나오는 명령은 IExplorerCommand와 패키지 ID로 등록된 것뿐이며, 고전 IContextMenu 확장은 「더 많은 옵션 표시」(Shift+F10)의 옛 메뉴로 옮겨집니다.67
  • 새 메뉴에 사용자 명령을 올리는 공식 경로는 IExplorerCommand를 구현한 네이티브 DLL을 MSIX 매니페스트(desktop4:FileExplorerContextMenus)에 등록하는 것입니다. MSIX가 될 수 없는 앱은 sparse package(외부 위치 MSIX)로 ID만 부여할 수 있습니다.78
  • 「이 앱으로 연다」만 원하면 연결과 정적 verb로 충분합니다. 셸 확장 DLL은 필요 없고, Microsoft 자신도 「요구를 충족하는 가장 단순한 방법(정적 verb)을 고르라」고 분명히 말합니다.9
  • 등록이나 변경 뒤에는 SHChangeNotify(SHCNE_ASSOCCHANGED)로 알리고, 제거 시에는 ProgID는 지우되 확장자 키의 기본값은 지우지 않는 것이 공식 안내입니다. 셸 통합에는 정리의 설계도 포함됩니다.110

한 문장으로 말하면, 연결과 verb의 세계는 그대로이고, 메뉴를 보여주는 방식만 Windows 11에서 둘로 나뉘었습니다. 아래에서는 이 토대부터 따라갑니다.

2. 파일 연결의 동작 ── 확장자 키 → ProgID → verb의 삼층 구조

2.1. 한 예로 삼층 구조를 읽기

어떤 확장자의 파일을 더블클릭했을 때 일어나는 일은 레지스트리 키 세 층으로 결정됩니다.1

HKEY_CLASSES_ROOT
   .kmrpt                                  ← (1) 확장자 키
      (Default) = KomuraSoft.Report.1      ←     ProgID 이름만 가리키는 포인터
      OpenWithProgids
         KomuraSoft.Report.1               ←     「연결 프로그램」 후보
   KomuraSoft.Report.1                     ← (2) ProgID(연결의 실체)
      (Default) = Komura Report document
      DefaultIcon
         (Default) = "C:\Program Files\KomuraSoft\Report.exe",0
      shell                                ← (3) verb 목록
         open
            command
               (Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
  • (1) 확장자 키 (.kmrpt)는 기본값으로 ProgID 이름만 가리킵니다. 여기에 명령을 직접 쓰는 것은 실수입니다.
  • (2) ProgID (KomuraSoft.Report.1)가 연결의 실체이며, 표시 이름, 아이콘, verb 목록을 가집니다.
  • (3) verb는 「열기」나 「인쇄」 같은 동작이며, shell\open\command의 기본값이 실제로 기동되는 명령줄입니다.

이 분리 덕분에 여러 확장자(.kmrpt.kmrpt-file처럼)를 같은 ProgID에 붙이거나, 앱을 업그레이드할 때 ProgID를 갈아 끼울 수 있습니다.

파일 연결의 삼층 구조확장자 키는 기본값으로 ProgID 이름을 가리키는 포인터이고, ProgID는 표시 이름·아이콘·verb 목록을 가진 실체이며, verb 아래 command의 기본값이 실제로 기동되는 명령줄이다기본값으로 ProgID를 가리킴확장자 키 .kmrptProgID KomuraSoft.Report.1verb(shell 아래의 open 등)command 기본값Report.exe가 기동된다표시 이름과 DefaultIcon도 가진다

그림 1: 확장자 키는 포인터, ProgID는 실체, verb의 command가 실제로 기동되는 명령줄이다.

2.2. HKCR는 「병합 뷰」 ── 쓰는 곳에 따라 의미가 바뀐다

위 예는 HKEY_CLASSES_ROOT(HKCR) 아래에 그려져 있지만, HKCR는 물리 저장 위치가 아니라 HKLM\Software\Classes와 HKCU\Software\Classes의 병합 뷰입니다. 같은 키가 양쪽에 있으면 HKCU 쪽이 이깁니다.2

HKCR는 병합 뷰이다HKCR는 HKLM과 HKCU의 Classes를 겹쳐 놓은 것이고, 같은 키가 양쪽에 있으면 HKCU가 이긴다. 등록은 HKLM 또는 HKCU에 명시적으로 쓰고 HKCR는 읽기 전용으로 취급한다HKLM\\Software\\Classes(모든 사용자)HKCR(병합 뷰)HKCU\\Software\\Classes(사용자별)같은 키가 있으면 HKCU가 이긴다읽기 전용으로 취급한다(확인용)

그림 2: HKCR는 HKLM과 HKCU Classes를 겹쳐 본 모습이며, 쓰기 대상은 항상 어느 한쪽을 명시한다.

쓰기 대상 의미 필요한 권한
HKLM\Software\Classes 모든 사용자 공통 등록 관리자
HKCU\Software\Classes 그 사용자만의 등록 없음
HKCR에 직접 쓰기 기존 키가 이미 있는 쪽에 따라 분배 상황에 따름

실무에서 안전한 분기는 등록을 항상 HKLM 또는 HKCU에 명시적으로 쓰고, HKCR는 확인용 읽기 전용으로 취급하는 것입니다. WOW64 레지스트리 리다이렉트와의 관계도 정리해 둘 가치가 있습니다. HKLM\Software\Classes 바로 아래의 확장자 키와 ProgID 같은 연결 데이터는 Windows 7부터 32비트와 64비트 레지스트리 뷰 사이에서 공유되므로, 32비트 설치 프로그램이 써도 Wow6432Node 쪽으로 달아나지 않습니다. 반면 Classes\CLSID 같은 일부 COM 등록 하위 키는 리다이렉트되며, 셸 확장(프로세스 내 COM)을 등록할 때는 32비트/64비트 쓰기 분기가 중요합니다. 자세한 내용은 「레지스트리의 32bit/64bit 리다이렉트와 가상화의 함정 ── Wow6432Node와 ‘분명히 썼는데 값이 없다’ 문제」에 있습니다.

2.3. 앱 쪽의 등록 ── App Paths, Applications, RegisteredApplications

파일 쪽(확장자와 ProgID)과 짝을 이루는 앱 쪽 등록도 세 종류가 있습니다.11

  • App Paths (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths): 실행 파일 이름만으로 ShellExecuteEx가 기동할 수 있게 하는 등록입니다. PATH 환경 변수를 더럽히지 않아도 되므로 Microsoft가 권장합니다.
  • Applications (HKCR\Applications\<app.exe>): 「연결 프로그램」으로 임의의 파일을 넘겼을 때의 기본 여는 방식과 앱의 표시 이름(FriendlyAppName)을 정의합니다.
  • RegisteredApplications + Capabilities: 앱이 다룰 수 있는 확장자와 MIME 유형을 선언하며, Windows 기본 앱 설정 페이지에 후보로 나타나게 하는 등록입니다.

「기본 앱 목록에 우리 앱이 안 나온다」는 상담의 대부분은 ProgID는 등록했는데 이 Capabilities 등록을 빠뜨린 경우입니다.

앱 쪽 등록의 세 종류앱 쪽 등록은 App Paths, Applications, RegisteredApplications의 세 종류이며, 각각 파일 이름만으로의 기동, 연결 프로그램의 기본 여는 방식, 기본 앱 설정 페이지 표시를 담당한다앱 쪽 등록App PathsApplicationsRegisteredApplications파일 이름만으로 기동연결 프로그램의 기본기본 앱 페이지에 나타남Capabilities 선언이 필요

그림 3: 앱 쪽 등록은 세 종류이며, 기본 앱 후보로 나타나려면 Capabilities 등록이 필요하다.

2.4. 기본 앱은 사용자의 것 ── UserChoice 보호

확장자 키의 기본값에 ProgID를 쓰는 것만으로는 기본 앱이 되지 않습니다. 사용자가 「연결 프로그램」 등에서 명시적으로 고른 결과는 HKCU\...\Explorer\FileExts\<extension>\UserChoice에 보관되며, 연결 해석은 그쪽을 우선합니다.

그리고 중요한 점은 Windows가 기본 앱의 프로그래밍 변경을 지원하지 않는다는 것입니다. 기본 앱 설정은 사용자가 시스템 설정 UI를 통해 하도록 설계되어 있고, UserChoice 데이터는 난독화되며, 필터 드라이버(UCPD.sys)가 앱의 쓰기를 막습니다. 관리 환경에서는 그룹 정책 / MDM 정책이 공식 수단입니다.3

해시을 「흉내 내어 다시 쓰는」 SetUserFTA 같은 도구가 쓰여 온 것은 이 보호의 이면입니다. 사내 앱의 설치 프로그램에 넣어야 할 것은 기본값을 가로채는 일이 아니라, (a) ProgID와 verb의 올바른 등록, (b) OpenWithProgIds에 자신을 더하기, (c) 필요하면 설정 페이지로 안내하기의 세 가지입니다.

기본 앱 해석과 UserChoice 보호사용자의 명시적 선택 결과는 UserChoice에 보관되어 연결 해석에서 우선되고, UCPD.sys가 앱의 재쓰기를 막으므로, 설치 프로그램이 할 수 있는 일은 후보로 등록하고 설정 페이지로 안내하는 것이다우선됨UCPD.sys가 차단UserChoice(사용자의 선택)연결 해석확장자 키 기본값앱에서의 재쓰기설치 프로그램의 일ProgID와 verb를 등록OpenWithProgIds에 추가설정 페이지로 안내

그림 4: 연결 해석은 사용자의 선택(UserChoice)을 우선하며, OS가 앱의 재쓰기로부터 그것을 보호한다.

3. 「열기」 이외의 verb ── print, edit, runas, 사용자 지정 verb

verb는 open만이 아닙니다. OS가 의미를 아는 표준 verb에는 open 외에 edit, print, play, preview가 있으며, 표준 verb는 OS 로캘을 따르는 표시 이름을 자동으로 얻습니다. 더블클릭에 쓰이는 기본 verb는 shell 키의 기본값 → 레지스트리의 첫 verb → openopenwith 순으로 결정됩니다.12

기본 verb가 결정되는 순서더블클릭에 쓰이는 기본 verb는 shell 키 기본값, 레지스트리의 첫 verb, open, openwith 순으로 처음 찾은 것이다없으면없으면없으면shell 키 기본값레지스트리의 첫 verbopenopenwith

그림 5: 더블클릭의 기본 verb는 이 순서로 처음 찾은 것이다.

자신만의 동작을 더하고 싶으면 사용자 지정 verb를 등록합니다.

KomuraSoft.Report.1
   shell
      open
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
      print
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" /print "%1"
      verify                          ← 사용자 지정 verb
         (Default) = Verify report (&V)   ← 메뉴 표시 이름
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" /verify "%1"

알아 두면 도움이 되는 작은 사실 세 가지입니다.

  • runas라는 이름의 verb를 등록하면 「관리자 권한으로 실행」에 해당하는 상승 기동을 정의하며, ShellExecute 계열 API가 runas를 지정할 때도 쓰입니다.
  • verb 키에 Extended라는 빈 값을 두면 Shift+오른쪽 클릭일 때만 보이는 확장 verb가 됩니다. 거의 쓰지 않는 위험한 동작을 숨기기에 편리합니다.12
  • 오래된 앱의 일부 연결은 DDE(ddeexec 키)로 문서를 기존 프로세스에 보내는 구성을 아직 가지고 있지만, DDE로 verb를 기동하는 것은 이미 Deprecated 레거시입니다. 새로 쓸 이유는 없습니다.12

자주 일어나는 사고 하나가 더 있습니다. 명령줄의 따옴표입니다. 명령 문자열의 요소에 공백이 들어갈 수 있으면 따옴표로 감싸야 합니다. C:\Program Files\... 같은 EXE 경로는 물론이고, %1(선택한 파일의 경로)도 항상 "%1"로 써야 합니다. 사용자 파일 경로에 공백이 없다고 보장할 수 없습니다. 따옴표 없는 My Program.exe는 「My를 인수 Program.exe로 기동한다」로 해석됩니다.13

명령줄의 따옴표 사고따옴표 없는 명령은 공백에서 나뉘어 My를 인수 Program.exe로 기동하는 것으로 오해되므로, 공백이 들어갈 수 있는 EXE 경로와 선택한 파일 경로를 나타내는 %1은 항상 따옴표로 감싼다공백에서 분할따옴표 없는 명령다른 EXE를 기동하는 것으로 오해따옴표 있는 명령의도대로 기동EXE 경로를 따옴표로 감싼다%1도 항상 따옴표로 감싼다

그림 6: 따옴표 없는 명령은 공백에서 잘못 나뉘므로, EXE 경로와 %1은 항상 따옴표로 감싼다.

여기까지의 레지스트리만의 기구(정적 verb)는 DLL을 하나도 쓰지 않고 실현할 수 있으며, 탐색기를 불안정하게 만들 위험도 없습니다. Microsoft 자신도 「셸 확장을 쓰기 전에, 요구를 충족하는 가장 단순한 정적 verb로 충분한지 검토하라」고 반복해서 말합니다.9

4. 고전 셸 확장 ── 탐색기 안에서 도는 DLL

4.1. 셸 확장의 종류

정적 verb가 충족하지 못하는 요구 ── 「선택에 따라 메뉴를 동적으로 바꾼다」, 「아이콘이나 속성 시트를 바꾼다」 ── 에는 셸 확장 핸들러를 씁니다. 대표적인 종류는 다음과 같습니다.4

핸들러 주요 인터페이스 할 수 있는 일
컨텍스트 메뉴 핸들러 IContextMenu + IShellExtInit 메뉴 항목을 동적으로 추가·제어
아이콘 핸들러 / 아이콘 오버레이 IExtractIcon / IShellIconOverlayIdentifier 파일별 아이콘과 오버레이
속성 시트 핸들러 IShellPropSheetExt 속성 시트에 탭 추가
미리 보기 / 정보 팁 IThumbnailProvider / IQueryInfo 미리 보기 보기와 가리키기 설명
끌어서 놓기 / 복사 훅 핸들러 IDropTarget / ICopyHook 놓기나 복사/이동에 개입

이들은 모두 COM 클래스로 구현되고 CLSID로 레지스트리에 등록됩니다. COM 자체에 대한 설명은 「COM / ActiveX / OCX란 무엇인가 - 차이와 관계를 정리해서 해설」에 있습니다.

4.2. 프로세스 내 COM 서버라는 것의 의미

고전 셸 확장의 본질은 탐색기(또는 공통 파일 대화 상자를 연 모든 앱)에 로드되는 프로세스 내 COM 서버(DLL)라는 점입니다. 모든 주의점은 여기서 나옵니다.4

  • 확장이 크래시하면 탐색기도 함께 내려갑니다. 멈추면 오른쪽 클릭이 몇 초간 멈춥니다. 피해도 탐색기에 그치지 않고, 파일 열기 대화 상자를 표시한 모든 앱에 닿습니다.
  • 메뉴 구축은 UI 스레드에서 일어나므로, 메뉴 표시 시점에 네트워크 접근이나 파일 I/O 같은 느린 일을 해서는 안 됩니다.
  • 스레딩 모델은 원칙적으로 Apartment로 등록합니다.
프로세스 내 확장의 부수 피해 구조셸 확장 DLL은 탐색기뿐 아니라 파일 대화 상자를 연 모든 앱의 프로세스에도 로드되므로, 확장의 크래시나 정지는 호스트 프로세스 전체로 퍼진다프로세스 내에 로드프로세스 내에 로드셸 확장 DLL탐색기대화 상자를 여는 모든 앱크래시나 정지가 퍼진다표시 시점에 느린 일을 하지 않는다

그림 7: 확장 DLL은 호스트 프로세스 안에서 도므로, 크래시나 정지가 호스트 전체로 퍼진다.

「특정 폴더를 열면 탐색기가 멈춘다」, 「오른쪽 클릭에 5초가 걸린다」는 상담을 조사하면, 원인이 사내 앱이 아니라 서드파티 셸 확장인 경우가 드물지 않습니다. 격리 방법은 8장에 있습니다.

4.3. 비트 수를 맞추기 ── 64비트 환경에는 64비트 DLL이 필요하다

프로세스 내 DLL은 로드하는 프로세스의 비트 수와 맞아야 합니다. 64비트 Windows의 탐색기는 64비트 프로세스이므로, 32비트로만 빌드한 셸 확장 DLL은 절대 로드되지 않고 메뉴에도 전혀 나타나지 않습니다. 오류도 없으므로 「등록했는데 안 나온다」의 단골 원인입니다. 32비트 앱 본체와 64비트 셸 확장 DLL의 조합은 정당한 구성이지만, COM 등록이 비트 수별로 갈라진다는 사실(Wow6432Node)을 봐야 합니다. verb의 command에서 기동하는 것은 별도 프로세스 EXE이므로 이 제약의 대상이 아닙니다(32비트 EXE로 남겨도 됩니다).

셸 확장 DLL의 비트 수 맞추기64비트 탐색기가 로드할 수 있는 셸 확장 DLL은 64비트뿐이며, 32비트 전용 DLL은 메뉴에 나타나지 않고 오류도 없다. verb command에서 기동하는 EXE는 별도 프로세스이므로 제약의 대상이 아니다로드할 수 있음로드할 수 없음별도 프로세스64비트 탐색기64비트 셸 확장 DLL32비트 전용 DLL오류 없이 메뉴에 나타나지 않음verb에서 기동하는 EXE32비트로 남겨도 됨

그림 8: 64비트 탐색기에 로드되는 DLL은 64비트 DLL뿐이며, verb에서 기동하는 EXE는 이 제약의 대상이 아니다.

4.4. 관리 코드로 쓰면 안 되는 이유

「셸 확장을 C#으로 쓸 수 있나요」라는 질문을 자주 받지만, Microsoft는 프로세스 내 셸 확장을 관리 코드(.NET)로 쓰는 것을 권장하지 않으며 지원 밖이라고 분명히 말합니다.5

이유는 확장이 임의의 프로세스에 로드되는 성질입니다. CLR 버전 충돌(.NET Framework 4 미만에서 특히), 락을 기다리는 동안 CLR이 메시지 루프에 재진입하는 문제, 가비지 컬렉션의 비결정적 객체 수명이 COM의 참조 카운트 계약과 충돌하는 문제는 호스트 앱을 불안정하게 만드는 구조적 이유입니다. 일부 항목은 .NET Framework 4 이후와 최신 .NET에서 완화되었지만, 공식 입장은 바뀌지 않았습니다.

실무 지침은 단순합니다. 프로세스 내 확장은 네이티브 C++로 씁니다. 관리 코드를 쓰고 싶으면 verb의 command로 기동하는 일반 EXE로 만들거나, 별도 프로세스에서 도는 프로세스 외 확장(미리 보기 핸들러 등)으로 만듭니다.5

관리 코드 허용 여부 판단탐색기 안에서 도는 프로세스 내 확장은 원칙적으로 네이티브 C++로 쓰고, 관리 코드를 원하면 verb command로 기동하는 일반 EXE나 별도 프로세스에서 도는 프로세스 외 확장으로 만든다아니오프로세스 내에서 도나?네이티브 C++로 쓴다관리 코드로 괜찮다CLR / 재진입 위험으로 호스트가 불안정해진다verb로 기동하는 EXE프로세스 외 미리 보기

그림 9: 프로세스 내 확장은 원칙적으로 네이티브 C++이며, 관리 코드는 별도 프로세스에서 도는 구성으로 한정한다.

5. Windows 11의 새 컨텍스트 메뉴 ── 둘로 나뉜 메뉴

5.1. 일어난 일

Windows 11은 파일 탐색기의 컨텍스트 메뉴를 새로 그렸습니다. 잘라내기, 복사 등이 맨 위 아이콘 줄이 되었고, 「열기」와 「연결 프로그램」이 위쪽에 묶였으며, 앱이 더하는 명령은 셸 표준 명령 아래에 묶입니다. 한 앱이 여러 명령을 더하면 앱 이름의 플라이아웃(하위 메뉴)으로 모입니다.6

그리고 핵심은 이것입니다. 고전 IContextMenu 기반 셸 확장은 삭제되지 않았습니다. 「더 많은 옵션 표시」(Shift+F10)로 열려 Windows 10 메뉴를 그대로 로드하는 옛 메뉴 쪽으로 옮겨졌을 뿐입니다.6 글머리 상담의 「메뉴가 숨겨졌다」의 정체가 이 분기입니다.

Windows 11이 둘로 나눈 컨텍스트 메뉴오른쪽 클릭에서 먼저 열리는 것은 새 메뉴이며, 거기 나오는 명령은 IExplorerCommand와 패키지 ID로 등록된 것뿐이다. 고전 IContextMenu 확장은 더 많은 옵션 표시로 여는 옛 메뉴로 옮겨진다더 많은 옵션 표시 Shift+F10파일을 오른쪽 클릭새 메뉴(Windows 11)IExplorerCommand + ID 명령옛 메뉴(Windows 10 메뉴)고전 IContextMenu 확장여러 명령은 플라이아웃으로 모인다

그림 10: 새 메뉴에 나오는 명령은 IExplorerCommand + ID 명령뿐이며, 고전 확장은 옛 메뉴 쪽으로 옮겨진다.

5.2. 새 메뉴로 가는 공식 경로 ── IExplorerCommand + 매니페스트 등록

새 메뉴에 사용자 명령을 올리는 방법은 하나뿐입니다. IExplorerCommand 인터페이스를 구현한 네이티브 DLL을 준비하고, MSIX 패키지 매니페스트에 COM 서버와 컨텍스트 메뉴 확장을 선언합니다.7

<!-- 패키지 매니페스트(발췌) -->
<com:Extension Category="windows.comServer">
  <com:ComServer>
    <com:SurrogateServer DisplayName="Komura commands">
      <com:Class Id="01234567-89AB-CDEF-0123-456789ABCDEF"
                 Path="KomuraCommand.dll" ThreadingModel="STA" />
    </com:SurrogateServer>
  </com:ComServer>
</com:Extension>
<desktop4:Extension Category="windows.fileExplorerContextMenus">
  <desktop4:FileExplorerContextMenus>
    <desktop5:ItemType Type=".kmrpt">
      <desktop5:Verb Id="VerifyReport"
                     Clsid="01234567-89AB-CDEF-0123-456789ABCDEF" />
    </desktop5:ItemType>
  </desktop4:FileExplorerContextMenus>
</desktop4:Extension>

ItemTypeType은 특정 확장자, 또는 *(모든 파일), Directory(폴더), Directory\Background(폴더 배경)를 지정할 수 있습니다. DLL은 탐색기의 아키텍처(64비트 / ARM64)에 맞춥니다.7

IExplorerCommand 자체는 Windows 7 시대부터 있는 인터페이스이며, 제목(GetTitle), 아이콘(GetIcon), 사용/사용 안 함/숨김 상태(GetState), 실행(Invoke)을 구현합니다. 메서드는 UI 스레드에서 호출되므로 네트워크 리소스 접근은 금지이며, 메뉴 구축 메서드는 빨리 돌아와야 합니다. 무거운 일은 Invoke 뒤에 합니다.147

새 메뉴 등록의 매니페스트 구조MSIX 매니페스트의 COM 서버 선언이 CLSID를 DLL에 매핑하고, 컨텍스트 메뉴 확장 선언이 ItemType과 Verb로 대상과 구현을 묶으므로, 사용자 명령이 새 메뉴에 나타난다CLSID를 DLL에 매핑ItemType과 Verb로 지정MSIX 매니페스트COM 서버 선언메뉴 확장 선언IExplorerCommand 구현 DLL명령이 새 메뉴에 나타남대상은 확장자, 모든 파일 등

그림 11: 매니페스트의 두 선언이 구현 DLL과 대상을 묶고, 명령이 새 메뉴에 나타난다.

5.3. 패키지되지 않은 앱을 위한 선택 ── sparse package로 ID만 얻기

「우리 앱은 MSI 말고는 배포할 수 없고, MSIX는 불가능하다」일 때의 비상구가 sparse package(외부 위치 MSIX)입니다. 앱 본체 없이 매니페스트만인 작은 MSIX에 서명하고, 기존 설치 프로그램의 마지막에 등록합니다. 그러면 앱이 패키지 ID를 얻고, 위의 매니페스트 등록(= 새 메뉴 표시)이 가능해집니다. Windows 10 버전 2004부터 사용할 수 있고, 패키지에는 대상 컴퓨터에서 신뢰되는 인증서의 서명이 필요합니다.8

sparse package로 ID를 얻는 흐름기존 설치 프로그램이 앱 본체를 배치한 뒤, 매니페스트만인 sparse package를 외부 위치로 등록하면 앱이 패키지 ID를 얻고 새 메뉴 매니페스트 등록이 가능해진다기존 설치 프로그램앱 본체를 배치sparse package매니페스트만, 본체 없음외부 위치로 등록패키지 ID를 획득새 메뉴 등록이 가능해짐신뢰되는 서명이 필요

그림 12: 본체 없는 sparse package를 외부 위치로 등록하면 앱이 패키지 ID를 얻는다.

가장 큰 이점은 설치 프로그램을 갈아엎지 않아도 된다는 점이며, 이미 MSI/EXE 설치 프로그램 자산을 가진 앱의 현실적인 답입니다. MSIX로의 전면 이전과의 비교는 「Windows 앱의 배포 방식을 어떻게 고를까 - MSI / MSIX / ClickOnce / xcopy / 자체 updater의 판단표」도 보십시오.

5.4. 연결 verb가 새 메뉴에 나타나는 방식

오해하기 쉬운 점입니다. 2장과 3장의 연결(ProgID와 verb)은 새 메뉴에서도 살아 있습니다. 더블클릭의 기본 verb, 「열기」, 「연결 프로그램」 후보는 연결에서 해석되어 새 메뉴 위쪽에 표시됩니다. 따라서 「이 앱으로 열 수 있으면 된다」만 원하면 Windows 11에서 추가 작업은 없습니다. 반면 연결은 범용 메뉴 확장이 아니므로, 임의의 사용자 명령을 새 메뉴 첫 층에 올리려면 IExplorerCommand와 ID가 필요합니다. 그것이 역할 분담입니다.7

연결과 새 메뉴의 역할 분담ProgID와 verb 연결은 새 메뉴에서도 기본 verb, 열기, 연결 프로그램을 해석하는 데 쓰여 위쪽에 표시되며, 임의의 사용자 명령을 새 메뉴 첫 층에 올리려면 IExplorerCommand와 ID가 필요하다연결(ProgID + verb)기본 / 열기를 해석새 메뉴 위쪽Win11에서 추가 작업 없음사용자 지정 명령IExplorerCommand+ID새 메뉴 첫 층

그림 13: 연결은 새 메뉴에서도 「열기」 계열 해석을 담당하며, 사용자 지정 명령만 IExplorerCommand와 ID가 필요하다.

6. 실무 판단표 ── 세 선택 중 어느 것을 택할 것인가

지금까지를 실무의 삼지선다로 정리합니다.

이루고 싶은 것 권장 수단 Windows 11에서의 모습 필요한 작업과 비용
(a) 더블클릭이나 「열기」로 사내 앱을 기동 연결 + 정적 verb(레지스트리 등록만) 새 메뉴의 「열기」와 「연결 프로그램」에 통합 설치 프로그램 레지스트리 등록만. DLL 없음, 추가 서명 요구 없음
(b) 선택한 파일/폴더에 대한 사용자 명령을 새 메뉴에 올리기 IExplorerCommand 구현 + MSIX 매니페스트 등록. 패키지되지 않은 앱은 sparse package로 ID를 부여 새 메뉴 첫 층(여러 명령은 앱 이름 플라이아웃으로 모임) 네이티브 C++ DLL + 패키지 ID + 코드 서명
(c) 기존 고전 IContextMenu 확장을 계속 쓰기 당분간은 그대로 유지(신규 개발에는 고르지 않음) 「더 많은 옵션 표시」(Shift+F10)의 옛 메뉴 쪽만 64비트 빌드와 COM 등록을 유지. 이후 (b)로의 이전을 계획

판단 지점은 둘입니다. 첫째, (a)로 충족되는 요구에 (b)나 (c)를 끌어들이지 마십시오. 셸 확장을 쓰는 순간 탐색기 안정성의 책임을 집니다. 둘째, (c)는 「고장나지 않았을」 뿐이며, 사용자 경험으로는 한 걸음 뒤집니다. 일상 운용에서 자주 쓰는 명령일수록 (b)로 옮기는 수익이 큽니다.

세 선택을 고르는 방법더블클릭이나 열기로의 기동만 원하면 연결과 정적 verb로 충분하다. 사용자 명령을 새 메뉴에 올리려면 IExplorerCommand와 MSIX 매니페스트 등록을 쓰고, MSIX가 될 수 없으면 sparse package로 ID를 부여한다. 기존 고전 IContextMenu 확장은 당분간 옛 메뉴 쪽에 둔다아니오아니오아니오열기로 충분한가?연결 + 정적 verb새 메뉴에 사용자 명령?MSIX가 될 수 있나?IExplorerCommand+MSIXsparse-pkg ID고전을 당분간 유지옛 메뉴 쪽만DLL 없음, 위험 작음

그림 14: 요구에 따라 정적 verb, IExplorerCommand와 ID, 고전 유지 가운데 고른다.

7. 실무의 배포와 등록 ── 설치 프로그램, sparse package, 정리

7.1. HKLM인가 HKCU인가

설치 프로그램의 형태에 맞춥니다. 전 사용자(Program Files 아래, 관리자 권한)는 HKLM\Software\Classes, 사용자별 설치(상승 없음)는 HKCU\Software\Classes입니다. 섞으면 「A는 열리는데 B는 안 열린다」는 문의가 납니다. CLSID 등록이 얽힌 셸 확장에서는, 레지스트리 등록 자체를 없애는 Reg-Free COM이 앱 내 COM 사용에는 유효한 선택이지만, 탐색기가 로드하는 셸 확장에는 적용할 수 없으므로 정공법의 등록이 필요합니다(「Reg-Free COM이란 무엇인가 - 등록 불필요로 COM을 쓰는 구조와, 맞는 장면·맞지 않는 장면」).

7.2. 변경 뒤에는 알린다 ── SHChangeNotify

연결을 등록·변경·삭제한 뒤에는 SHChangeNotify로 SHCNE_ASSOCCHANGED 이벤트를 알립니다. 이것을 빠뜨리면 재부팅까지 탐색기가 변경을 알아채지 못할 수 있습니다.110

// 연결을 바꾼 뒤 한 번 호출한다. 예: 설치 프로그램 사용자 지정 동작에서
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);

7.3. sparse package의 등록과 제거

sparse package의 등록과 제거는 설치 프로그램의 일입니다. 파일을 배치한 뒤에 등록하고, 파일을 지우기 전에 제거합니다.8

# 설치 시: 파일을 배치한 뒤, 설치 폴더를 외부 위치로 등록
Add-AppxPackage -Path "C:\Program Files\KomuraSoft\KomuraReport.identity.msix" `
                -ExternalLocation "C:\Program Files\KomuraSoft"

# 제거 시: 파일을 지우기 전에 패키지 등록을 제거
Remove-AppxPackage <package full name>

주의점: Add-AppxPackage실행한 사용자에게 등록합니다. 컴퓨터별 MSI의 사용자 지정 동작에서 LocalSystem으로 호출하면 설치한 사용자에게 ID가 부여되지 않으므로, 사용자 가장으로 실행하도록 구성합니다. 그래도 가장 아래의 등록은 그 설치를 실행한 사용자만의 것입니다. 여러 사용자가 쓰는 PC에서는 다른 사용자와 나중에 만든 사용자에게 패키지 ID가 없고, 명령이 새 메뉴에 나타나지 않습니다. 모든 사용자가 쓰게 하려면 첫 기동 때 자신의 패키지 등록을 확인하고 없으면 등록하는 메커니즘(사용자별 등록)을 두고, 제거 계획에는 등록이 있는 각 사용자로부터의 제거를 넣습니다. 매니페스트 등록을 반영하려면 탐색기 재시작(또는 로그오프)이 필요할 수도 있습니다.7

sparse package 등록과 제거의 순서설치 시에는 파일을 배치한 뒤 sparse package를 등록하고, 제거 시에는 파일을 지우기 전에 등록을 제거한다. 등록은 실행한 사용자에게만 유효하다는 점을 본다설치파일을 배치sparse package를 등록제거패키지 등록을 제거파일을 삭제등록은 실행 사용자에게만 유효

그림 15: 파일을 배치한 뒤에 등록하고, 지우기 전에 제거하며, 등록이 실행 사용자별이라는 점을 본다.

7.4. 제거 시의 정리 ── 지울 것과 남길 것

제거 시의 정리에는 분명한 공식 안내 선이 있습니다.1

  • 지운다: 사내 ProgID 키 전체, Capabilities/RegisteredApplications 등록, 셸 확장의 CLSID 등록, sparse package(Remove-AppxPackage).
  • 남긴다: 확장자 키(.kmrpt)의 기본값. 사내 ProgID를 아직 가리키고 있어도 지우지 않는 것이 공식 권장입니다. 설치 뒤에 다른 앱이 기본값을 가져갔는지 판단하기 어렵고, Windows는 등록되지 않은 기본값 ProgID를 그냥 무시하므로, 남겨도 실질 해는 없습니다.
  • 정리의 마지막에도 SHChangeNotify(SHCNE_ASSOCCHANGED)를 호출합니다.

「제거했는데 메뉴에 잔여가 남는다」는 문제의 대부분은 이 정리 설계의 누수입니다.

제거 시의 정리 설계제거 시에는 사내 ProgID 키, CLSID 등록, sparse package를 지우고, 등록되지 않은 ProgID는 무시되므로 확장자 키 기본값은 남긴다. 정리 마지막에 SHChangeNotify로 변경을 알린다제거지운다남긴다ProgID와 CLSID 등록sparse package확장자 키 기본값등록되지 않은 ProgID는 무시됨마지막에 SHChangeNotify로 알림

그림 16: 사내 등록은 지우고, 확장자 키 기본값은 남기며, 정리 마지막에 변경을 알린다.

8. 문제 해결 ── 안 나옴, 중복, 무거움

8.1. 메뉴에 나타나지 않는다

이 순서로 가릅니다.

  1. 보고 있는 메뉴가 어느 쪽인가: 고전 스타일 등록은 Shift+F10의 옛 메뉴 쪽에만 나타납니다. 먼저 양쪽을 확인합니다.
  2. 비트 수: 32비트 전용 셸 확장 DLL은 64비트 탐색기에 로드되지 않습니다(4.3절).
  3. 등록 대상: HKLM/HKCU, Wow6432Node 혼동. reg query로 실제 키를 확인합니다.
  4. 패키지 등록: 새 메뉴라면 Get-AppxPackage로 존재, 서명 인증서의 신뢰, -ExternalLocation 경로를 확인한 뒤 탐색기를 다시 시작합니다.7
  5. 알림 누락: SHChangeNotify를 빠뜨렸다면, 탐색기 재시작으로 반영되는지 보면 알 수 있습니다.
메뉴에 나타나지 않을 때의 격리 순서보고 있는 메뉴가 어느 쪽인지 확인한 뒤, DLL 비트 수, 레지스트리 등록 대상, 패키지 등록과 서명, SHChangeNotify 누락을 그 순서로 가른다옛 메뉴인지 새 메뉴인지 확인DLL 비트 수를 확인HKLM과 HKCU 등록 대상을 확인패키지 등록과 서명을 확인재시작으로 알림 누락을 판별

그림 17: 「안 나온다」면 보고 있는 메뉴, 비트 수, 등록 대상, 패키지 등록, 알림 누락 순으로 가른다.

8.2. 두 번 나타나거나, 사라지지 않는다

전형적인 원인은 고전 레지스트리 등록과 매니페스트 등록의 공존, 제거 정리의 누수(7.4절), 옛 버전 ProgID의 잔여입니다. 옛 메뉴에만 두 번 나타나면 잔여를, 옛것과 새것에 모두 나타나면 공존을 생각합니다.

이중 표시의 격리옛 메뉴에만 두 번이면 정리 누수나 옛 ProgID 같은 잔여를 가리키고, 옛것과 새것에 모두 두 번이면 고전 레지스트리 등록과 매니페스트 등록의 공존을 가리킨다옛 메뉴만옛것과 새것 모두어디에 두 번 나타나는가?잔여공존정리 누수 또는 남은 옛 ProgID고전 레지스트리 등록과 새 등록의 공존

그림 18: 옛 메뉴에만 두 번이면 잔여, 옛것과 새것에 모두 두 번이면 공존을 가리킨다.

8.3. 탐색기가 무겁거나 크래시한다

오른쪽 클릭이 느리거나 특정 폴더에서 크래시하면, 먼저 설치된 셸 확장을 목록화합니다. NirSoft의 ShellExView 같은 도구로 Microsoft 이외의 확장을 나열하고, 의심되는 것을 잠시 끄고 이진 탐색으로 원인 DLL을 찾습니다. 크래시에서는 이벤트 뷰어의 「Faulting module」도 단서입니다. 사내 확장이 원인이었다면, 메뉴 구축 경로의 동기 I/O나 네트워크 접근을 의심합니다(4.2절과 5.2절).

무겁거나 크래시할 때 원인 DLL을 찾기ShellExView에서 Microsoft 이외의 셸 확장을 나열하고, 의심되는 것을 잠시 끄고 이진 탐색으로 원인 DLL을 찾는다. 크래시에서는 이벤트 뷰어의 faulting module도 단서다셸 확장을 목록화Microsoft 이외를 나열잠시 끄고 이진 탐색원인 DLL을 특정크래시 시faulting module을 확인

그림 19: Microsoft 이외의 확장을 잠시 끄고 이진 탐색하며, 크래시에서는 이벤트 뷰어도 쓴다.

8.4. 검증에는 Windows Sandbox가 편리하다

셸 통합의 검증은 「깨끗한 환경에 설치 → 조작 → 제거 → 잔여 제로」를 확인하는 것이 기본입니다. 여기서 편리한 것이 Windows Sandbox(Pro/Enterprise/Education)입니다. 실행할 때마다 몇 초 만에 일회용 Windows가 새로 뜨므로, 설치 프로그램의 등록과 정리 테스트를 몇 번이든 돌릴 수 있습니다. 닫으면 모두 사라지므로 잔여 레지스트리 조사에도 맞습니다.15

9. 정리

  • 파일 연결은 「확장자 키 → ProgID → verb」의 삼층 구조이며, HKCR는 HKLM/HKCU Classes의 병합 뷰입니다. 쓰기 대상을 명시하고, %1은 항상 따옴표로 감쌉니다.
  • 기본 앱은 사용자가 고르도록 설계되어 있으며 프로그램에서 바꿀 수 없습니다. 설치 프로그램의 일은 후보로 올바르게 등록하는 것까지입니다.
  • 고전 셸 확장은 탐색기에 로드되는 프로세스 내 COM DLL입니다. 크래시나 지연은 전체로 퍼지고, 64비트가 필요하며, 관리 코드는 지원되지 않고, 구현은 네이티브 C++이 원칙입니다.
  • Windows 11에서 컨텍스트 메뉴가 둘로 나뉘었습니다. 새 메뉴에 사용자 명령을 올리려면 IExplorerCommand와 MSIX 매니페스트가 필요하고, 고전 IContextMenu는 「더 많은 옵션 표시」 쪽으로 옮겨집니다.
  • MSIX가 될 수 없는 앱에는 sparse package(외부 위치 MSIX)로 ID를 얻는 것이 현실적인 답입니다.
  • 「이 앱으로 연다」만 원하면 연결과 정적 verb로 충분합니다. 가장 단순한 수단부터 시작하는 것이 공식 지침이기도 합니다.
  • 등록·변경·삭제 뒤에는 SHChangeNotify로 알리고, 제거 시에는 ProgID는 지우되 확장자 키 기본값은 남깁니다. 검증에는 Windows Sandbox가 편리합니다.

Windows 11 PC 교체로 「메뉴가 숨겨졌다」는 것을 알아차렸다면, 먼저 6장의 판단표에서 (a), (b), (c) 중 어느 것인지 확인하십시오. 그 자리에서 작업 규모를 가늠할 수 있어야 합니다.

관련 기사

관련 상담 영역

합동회사 코무라소프트는 업무 앱의 파일 연결, 컨텍스트 메뉴, 셸 확장의 설계와 구현, Windows 11 새 컨텍스트 메뉴를 겨냥하는 작업(IExplorerCommand로의 이전, sparse package 도입), 기존 설치 프로그램의 등록과 정리 검토, 탐색기가 무겁거나 크래시하는 원인 조사를 다룹니다. 「더 많은 옵션 표시」 아래로 숨겨진 메뉴를 어떻게 할지 정하는 단계부터여도 괜찮습니다.

참고 링크

  1. Microsoft Learn, File Types. 확장자 키가 ProgID를 가리키는 구조, OpenWithProgIds, HKLM/HKCU\Software\Classes에서의 등록 분기, 연결 변경 뒤 SHChangeNotify(SHCNE_ASSOCCHANGED) 호출, 제거 시 ProgID는 지우되 확장자 키 기본값은 남긴다는 점에 대해.  2 3 4 5

  2. Microsoft Learn, HKEY_CLASSES_ROOT Key. HKEY_CLASSES_ROOT가 HKLM\Software\Classes와 HKCU\Software\Classes의 병합 뷰라는 점, 사용자 쪽 정의가 컴퓨터 쪽보다 우선한다는 점, 쓸 때의 분배 규칙에 대해.  2

  3. Microsoft Learn, Windows app defaults platform. 기본 앱 변경이 시스템 설정 UI를 통해서만 하도록 설계되었다는 점, 사용자 설정 데이터가 난독화되고 필터 드라이버(UCPD.sys)로 쓰기 보호된다는 점, 레지스트리 기반 변경이 지원되지 않는다는 점, 관리 환경에서는 그룹 정책 / MDM 정책을 쓴다는 점에 대해.  2

  4. Microsoft Learn, Working with Shell Extensions. 셸 확장 핸들러의 종류, 확장이 탐색기(및 셸을 호스트하는 프로세스)에 로드되는 프로세스 내 COM DLL이므로 크래시나 정지가 탐색기 전체로 퍼진다는 점, ThreadingModel=Apartment 등록, 셸 확장 전에 더 단순한 대안을 검토한다는 점에 대해.  2 3

  5. Microsoft Learn, Guidance for Implementing In-Process Extensions. Microsoft가 관리 코드로 프로세스 내 셸 확장을 구현하는 것을 권장하지 않고 지원하지 않는다는 점, CLR 버전 충돌·재진입·비결정적 객체 수명 등의 이유, 프로세스 외 확장(미리 보기 핸들러, 또는 shell\verb\command에서의 기동)은 관리 코드가 허용된다는 점에 대해.  2 3

  6. Windows Developer Blog, Extending the Context Menu and Share Dialog in Windows 11. Windows 11 새 컨텍스트 메뉴의 설계, IExplorerCommand와 앱 ID를 통한 확장, 「열기」와 「연결 프로그램」을 위쪽에 두는 점, 여러 명령을 앱 이름 플라이아웃으로 모으는 점, 고전 IContextMenu 확장이 「더 많은 옵션 표시」(Shift+F10)로 Windows 10 메뉴로 로드된다는 점에 대해.  2 3

  7. Microsoft Learn, Add a File Explorer context menu command to a packaged desktop app. Windows 11 새 컨텍스트 메뉴 등록이 IExplorerCommand 구현과 windows.comServer 및 desktop4:FileExplorerContextMenus 매니페스트 선언으로 이루어진다는 점, ItemType이 *, Directory, Directory\Background를 지정할 수 있다는 점, DLL 아키텍처 맞추기, 메뉴 구축 메서드를 빠르게 유지하기, 패키지되지 않은 앱을 sparse package로 커버하기, 등록 반영에 탐색기 재시작이 필요할 수 있다는 점, 파일 연결이 범용 메뉴 확장이 아니라는 점에 대해.  2 3 4 5 6 7 8

  8. Microsoft Learn, Grant package identity by packaging with external location. 기존 설치 프로그램을 바꾸지 않고 외부 위치 패키지(sparse package)를 등록해 패키지 ID를 얻는다는 점, Windows 10 버전 2004부터 사용할 수 있다는 점, ID가 필요한 Windows 기능(컨텍스트 메뉴 등록, 알림 등)을 쓸 수 있게 된다는 점에 대해.  2 3

  9. Microsoft Learn, Choosing a Static or Dynamic Shortcut Menu Method. 요구를 충족하는 가장 단순한 정적 verb 방법을 고른다는 점, IContextMenu가 가장 강력하지만 가장 복잡하고 비권장 쪽으로 분류된다는 점, IExplorerCommand/IExplorerCommandState가 권장 방법이라는 점에 대해.  2

  10. Microsoft Learn, SHChangeNotify function. 파일 연결 변경을 시스템에 알리는 SHCNE_ASSOCCHANGED 이벤트를 올리는 방법과, 셸이 변경을 알아채도록 쓰는 방법에 대해.  2

  11. Microsoft Learn, Application Registration. App Paths 하위 키로 실행 파일을 등록하는 것이 권장된다는 점, Applications 하위 키의 역할, SystemFileAssociations를 통한 verb 등록, 기본 앱이 바뀔 때 ProgID와 관련 정보의 우선순위에 대해. 

  12. Microsoft Learn, Creating Shortcut Menu Handlers. 정적 verb 등록 방법, 기본 verb가 결정되는 순서(기본값 → 첫 verb → Open → Open With), 표준 verb의 표시 이름을 OS가 공급한다는 점, Extended를 통한 확장 verb, DDE 명령과의 연결이 Deprecated라는 점, 64비트 환경의 WOW64 리다이렉트 주의점에 대해.  2 3

  13. Microsoft Learn, Verbs and File Associations. verb가 ShellExecuteEx에서도 쓰이는 동작이라는 점, 공백이 들어갈 수 있는 명령 문자열 요소는 따옴표로 감싸고 “%1”은 항상 따옴표로 쓴다는 점, HKCR\Applications 아래의 기본 절차 등록에 대해. 

  14. Microsoft Learn, IExplorerCommand interface. GetTitle, GetIcon, GetState, Invoke, EnumSubCommands 등의 메서드 구성, 메서드가 UI 스레드에서 호출되므로 네트워크 리소스와 통신해서는 안 된다는 점, Windows Vista부터 사용할 수 있다는 점에 대해. 

  15. Microsoft Learn, Windows Sandbox. 몇 초 만에 일회용 격리 Windows 환경을 기동할 수 있고, 닫으면 모든 변경이 버려지며, 소프트웨어 테스트와 설치 프로그램 검증에 맞고, Pro/Enterprise/Education에서 사용할 수 있다는 점에 대해. 

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

ActiveX 이관

COM / ActiveX / OCX 자산을 유지할지, 감쌀지, 교체할지의 단계적 판단을 정리한 토픽 페이지입니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

Windows 11에서 우리 앱의 컨텍스트 메뉴 항목이 「더 많은 옵션 표시」 아래에만 나타나는 이유는 무엇인가요?
Windows 11에서 파일 탐색기의 컨텍스트 메뉴가 새 메뉴와 옛 메뉴의 두 층으로 나뉘었기 때문입니다. 새 메뉴에 나올 수 있는 명령은 IExplorerCommand 인터페이스를 구현하고 MSIX 패키지 매니페스트에 등록된 것(= 패키지 ID를 가진 것)뿐입니다. 고전 IContextMenu 기반 셸 확장은 「더 많은 옵션 표시」(Shift+F10)로 여는 옛 메뉴로 옮겨졌습니다. 확장 자체가 고장난 것은 아니므로 당분간은 동작하지만, 새 메뉴에 올리려면 IExplorerCommand로의 이전과 MSIX 패키징 또는 sparse package로 ID를 부여하는 작업이 필요합니다.
설치 프로그램이 파일을 더블클릭했을 때 열리는 기본 앱으로 우리 앱을 설정할 수 있나요?
할 수 없습니다. 기본 앱 선택은 사용자가 하도록 설계되어 있고, Windows는 시스템 설정 UI 이외의 경로에서 기본 앱을 바꾸는 것을 지원하지 않습니다. 각 사용자의 선택을 담는 UserChoice 정보는 난독화되어 있고, 필터 드라이버(UCPD.sys)가 앱의 쓰기도 차단합니다. 설치 프로그램이 할 수 있는 일은 ProgID와 verb를 등록하고, OpenWithProgIds에 자신을 더해 「연결 프로그램」 후보로 보이게 하며, 사용자를 기본 앱 설정 페이지로 안내하는 것입니다. 올바른 구현은 기본값을 가로채는 것이 아니라, 선택받을 준비를 하는 것입니다.
셸 확장을 C# 같은 관리 코드로 작성해도 되나요?
Microsoft는 프로세스 내 셸 확장(컨텍스트 메뉴 핸들러, 아이콘 핸들러 등)을 관리 코드로 작성하는 것을 권장하지 않으며 지원하지 않는다고 분명히 말합니다. 확장은 탐색기와 공통 파일 대화 상자를 연 모든 앱의 프로세스에 로드되므로, CLR 버전 충돌, 재진입, 비결정적인 객체 수명이 호스트 앱을 불안정하게 만듭니다. 구현은 네이티브 C++이 원칙입니다. verb의 command로 기동하는 일반 EXE나, 미리 보기 핸들러처럼 별도 프로세스에서 도는 프로세스 외 확장은 관리 코드로도 괜찮습니다.
sparse package(외부 위치 MSIX)란 무엇인가요?
앱 파일은 포함하지 않고 매니페스트(ID 정보)만 담은 작은 MSIX 패키지입니다. 기존 설치 프로그램(MSI, Inno Setup 등)으로 보통처럼 설치한 앱에, 설치 폴더를 가리키는 Add-AppxPackage -ExternalLocation으로 등록하면 앱이 패키지 ID를 얻고 Windows 11 새 컨텍스트 메뉴 등록이나 토스트 알림처럼 ID가 필요한 기능을 쓸 수 있습니다. Windows 10 버전 2004부터 사용할 수 있고, 대상 컴퓨터에서 신뢰되는 코드 서명이 필요합니다. 배포 방식 전체를 MSIX로 옮기지 않고 새 메뉴를 지원하고 싶을 때의 현실적인 선택입니다.
컨텍스트 메뉴 항목이 두 번 나타나거나, 사라지지 않으면 어떻게 하나요?
먼저 새 메뉴에 나타나는지, 옛 메뉴(더 많은 옵션 표시)에 나타나는지를 확인해 원인을 가릅니다. 전형적인 이중 표시는 고전 레지스트리 등록과 MSIX 매니페스트 등록이 공존하거나, 제거 시 ProgID나 확장 CLSID 등록이 남은 경우입니다. 연결을 바꾼 뒤에는 SHChangeNotify(SHCNE_ASSOCCHANGED)를 빠뜨린 것도, 패키지 등록 직후에는 탐색기 재시작을 빠뜨린 것도 의심합니다. 그래도 해결되지 않으면 ShellExView에서 Microsoft 이외의 확장을 잠시 끄고 이진 탐색으로 원인 DLL을 찾습니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기