Context menu ו-file association ב-Windows 11 —‏ IExplorerCommand, MSIX ו-sparse package

· עודכן בתאריך: · · Windows, shell extension, context menu, file association, COM, Windows 11, File Explorer, MSIX, פיתוח Windows

היסטוריית עדכונים (גרסה ראשונה, פורסמה בתאריך 20 Aug 2026)
פרסום ראשון
לצטט את המאמר הזה(DOI: 10.5281/zenodo.22176262)

מאמר זה מאוחסן בארכיון Zenodo. להלן גם ה-DOI שתמיד מפנה לגרסה האחרונה וגם ה-DOI המקובע לגרסה שאתם קוראים.

Go Komura (2026). Context menu ו-file association ב-Windows 11 —‏ IExplorerCommand, MSIX ו-sparse package. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22176262 https://comcomponent.com/he/blog/windows-shell-integration-context-menu-file-association/

DOI (הגרסה האחרונה)
10.5281/zenodo.22176262
DOI (הגרסה הזו)
10.5281/zenodo.22176263

קיבלתי פנייה ש«החלפנו את המחשבים ל-Windows 11, וה-context menu של האפליקציה שבניתם לנו לפני שנים נעלם». בהקשבה מדויקת יותר, הוא לא נעלם. לוחצים לחיצה ימנית על קובץ, בוחרים “הצג אפשרויות נוספות” בתחתית התפריט, והתפריט המוכר מופיע כמו תמיד. כלומר פריטי התפריט של האפליקציה הוסתרו לחיצה אחת פנימה. מהשטח שומעים «לחיצה אחת נוספת» ו«פניות על כך שלא מוצאים את הפריט התרבו».

זו לא תקלה ולא טעות הגדרה; זה שינוי עיצוב של Windows 11. ה-context menu של File Explorer הפך למבנה דו-שכבתי, ישן וחדש, והתנאים להעלות פריט לתפריט החדש שונים לגמרי ממה שהיה.

בינתיים מנגנון file association ו-shell extension מתחתיו עדיין עולם COM ו-Registry ישן. מפתח extension מצביע על ProgID, ה-verb של ה-ProgID מחזיק command line, ו-extension מורכב יותר רץ כ-in-process COM server (DLL) שנטען ל-Explorer — המבנה הזה לא השתנה יותר מעשרים שנה. אם לא מכירים גם את היסוד שלא השתנה וגם את התפריט ש-Windows 11 פיצלה לשניים, אי אפשר לבודד «התפריט לא מופיע», «הוסתר» או «מופיע פעמיים».

המאמר מיועד לאנשי IT בעסקים קטנים ובינוניים ולמפתחי Windows שמטפלים באפליקציות עסקיות. הוא קושר בתמונה אחת את המבנה התלת-שלבי של file association, מגבלות של shell extension קלאסי, איך לכוון ל-context menu החדש של Windows 11, ורישום installer, cleanup ו-troubleshooting.

1. קודם כל, המסקנות

  • היסוד של context menu ושל file association הוא מבנה Registry תלת-שלבי: מפתח extension → ProgID → verb. מפתח ה-extension הוא pointer ל-ProgID, ה-ProgID הוא האובייקט עצמו, ו-shell\<verb>\command תחתיו מחזיק את ה-command line.1
  • HKEY_CLASSES_ROOT (HKCR) אינה hive עצמאית; היא merged view של HKLM\Software\Classes ו-HKCU\Software\Classes. רישום לכל המשתמשים כותבים ל-HKLM, רישום לפי משתמש ל-HKCU, ו-HKCR מתייחסים אליו כקריאה בלבד.2
  • Default app (זו שנפתחת ב-double-click) מיועדת להיבחר בידי המשתמש, ותוכנית לא יכולה לגנוב אותה. ה-OS מגן על בחירת המשתמש; מה ש-installer יכול לעשות הוא להירשם כמועמד.3
  • Shell extension קלאסי הוא in-process COM DLL שנטען ל-Explorer. crash או delay ב-extension מתפשטים ל-Explorer כולו (ולאפליקציות אחרות שמשתמשות ב-shell); בסביבת 64-bit נדרש DLL של 64-bit; ומימוש ב-managed code אינו נתמך.45
  • ב-Windows 11 ה-context menu התפצל לשניים. הפקודות היחידות שמופיעות בתפריט החדש הן אלה שנרשמו עם IExplorerCommand ועם package identity; הרחבות IContextMenu קלאסיות עוברות לתפריט הישן תחת «הצג אפשרויות נוספות» (Shift+F10).67
  • המסלול הרשמי להעלות command מותאם לתפריט החדש הוא לרשום native DLL שמממש IExplorerCommand ב-MSIX manifest (desktop4:FileExplorerContextMenus). אפליקציה שאינה יכולה לעבור ל-MSIX יכולה לקבל identity בלבד עם sparse package (MSIX עם external location).78
  • אם כל מה שרוצים הוא «לפתוח עם האפליקציה הזו», file association ו-static verb עדיין מספיקים. אין צורך ב-DLL של shell extension, ו-Microsoft עצמה כותבת במפורש «בחרו בשיטה הפשוטה ביותר שעומדת בדרישות (static verb)».9
  • אחרי רישום או שינוי מודיעים ב-SHChangeNotify(SHCNE_ASSOCCHANGED); ב-uninstall מוחקים את ה-ProgID אך לא מוחקים את ערך ברירת המחדל של מפתח ה-extension — זה המדריך הרשמי. shell integration כולל גם את עיצוב ה-cleanup.110

במשפט אחד: עולם ה-file association וה-verbs לא השתנה; רק אופן הצגת התפריט התפצל לשניים ב-Windows 11. להלן עולים מהיסוד.

2. איך עובד file association — המבנה התלת-שלבי מפתח extension → ProgID → verb

2.1. קריאת המבנה התלת-שלבי מתוך דוגמה אחת

מה שקורה ב-double-click על קובץ עם extension נתון נקבע בשלוש שכבות של מפתחות Registry.1

HKEY_CLASSES_ROOT
   .kmrpt                                  ← (1) מפתח ה-extension
      (Default) = KomuraSoft.Report.1      ←     pointer שרק נותן שם ל-ProgID
      OpenWithProgids
         KomuraSoft.Report.1               ←     מועמד תחת «פתיחה באמצעות»
   KomuraSoft.Report.1                     ← (2) ProgID (האובייקט של ה-association)
      (Default) = Komura Report document
      DefaultIcon
         (Default) = "C:\Program Files\KomuraSoft\Report.exe",0
      shell                                ← (3) רשימת verbs
         open
            command
               (Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
  • (1) מפתח ה-extension (.kmrpt) מצביע רק על שם ProgID כערך ברירת המחדל שלו. כתיבת command כאן ישירות היא טעות.
  • (2) ה-ProgID (KomuraSoft.Report.1) הוא האובייקט של ה-association; הוא מחזיק את display name, האייקון ורשימת ה-verbs.
  • (3) verb הוא פעולה כמו «פתיחה» או «הדפסה», וערך ברירת המחדל של shell\open\command הוא ה-command line שמופעל בפועל.

ההפרדה הזו היא הסיבה שאפשר להצביע כמה extensions (למשל .kmrpt ו-.kmrpt-file) לאותו ProgID, או להחליף את ה-ProgID בשדרוג האפליקציה.

המבנה התלת-שלבי של file associationמפתח ה-extension הוא pointer שערך ברירת המחדל שלו נותן שם ל-ProgID; ה-ProgID הוא האובייקט שמחזיק display name, אייקון ורשימת verbs; וערך ברירת המחדל של command תחת ה-verb הוא ה-command line שמופעל בפועלמצביע ל-ProgID כברירת מחדלמפתח extension .kmrptProgID KomuraSoft.Report.1verb (open ואחרים תחת shell)ערך ברירת המחדל של commandReport.exe מופעלמחזיק גם display name ו-DefaultIcon

איור 1: מפתח ה-extension הוא pointer, ה-ProgID הוא האובייקט, ו-command של ה-verb הוא ה-command line שמופעל בפועל.

2.2. HKCR היא «merged view» — לאן כותבים משנה את המשמעות

הדוגמה למעלה מוצגת תחת HKEY_CLASSES_ROOT (HKCR), אבל HKCR אינה מיקום אחסון פיזי; היא merged view של HKLM\Software\Classes ו-HKCU\Software\Classes. אם אותו מפתח קיים בשניהם, צד HKCU מנצח.2

HKCR היא merged viewHKCR היא Classes של HKLM ושל HKCU מונחות זו על זו; אם אותו מפתח קיים בשניהם HKCU מנצחת; כותבים רישום ל-HKLM או ל-HKCU במפורש ומתייחסים ל-HKCR כקריאה בלבדHKLM\\Software\\Classes (כל המשתמשים)HKCR (merged view)HKCU\\Software\\Classes (לפי משתמש)אם אותו מפתח קיים, HKCU מנצחתמתייחסים אליה כקריאה בלבד (לאישור)

איור 2: HKCR היא איך נראות Classes של HKLM ושל HKCU יחד; תמיד מציינים אחד מהם כיעד כתיבה.

יעד כתיבה משמעות הרשאות נדרשות
HKLM\Software\Classes רישום משותף לכל המשתמשים Administrator
HKCU\Software\Classes רישום למשתמש הזה בלבד אין
כתיבה ישירה ל-HKCR מנותבת לפי היכן כבר חי המפתח הקיים תלוי

בפועל, הפיצול הבטוח הוא תמיד לכתוב רישום ל-HKLM או ל-HKCU במפורש, ולהתייחס ל-HKCR כקריאה בלבד (לאישור). גם היחס ל-WOW64 Registry redirect ראוי לסידור. נתוני association ישירות תחת HKLM\Software\Classes כמו מפתחות extension ו-ProgID משותפים בין תצוגות ה-Registry 32-bit ו-64-bit מאז Windows 7, כך ש-installer 32-bit שכותב אותם אינו בורח לצד Wow6432Node. חלק ממפתחות-המשנה של רישום COM כמו Classes\CLSID, לעומת זאת, מופנים, וכשרושמים shell extension (in-process COM) פיצול הכתיבה 32 / 64-bit חשוב. פרטים ב-“Registry 32-bit/64-bit Redirection and Virtualization Pitfalls — Wow6432Node and the "The Value I Wrote Isn’t There" Problem”.

2.3. רישום מצד האפליקציה — App Paths, Applications, RegisteredApplications

יש גם שלושה סוגי רישום מצד האפליקציה, שמתאימים לצד הקובץ (extension ו-ProgID).11

  • App Paths (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths): רישום שמאפשר ל-ShellExecuteEx להפעיל לפי שם קובץ ההפעלה בלבד. Microsoft ממליצה על כך כי אין צורך לזהם את משתנה הסביבה PATH.
  • Applications (HKCR\Applications\<app.exe>): מגדיר את דרך הפתיחה כברירת מחדל כשמוסרים קובץ שרירותי תחת «פתיחה באמצעות», ואת display name של האפליקציה (FriendlyAppName).
  • RegisteredApplications + Capabilities: מכריז על ה-extensions וסוגי MIME שהאפליקציה יכולה לטפל בהם, והוא הרישום שגורם לה להופיע כמועמדת בדף הגדרות default apps של Windows.

רוב הפניות מסוג «האפליקציה שלנו לא מופיעה ברשימת default apps» הן מקרים שבהם נרשם ה-ProgID והושמט רישום Capabilities הזה.

שלושת סוגי הרישום מצד האפליקציהרישום מצד האפליקציה יש לו שלושה סוגים — App Paths, Applications ו-RegisteredApplications — האחראים בהתאמה להפעלה לפי שם קובץ בלבד, לדרך הפתיחה כברירת מחדל תחת פתיחה באמצעות, ולהופעה בדף default appsרישום מצד האפליקציהApp PathsApplicationsRegisteredApplicationsהפעלה לפי שם קובץ בלבדברירת מחדל תחת פתיחה באמצעותמופיע בדף default appsנדרשת הכרזת Capabilities

איור 3: יש שלושה סוגי רישום מצד האפליקציה, והופעה כמועמדת ל-default apps דורשת רישום Capabilities.

2.4. Default app שייך למשתמש — הגנת UserChoice

כתיבת ProgID כערך ברירת המחדל של מפתח ה-extension אינה הופכת אותו לבדה ל-default app. תוצאת בחירה מפורשת של המשתמש תחת «פתיחה באמצעות» וכדומה נשמרת ב-HKCU\...\Explorer\FileExts\<extension>\UserChoice, ופתרון ה-association מעדיף את הצד ההוא.

והנקודה החשובה היא שWindows אינה תומכת בשינוי תכנותי של default app. הגדרות default app מיועדות להיעשות בידי המשתמש דרך Settings של המערכת; נתוני UserChoice הם obfuscated, ו-filter driver (UCPD.sys) חוסם כתיבה מאפליקציות. בסביבה מנוהלת, Group Policy / MDM היא האמצעי הרשמי.3

זה שכלים כמו SetUserFTA, ש«מחקים את ה-hash וכותבים מחדש», היו בשימוש הוא הצד השני של ההגנה הזו. מה שצריך לשים ב-installer של אפליקציה פנימית אינו גניבת ה-default, אלא השלושה (א) רישום נכון של ה-ProgID וה-verbs, (ב) הוספת עצמכם ל-OpenWithProgIds, ו-(ג) במידת הצורך, הפניה לדף ההגדרות.

פתרון default app והגנת UserChoiceתוצאת בחירה מפורשת של המשתמש נשמרת ב-UserChoice ומועדפת בפתרון ה-association; UCPD.sys חוסם כתיבה מחדש מאפליקציות, ולכן מה ש-installer יכול לעשות הוא להירשם כמועמד ולהפנות לדף ההגדרותמועדףUCPD.sys חוסםUserChoice (בחירת המשתמש)פתרון associationערך ברירת המחדל של מפתח ה-extensionכתיבה מחדש מאפליקציהעבודת ה-installerרישום ה-ProgID וה-verbsהוספה ל-OpenWithProgIdsהפניה לדף ההגדרות

איור 4: פתרון ה-association מעדיף את בחירת המשתמש (UserChoice), וה-OS מגן עליה מכתיבה מחדש של אפליקציות.

3. Verbs שאינם «open» — print, edit, runas ו-custom verbs

Verb אינו רק open. verbs תקניים שה-OS מכיר את משמעותם כוללים גם edit, print, play ו-preview מלבד open, ו-verb תקני מקבל אוטומטית display name שעוקב אחרי locale של ה-OS. ה-verb כברירת מחדל ב-double-click נקבע בסדר: ערך ברירת המחדל של מפתח shell → ה-verb הראשון ב-Registry → open → openwith.12

הסדר שבו נקבע ה-verb כברירת מחדלה-verb כברירת מחדל ב-double-click הוא הראשון שנמצא בסדר ערך ברירת המחדל של מפתח shell, ה-verb הראשון ב-Registry, open, openwithאם איןאם איןאם איןערך ברירת המחדל של מפתח shellה-verb הראשון ב-Registryopenopenwith

איור 5: ה-verb כברירת מחדל ב-double-click הוא הראשון שנמצא בסדר הזה.

כשרוצים להוסיף פעולה משלכם, רושמים custom 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                          ← custom verb
         (Default) = Verify report (&V)   ← display name בתפריט
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" /verify "%1"

שלוש עובדות קטנות שכדאי לדעת.

  • רושמים verb בשם runas ומגדירים הפעלה elevated ששקולה ל-«הפעל כמנהל», והוא משמש גם כש-API ממשפחת ShellExecute מציין runas.
  • שמים ערך ריק בשם Extended על מפתח ה-verb והוא הופך לextended verb שמוצג רק ב-Shift+לחיצה ימנית. נוח להסתרת פעולה מסוכנת שנדיר להשתמש בה.12
  • לחלק מ-associations של אפליקציות ישנות עדיין יש תצורה ששולחת מסמך ל-process קיים עם DDE (מפתח ddeexec), אבל הפעלת verb דרך DDE היא כבר מורשת Deprecated. אין סיבה לכתוב אותה מחדש.12

תאונה נוספת שקורה הרבה היא ציטוט ב-command line. אם רכיב במחרוזת הפקודה יכול להכיל רווח, חייבים לעטוף אותו במירכאות. זה חל כמובן על נתיב EXE כמו C:\Program Files\..., ו%1 (נתיב הקובץ שנבחר) צריך תמיד להיכתב "%1". אי אפשר להבטיח שנתיב קובץ של משתמש אינו מכיל רווח. My Program.exe בלי מירכאות מתפרש כ-«הפעל את My עם ה-argument Program.exe».13

תאונת הציטוט ב-command lineפקודה בלי מירכאות נחתכת ברווח ומתפרשת בטעות כהפעלת My עם ה-argument Program.exe, ולכן נתיב EXE שיכול להכיל רווח ו-%1 שמייצג את נתיב הקובץ שנבחר צריכים תמיד להיות עטופים במירכאותנחתכת ברווחפקודה בלי מירכאותמתפרשת בטעות כהפעלת EXE אחרפקודה במירכאותמופעלת כמתוכנןעוטפים את נתיב ה-EXE במירכאותעוטפים גם את %1 תמיד במירכאות

איור 6: פקודה בלי מירכאות נחתכת בטעות ברווח, לכן תמיד עוטפים את נתיב ה-EXE ואת %1 במירכאות.

מנגנון ה-Registry בלבד עד כאן (static verb) אפשר לממש בלי לכתוב אפילו DLL אחד, והוא אינו מסכן את יציבות Explorer. Microsoft עצמה חוזרת ואומרת «לפני שכותבים shell extension, בדקו אם ה-static verb הפשוט ביותר שעומד בדרישות יספיק».9

4. Shell extension קלאסי — DLL שרץ בתוך Explorer

4.1. סוגי shell extension

דרישות ש-static verb אינו יכול למלא — «שנו את התפריט דינמית לפי הבחירה», «החליפו את האייקון או property sheet» — משתמשות ב-shell extension handler. סוגים מייצגים להלן.4

Handler ממשק עיקרי מה הוא יכול לעשות
Context menu handler IContextMenu + IShellExtInit הוספה ושליטה דינמית בפריטי תפריט
Icon handler / icon overlay IExtractIcon / IShellIconOverlayIdentifier אייקון לכל קובץ ושכבת-על
Property sheet handler IShellPropSheetExt הוספת לשונית ל-property sheet
Thumbnail / infotip IThumbnailProvider / IQueryInfo תצוגת thumbnail ותיאור hover
Drag-and-drop / copy hook handler IDropTarget / ICopyHook התערבות ב-drop או בהעתקה/העברה

כל אלה ממומשים כמחלקות COM ונרשמים ב-Registry לפי CLSID. רעיון COM עצמו מכוסה ב-“מה זה COM /‏ ActiveX /‏ OCX — הסבר מרוכז על ההבדלים והקשרים”.

4.2. מה פירוש להיות in-process COM server

מהות shell extension קלאסי היא שזו in-process COM server (DLL) שנטען ל-Explorer (או לכל אפליקציה שפתחה common file dialog). כל אזהרה נובעת מכך.4

  • אם ה-extension קורס, Explorer נופל איתו. אם הוא נתקע, לחיצה ימנית קופאת כמה שניות. הנזק גם אינו מוגבל ל-Explorer; הוא מגיע לכל אפליקציה שהציגה דיאלוג פתיחת קובץ.
  • בניית התפריט מתרחשת ב-UI thread, לכן אסור לעשות עבודה איטית כמו גישה לרשת או I/O של קבצים בזמן הצגת התפריט.
  • רושמים את מודל ה-threading כ-Apartment ככלל.
מבנה הנזק הנלווה של extension in-processDLL של shell extension נטען לא רק ל-Explorer אלא גם ל-process של כל אפליקציה שפתחה file dialog, ולכן crash או hang ב-extension מתפשטים לכל ה-host processנטען in-processנטען in-processDLL של shell extensionExplorerכל אפליקציה שפותחת dialogcrash או hang מתפשטיםלא עושים עבודה איטית בזמן הצגה

איור 7: ה-DLL של ה-extension רץ בתוך ה-host process, ולכן crash או hang מתפשטים ל-host כולו.

חוקרים פנייה כמו «Explorer קופא כשאני פותח תיקייה מסוימת» או «לחיצה ימנית לוקחת חמש שניות» ולא נדיר שהסיבה היא shell extension של צד שלישי ולא האפליקציה הפנימית. שיטות בידוד בפרק 8.

4.3. התאמת bitness — סביבת 64-bit דורשת DLL של 64-bit

DLL in-process חייב להתאים ל-bitness של ה-process שטוען אותו. Explorer ב-Windows 64-bit הוא process 64-bit, לכן DLL של shell extension שנבנה רק כ-32-bit לעולם אינו נטען ולעולם אינו מופיע בתפריט. וגם אין שגיאה, ולכן זו סיבה קבועה ל-«רשמתי אבל זה לא מופיע». שילוב גוף אפליקציה 32-bit עם DLL של shell extension 64-bit הוא תצורה לגיטימית, אבל צריך לשים לב שרישום COM מתפצל לפי bitness (Wow6432Node). הפעלה מ-command של verb היא EXE ב-process נפרד, ולכן אינה כפופה למגבלה הזו (להשאיר אותו EXE 32-bit זה בסדר).

התאמת bitness של DLL של shell extensionDLL של shell extension היחיד ש-Explorer 64-bit יכול לטעון הוא בן 64-bit; DLL של 32-bit בלבד אינו מופיע בתפריט ואינו מייצר שגיאה; EXE שמופעל מ-verb command הוא process נפרד ואינו כפוף למגבלהיכול לטעוןלא יכול לטעוןprocess נפרדExplorer 64-bitDLL של shell extension 64-bitDLL של 32-bit בלבדלא מופיע בתפריט, בלי שגיאהEXE שמופעל מ-verbבסדר להשאיר 32-bit

איור 8: ה-DLL היחיד שנטען ל-Explorer 64-bit הוא DLL של 64-bit; EXE שמופעל מ-verb אינו כפוף למגבלה הזו.

4.4. מדוע אסור לכתוב זאת ב-managed code

אני מקבל לעיתים קרובות את השאלה «אפשר לכתוב shell extension ב-C#», אבל Microsoft קבעה במפורש שכתיבת shell extension in-process ב-managed code (.NET) אינה מומלצת ומחוץ לתמיכה.5

הסיבה היא טבע ה-extension שנטען ל-process שרירותי. התנגשות גרסאות CLR (במיוחד מתחת ל-.NET Framework 4), בעיית ה-reentrancy של ה-CLR ל-message loop בזמן המתנה על lock, ו-object lifetime לא-דטרמיניסטי מ-garbage collection שמתנגש בחוזה reference counting של COM הם סיבות מבניות לכך שאפליקציית ה-host הופכת ללא יציבה. חלק מהסעיפים הוקלו ב-.NET Framework 4 ואילך וב-.NET מודרני, אבל העמדה הרשמית לא השתנתה.

הקו המעשי פשוט. כותבים extension in-process ב-native C++. אם רוצים managed code, עושים ממנו EXE רגיל שמופעל מ-verb command, או out-of-process extension שרץ ב-process נפרד (preview handler וכדומה).5

שיפוט האם managed code מותרextension in-process שרץ בתוך Explorer נכתב ב-native C++ ככלל; אם רוצים managed code עושים ממנו EXE רגיל שמופעל מ-verb command או out-of-process extension שרץ ב-process נפרדכןלארץ in-process?כותבים ב-native C++managed code בסדרהתנגשות CLR / reentrancy הופכת את ה-host ללא יציבEXE שמופעל מ-verbpreview מחוץ ל-process

איור 9: extension in-process הוא native C++ ככלל; managed code מוגבל לתצורה שרצה ב-process נפרד.

5. ה-context menu החדש של Windows 11 — התפריט שהתפצל לשניים

5.1. מה קרה

Windows 11 ריעננה את ה-context menu של File Explorer. גזירה, העתקה וכדומה הפכו לשורת אייקונים למעלה; «פתיחה» ו-«פתיחה באמצעות» קובצו יחד למעלה; ופקודות שאפליקציה מוסיפה מקובצות מתחת לפקודות התקניות של ה-shell. כשאפליקציה אחת מוסיפה כמה commands, הן נאספות ל-flyout (תת-תפריט) בשם האפליקציה.6

והנקודה המכרעת היא זו. shell extensions קלאסיים מבוססי IContextMenu לא נמחקו; הם הועברו לצד התפריט הישן שנפתח ב-«הצג אפשרויות נוספות» (Shift+F10) וטוען את תפריט Windows 10 כפי שהוא.6 זהות פניית הפתיחה «התפריט הוסתר» היא הפיצול הזה.

ה-context menu ש-Windows 11 פיצלה לשנייםמה שנפתח ראשון בלחיצה ימנית הוא התפריט החדש; הפקודות היחידות שמופיעות שם הן אלה שנרשמו עם IExplorerCommand ו-package identity; הרחבות IContextMenu קלאסיות עוברות לתפריט הישן שנפתח בהצג אפשרויות נוספותהצג אפשרויות נוספות Shift+F10לחיצה ימנית על קובץהתפריט החדש (Windows 11)פקודות IExplorerCommand + identityהתפריט הישן (תפריט Windows 10)הרחבות IContextMenu קלאסיותכמה commands נאספות ל-flyout

איור 10: הפקודות היחידות שמופיעות בתפריט החדש הן פקודות IExplorerCommand + identity; הרחבות קלאסיות עוברות לצד התפריט הישן.

5.2. המסלול הרשמי לתפריט החדש — IExplorerCommand + רישום manifest

יש דרך אחת להעלות command מותאם לתפריט החדש. מכינים native DLL שמממש את ממשק IExplorerCommand, ומכריזים על COM server ועל הרחבת context menu ב-manifest של חבילת MSIX.7

<!-- Package manifest (excerpt) -->
<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>

Type של ItemType יכול לציין extension מסוים, או * (כל הקבצים), Directory (תיקיות), או Directory\Background (רקע תיקייה). מתאימים את ה-DLL לארכיטקטורת Explorer (64-bit / ARM64).7

IExplorerCommand עצמו הוא ממשק שקיים מאז עידן Windows 7; מממשים כותרת (GetTitle), אייקון (GetIcon), מצב מופעל / מושבת / מוסתר (GetState) והפעלה (Invoke). המתודות נקראות מ-UI thread, לכן גישה למשאבי רשת אסורה, ומתודות בניית תפריט צריכות לחזור במהירות. עבודה כבדה אחרי Invoke.147

מבנה ה-manifest של רישום התפריט החדשהכרזת COM server ב-MSIX manifest ממפה CLSID ל-DLL, והכרזת הרחבת context menu קושרת את היעד והמימוש עם ItemType ו-Verb, כך ש-command מותאם מופיע בתפריט החדשממפה CLSID ל-DLLמציין עם ItemType ו-VerbMSIX manifestהכרזת COM serverהכרזת הרחבת תפריטDLL מימוש IExplorerCommandהפקודה מופיעה בתפריט החדשהיעד הוא extension, כל הקבצים וכדומה

איור 11: שתי הכרזות ב-manifest קושרות את DLL המימוש ליעד, והפקודה מופיעה בתפריט החדש.

5.3. האפשרות לאפליקציה לא-ארוזה — קבלת identity בלבד עם sparse package

פתח המילוט כש«האפליקציה שלנו לא יכולה להיות מופצת אלא כ-MSI; MSIX בלתי אפשרי» הוא sparse package (MSIX עם external location). חותמים MSIX קטן שהוא manifest בלבד, בלי גוף אפליקציה, ורושמים אותו בסוף ה-installer הקיים. האפליקציה רוכשת אז package identity, ורישום ה-manifest למעלה (= הופעה בתפריט החדש) הופך לאפשרי. זמין מ-Windows 10 version 2004 ואילך, והחבילה זקוקה לחתימה בתעודה מהימנה במחשב היעד.8

זרימת קבלת identity עם sparse packageאחרי שה-installer הקיים מניח את גוף האפליקציה, רישום sparse package של manifest בלבד עם external location נותן לאפליקציה package identity ומאפשר רישום manifest לתפריט החדשinstaller קייםהנחת גוף האפליקציהsparse packagemanifest בלבד, בלי גוףרישום עם external locationרכישת package identityרישום לתפריט החדש הופך לאפשרינדרשת חתימה מהימנה

איור 12: רושמים sparse package שאינה מכילה גוף, עם external location, והאפליקציה מקבלת package identity.

היתרון הגדול ביותר הוא שאין צורך להחליף את ה-installer; זו התשובה המציאותית לאפליקציה שכבר יש לה נכס installer MSI/EXE. להשוואה עם מעבר מלא ל-MSIX ראו גם “איך בוחרים שיטת הפצה ליישום Windows - MSI/‏MSIX/‏ClickOnce/‏xcopy/עדכון עצמי”.

5.4. איך verbs של association מופיעים בתפריט החדש

נקודה שקל לטעות בה: ה-associations של פרקים 2 ו-3 (ProgID ו-verb) עדיין חיים בתפריט החדש. ה-verb כברירת מחדל ב-double-click, «פתיחה», ומועמדי «פתיחה באמצעות» נפתרים מה-association ומוצגים בראש התפריט החדש. לכן אם כל מה שרוצים הוא «להיות מסוגלים לפתוח עם האפליקציה הזו», Windows 11 אינו דורש עבודה נוספת. מצד שני, association אינו הרחבת תפריט כלל-תכליתית, ולכן אם רוצים command מותאם שרירותי בשכבה הראשונה של התפריט החדש צריך IExplorerCommand ועוד identity — זה פיצול התפקידים.7

פיצול התפקידים בין associations לתפריט החדשassociation של ProgID ו-verb עדיין משמש בתפריט החדש לפתרון ה-verb כברירת מחדל, פתיחה ופתיחה באמצעות, ומוצג למעלה; העלאת command מותאם שרירותי לשכבה הראשונה של התפריט החדש דורשת IExplorerCommand ו-identityassociation (ProgID + verb)פתרון ברירת מחדל / פתיחהראש התפריט החדשאין עבודה נוספת ב-Win11command מותאםIExplorerCommand+identityהשכבה הראשונה של התפריט החדש

איור 13: associations עדיין מטפלים בפתרון משפחת «פתיחה» בתפריט החדש; רק command מותאם דורש IExplorerCommand ועוד identity.

6. טבלת החלטה מעשית — איזו משלוש האפשרויות לבחור

מארגנים את מה שיש עד כאן לבחירה משולשת מעשית.

מה רוצים להשיג האמצעי המומלץ איך זה נראה ב-Windows 11 העבודה והעלות הנדרשות
(א) להפעיל את האפליקציה הפנימית ב-double-click או «פתיחה» association + static verb (רישום Registry בלבד) משולב ב-«פתיחה» וב-«פתיחה באמצעות» בתפריט החדש רישום Registry של ה-installer בלבד. אין DLL, אין דרישת חתימה נוספת
(ב) להעלות command מותאם לקובץ/תיקייה שנבחרו לתפריט החדש מימוש IExplorerCommand + רישום MSIX manifest. אפליקציה לא-ארוזה מקבלת identity ב-sparse package השכבה הראשונה של התפריט החדש (כמה commands נאספות ל-flyout בשם האפליקציה) native C++ DLL + package identity + code signing
(ג) להמשיך להשתמש ב-IContextMenu קלאסי קיים להשאיר כרגע כמו שהוא (לא לבחור לפיתוח חדש) צד התפריט הישן בלבד, תחת «הצג אפשרויות נוספות» (Shift+F10) תחזוקת build 64-bit ורישום COM. לתכנן מעבר מאוחר ל-(ב)

שתי נקודות שיפוט. ראשית, אל תביאו (ב) או (ג) לדרישה ש-(א) ממלאת. ברגע שכותבים shell extension לוקחים אחריות על יציבות Explorer. שנית, (ג) הוא רק «לא שבור»; כחוויית משתמש הוא נשאר צעד גרוע יותר. ככל ש-command בשימוש תכוף יותר בפעילות יומיומית, כך גדול יותר התשואה על מעבר ל-(ב).

איך לבחור בין שלוש האפשרויותאם כל מה שרוצים הוא הפעלה ב-double-click או פתיחה, association ו-static verb מספיקים; להעלאת command מותאם לתפריט החדש משתמשים ב-IExplorerCommand ורישום MSIX manifest; אם אי אפשר לעבור ל-MSIX מעניקים identity ב-sparse package; משאירים IContextMenu קלאסי קיים בצד התפריט הישן כרגעכןלאכןכןלאלאהאם פתיחה מספיקה?association + static verbמותאם בתפריט החדש?אפשר לעבור ל-MSIX?IExplorerCommand+MSIXidentity ב-sparse packageלהשאיר קלאסי כרגעצד התפריט הישן בלבדאין DLL, סיכון קטן

איור 14: בוחרים בין static verb, IExplorerCommand ועוד identity, והשארת הקלאסי, לפי הדרישה.

7. פריסה ורישום בפועל — installer, sparse package, cleanup

7.1. HKLM או HKCU

מתאימים לצורה של ה-installer. לכל המשתמשים (מונח תחת Program Files, הרשאות Administrator) הוא HKLM\Software\Classes; התקנה לפי משתמש (בלי elevation) היא HKCU\Software\Classes. מערבבים אותם ומייצרים פניות מסוג «א יכול לפתוח אבל ב לא יכול». ל-shell extension שכולל רישום CLSID, Reg-Free COM — שמסיר את הצורך ברישום ה-Registry עצמו — הוא אפשרות תקפה לשימוש COM בתוך האפליקציה, אבל אי אפשר ליישם אותו על shell extension ש-Explorer טוען, ולכן צריך את הרישום הישיר (“מה זה Reg-Free COM - שימוש ב-COM בלי רישום”).

7.2. אחרי שינוי, מודיעים — SHChangeNotify

אחרי שרושמים, משנים או מוחקים association, מודיעים על אירוע SHCNE_ASSOCCHANGED עם SHChangeNotify. מדלגים על זה ו-Explorer עלול לא להבחין בשינוי עד restart.110

// Call once after changing associations, e.g. from an installer custom action
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);

7.3. רישום והסרת sparse package

רישום והסרת sparse package הם עבודת ה-installer. רושמים אחרי הנחת הקבצים; מסירים לפני מחיקת הקבצים.8

# At install time: after placing the files, register the install folder as the external location
Add-AppxPackage -Path "C:\Program Files\KomuraSoft\KomuraReport.identity.msix" `
                -ExternalLocation "C:\Program Files\KomuraSoft"

# At uninstall time: remove the package registration before deleting the files
Remove-AppxPackage <package full name>

נקודה לשמירה: Add-AppxPackage רושם עבור המשתמש שהריץ אותו. אם קוראים לו מ-custom action של MSI לכל-המחשב, שרצה תחת LocalSystem, זה אינו מעניק identity למשתמש שהתקין, ולכן מגדירים אותו לרוץ תחת impersonation למשתמש. גם אז, הרישום תחת impersonation הוא רק עבור המשתמש שהריץ את ההתקנה הזו. במחשב שמשמש כמה משתמשים, למשתמשים אחרים ולמשתמשים שנוצרים אחר כך אין package identity, והפקודה אינה מופיעה בתפריט החדש. כדי שכל משתמש ישתמש בה, מספקים מנגנון כמו בדיקת רישום החבילה שלכם בהפעלה הראשונה ורישום אם חסר (רישום לפי משתמש), וכוללים הסרה מכל משתמש שיש לו רישום בתוכנית ה-uninstall. השתקפות רישום manifest עשויה גם לדרוש restart של Explorer (או sign-out).7

סדר רישום והסרת sparse packageבזמן התקנה רושמים את ה-sparse package אחרי הנחת הקבצים; בזמן uninstall מסירים את הרישום לפני מחיקת הקבצים; שמים לב שהרישום תקף רק למשתמש שהריץ אותוהתקנההנחת הקבציםרישום ה-sparse packageuninstallהסרת רישום החבילהמחיקת הקבציםהרישום תקף רק למשתמש הרץ

איור 15: רושמים אחרי הנחת הקבצים, מסירים לפני מחיקת הקבצים, ושמים לב שהרישום הוא לפי המשתמש הרץ.

7.4. Cleanup ב-uninstall — מה למחוק ומה להשאיר

ל-cleanup ב-uninstall יש קו מדריך רשמי ברור.1

  • מוחקים: את כל מפתח ה-ProgID הפנימי, רישום Capabilities/RegisteredApplications, רישום CLSID של ה-shell extension, ה-sparse package (Remove-AppxPackage).
  • משאירים: ערך ברירת המחדל של מפתח ה-extension (.kmrpt). ההמלצה הרשמית היא לא למחוק אותו גם אם הוא עדיין מצביע על ה-ProgID הפנימי. לשפוט אחרי התקנה אם אפליקציה אחרת לקחה את ה-default קשה, ו-Windows פשוט מתעלמת מ-ProgID ערך-ברירת-מחדל שאינו רשום, כך שהשארתו אינה מזיקה באמת.
  • קוראים ל-SHChangeNotify(SHCNE_ASSOCCHANGED) גם בסוף ה-cleanup.

רוב בעיות «הסרנו התקנה ועדיין מופיעות שאריות בתפריט» הן דליפה בעיצוב ה-cleanup הזה.

עיצוב ה-cleanup ב-uninstallב-uninstall מוחקים את מפתח ה-ProgID הפנימי, רישום CLSID וה-sparse package; משאירים את ערך ברירת המחדל של מפתח ה-extension כי ProgID לא-רשום מתעלמים ממנו; מודיעים על השינוי ב-SHChangeNotify בסוף ה-cleanupuninstallמוחקיםמשאיריםרישום ProgID ו-CLSIDsparse packageערך ברירת המחדל של מפתח ה-extensionמתעלמים מ-ProgID לא-רשוםמודיעים ב-SHChangeNotify בסוף

איור 16: מוחקים את הרישום הפנימי, משאירים את ערך ברירת המחדל של מפתח ה-extension, ומודיעים על השינוי בסוף ה-cleanup.

8. Troubleshooting — חסר, כפול, כבד

8.1. זה לא מופיע בתפריט

מבודדים בסדר הזה.

  1. באיזה תפריט מסתכלים: רישום בסגנון קלאסי מופיע רק בצד התפריט הישן תחת Shift+F10. בודקים קודם את שניהם.
  2. Bitness: DLL של shell extension של 32-bit בלבד אינו נטען ל-Explorer 64-bit (סעיף 4.3).
  3. יעד רישום: HKLM/HKCU, ערבוב Wow6432Node. מאשרים את המפתח בפועל עם reg query.
  4. רישום חבילה: לתפריט החדש מאשרים נוכחות עם Get-AppxPackage, אמון בתעודת החתימה, ונתיב -ExternalLocation, ואז עושים restart ל-Explorer.7
  5. הודעה שפוספסה: אם שכחו SHChangeNotify, אפשר לדעת לפי האם restart של Explorer גורם לזה להיכנס לתוקף.
סדר בידוד כשזה לא מופיע בתפריטמתחילים באישור באיזה תפריט מסתכלים, ואז מבודדים bitness של DLL, יעד רישום ה-Registry, רישום חבילה וחתימה, ו-SHChangeNotify שפוספס, בסדר הזהמאשרים איזה תפריט, ישן או חדשמאשרים bitness של DLLמאשרים יעד רישום HKLM ו-HKCUמאשרים רישום חבילה וחתימהמזהים הודעה שפוספסה ב-restart

איור 17: כש«לא מופיע», מבודדים בסדר התפריט שמסתכלים עליו, bitness, יעד הרישום, רישום החבילה, הודעה שפוספסה.

8.2. זה מופיע פעמיים, או לא נעלם

סיבות טיפוסיות הן דו-קיום של רישום Registry קלאסי ורישום manifest, דליפה ב-cleanup של uninstall (סעיף 7.4), או שאריות ProgID של גרסה ישנה. אם זה מופיע פעמיים רק בתפריט הישן, חושבים על שאריות; אם זה מופיע גם בישן וגם בחדש, חושבים על דו-קיום.

בידוד תצוגה כפולהפעמיים רק בתפריט הישן מצביע על שאריות כמו דליפת cleanup או ProgID ישן; פעמיים גם בישן וגם בחדש מצביע על דו-קיום של רישום Registry קלאסי ורישום manifestהתפריט הישן בלבדישן וחדשבאיזה זה מופיע פעמיים?שאריותדו-קיוםדליפת cleanup או ProgID ישן שנשאררישום Registry קלאסי שמתקיים יחד עם הרישום החדש

איור 18: פעמיים רק בתפריט הישן מצביע על שאריות; פעמיים גם בישן וגם בחדש מצביע על דו-קיום.

8.3. Explorer כבד או קורס

כשלחיצה ימנית איטית, או שתיקייה מסוימת קורסת, קודם ממפים את ה-shell extensions המותקנים. מפרטים extensions שאינם של Microsoft בכלי כמו ShellExView של NirSoft, משביתים זמנית את החשודות, ומאתרים את ה-DLL בחיפוש בינארי. ב-crash, «Faulting module» ב-Event Viewer הוא גם רמז. אם ה-extension הפנימי היה הסיבה, חושדים ב-I/O סינכרוני או בגישה לרשת בנתיב בניית התפריט (סעיפים 4.2 ו-5.2).

זיהוי ה-DLL האשם כשזה כבד או קורסמפרטים shell extensions שאינם של Microsoft ב-ShellExView, משביתים זמנית את החשודות ומאתרים את ה-DLL בחיפוש בינארי; ב-crash מודול התקלה ב-Event Viewer הוא גם רמזמיפוי shell extensionsפירוט אלה שאינם של Microsoftהשבתה זמנית וחיפוש בינאריזיהוי ה-DLL האשםב-crashבדיקת מודול התקלה

איור 19: משביתים זמנית extensions שאינם של Microsoft ומאתרים בחיפוש בינארי; ב-crash משתמשים גם ב-Event Viewer.

8.4. Windows Sandbox נוח לאימות

אימות shell integration מבוסס על אישור «התקנה בסביבה נקייה → הפעלה → uninstall → אפס שאריות». הנוח כאן הוא Windows Sandbox (Pro/Enterprise/Education): כל הפעלה מביאה Windows חדש חד-פעמי תוך שניות, כך שאפשר להריץ בדיקות רישום ו-cleanup של ה-installer כמה שרוצים. סוגרים והכול נעלם, ולכן זה מתאים גם לחקירת שאריות Registry.15

9. סיכום

  • File association הוא המבנה התלת-שלבי «מפתח extension → ProgID → verb», ו-HKCR היא merged view של Classes של HKLM/HKCU. מציינים את יעד הכתיבה במפורש, ותמיד עוטפים %1 במירכאות.
  • Default app מיועד להיבחר בידי המשתמש ואי אפשר לשנות אותו מתוכנית. עבודת ה-installer היא להירשם נכון כמועמד.
  • Shell extension קלאסי הוא in-process COM DLL שנטען ל-Explorer. crash או delay מתפשטים לכלל; נדרשות 64-bit; managed code אינו נתמך; המימוש ב-native C++ הוא הכלל.
  • ב-Windows 11 ה-context menu התפצל לשניים. העלאת command מותאם לתפריט החדש דורשת IExplorerCommand ועוד MSIX manifest; IContextMenu קלאסי עובר לצד «הצג אפשרויות נוספות».
  • לאפליקציה שאינה יכולה לעבור ל-MSIX, קבלת identity עם sparse package (MSIX עם external location) היא התשובה המציאותית.
  • אם כל מה שרוצים הוא «לפתוח עם האפליקציה הזו», association ו-static verb עדיין מספיקים. להתחיל מהאמצעי הפשוט ביותר הוא גם המדריך הרשמי.
  • אחרי רישום, שינוי או מחיקה מודיעים ב-SHChangeNotify; ב-uninstall מוחקים את ה-ProgID ומשאירים את ערך ברירת המחדל של מפתח ה-extension. Windows Sandbox נוח לאימות.

אם החלפת מחשב ל-Windows 11 גרמה לכם להבחין ש«התפריט הוסתר», קודם מאשרים איזה מבין (א), (ב) ו-(ג) בטבלת ההחלטה של פרק 6 זה. אמורים להיות מסוגלים להעריך את היקף העבודה במקום.

מאמרים קשורים

תחומי ייעוץ קשורים

KomuraSoft LLC מטפלת בתכנון ובמימוש של file association, context menu ו-shell extension לאפליקציות עסקיות; כיוון ל-context menu החדש של Windows 11 (מעבר ל-IExplorerCommand, הכנסת sparse package); סקירת רישום ו-cleanup של installer קיים; וחקירת הסיבה ל-Explorer כבד או קורס. אפשר להתחיל מההחלטה מה לעשות עם «התפריט שהוסתר תחת הצג אפשרויות נוספות».

קישורי עיון

  1. Microsoft Learn, File Types. על מבנה מפתח extension שמצביע על ProgID; OpenWithProgIds; פיצול הרישום בין HKLM/HKCU\Software\Classes; קריאה ל-SHChangeNotify(SHCNE_ASSOCCHANGED) אחרי שינוי association; ומחיקת ה-ProgID ב-uninstall תוך השארת ערך ברירת המחדל של מפתח ה-extension. ↩ ↩2 ↩3 ↩4 ↩5

  2. Microsoft Learn, HKEY_CLASSES_ROOT Key. על כך ש-HKEY_CLASSES_ROOT היא merged view של HKLM\Software\Classes ו-HKCU\Software\Classes; הגדרות צד המשתמש קודמות לאלה של צד המחשב; וכללי הניתוב בכתיבה. ↩ ↩2

  3. Microsoft Learn, Windows app defaults platform. על כך ששינוי default app מיועד להיעשות רק דרך Settings של המערכת; נתוני הגדרות משתמש הם obfuscated ומוגנים בכתיבה ב-filter driver (UCPD.sys); שינוי מבוסס-Registry אינו נתמך; ושימוש ב-Group Policy / MDM בסביבה מנוהלת. ↩ ↩2

  4. Microsoft Learn, Working with Shell Extensions. על סוגי shell extension handler; על כך ש-extension הוא in-process COM DLL שנטען ל-Explorer (ול-processes שמארחים את ה-shell) כך ש-crash או hang מתפשטים ל-Explorer כולו; רישום עם ThreadingModel=Apartment; ובחינת חלופה פשוטה יותר לפני shell extension. ↩ ↩2 ↩3

  5. Microsoft Learn, Guidance for Implementing In-Process Extensions. על כך ש-Microsoft אינה ממליצה ואינה תומכת במימוש shell extension in-process ב-managed code; סיבות כוללות התנגשות גרסאות CLR, reentrancy ו-object lifetime לא-דטרמיניסטי; ו-managed code מקובל ל-out-of-process extension (preview handler, או הפעלה מ-shell\verb\command). ↩ ↩2 ↩3

  6. Windows Developer Blog, Extending the Context Menu and Share Dialog in Windows 11. על עיצוב ה-context menu החדש של Windows 11; הרחבה דרך IExplorerCommand ועוד identity של אפליקציה; מיקום «פתיחה» ו-«פתיחה באמצעות» למעלה; איסוף כמה commands ל-flyout בשם האפליקציה; וטעינת הרחבות IContextMenu קלאסיות כתפריט Windows 10 תחת «הצג אפשרויות נוספות» (Shift+F10). ↩ ↩2 ↩3

  7. Microsoft Learn, Add a File Explorer context menu command to a packaged desktop app. על כך שרישום ב-context menu החדש של Windows 11 נעשה עם מימוש IExplorerCommand ועוד windows.comServer ועוד הכרזת manifest desktop4:FileExplorerContextMenus; ItemType יכול לציין *, Directory או Directory\Background; התאמת ארכיטקטורת DLL; שמירה על מתודות בניית תפריט מהירות; כיסוי אפליקציה לא-ארוזה ב-sparse package; שלפעמים נדרש restart של Explorer כדי שהרישום ייכנס לתוקף; וש-file association אינו הרחבת תפריט כלל-תכליתית. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8

  8. Microsoft Learn, Grant package identity by packaging with external location. על קבלת package identity ברישום חבילת external location (sparse package) בלי לשנות את ה-installer הקיים; זמינות מ-Windows 10 version 2004 ואילך; ותכונות Windows שדורשות identity (רישום context menu, notifications וכדומה) הופכות לשימושיות. ↩ ↩2 ↩3

  9. Microsoft Learn, Choosing a Static or Dynamic Shortcut Menu Method. על בחירת שיטת ה-static verb הפשוטה ביותר שעומדת בדרישות; IContextMenu החזק ביותר אבל גם המורכב ביותר ומסווג לצד הלא-מומלץ; ו-IExplorerCommand/IExplorerCommandState השיטה המומלצת. ↩ ↩2

  10. Microsoft Learn, SHChangeNotify function. על איך להרים את אירוע SHCNE_ASSOCCHANGED שמודיע למערכת על שינוי file association, ועל השימוש בו כדי שה-shell תבחין בשינוי. ↩ ↩2

  11. Microsoft Learn, Application Registration. על כך שרישום קובץ הפעלה דרך מפתח-המשנה App Paths מומלץ; תפקיד מפתח-המשנה Applications; רישום verbs דרך SystemFileAssociations; ועדיפות ה-ProgID והמידע הקשור כש-default app משתנה. ↩

  12. Microsoft Learn, Creating Shortcut Menu Handlers. על איך לרשום static verb; הסדר שבו נקבע ה-verb כברירת מחדל (ערך ברירת מחדל → verb ראשון → Open → Open With); display names של verbs תקניים מסופקים בידי ה-OS; extended verbs דרך Extended; association לפקודות DDE הוא Deprecated; ואזהרות WOW64 redirect בסביבת 64-bit. ↩ ↩2 ↩3

  13. Microsoft Learn, Verbs and File Associations. על כך ש-verb הוא פעולה שמשמשת גם את ShellExecuteEx; רכיבים במחרוזת command שיכולים להכיל רווח צריכים להיות עטופים במירכאות, ו-“%1” תמיד נכתב במירכאות; ורישום הליך ברירת מחדל תחת HKCR\Applications. ↩

  14. Microsoft Learn, IExplorerCommand interface. על הרכב המתודות GetTitle, GetIcon, GetState, Invoke, EnumSubCommands וכדומה; המתודות נקראות ב-UI thread ולכן אסור להן לתקשר עם משאבי רשת; וזמינות מ-Windows Vista ואילך. ↩

  15. Microsoft Learn, Windows Sandbox. על היכולת להפעיל סביבת Windows מבודדת חד-פעמית תוך שניות, כל השינויים נזרקים כשסוגרים אותה, התאמתה לבדיקות תוכנה ואימות installer, וזמינות ב-Pro/Enterprise/Education. ↩

מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.

העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.

המאמר קשור ישירות לשירותים הבאים.

שאלות נפוצות

שאלות נפוצות בפניות בנושא המאמר.

למה ב-Windows 11 פריט ה-context menu של האפליקציה שלנו מופיע רק תחת "הצג אפשרויות נוספות"?
כי ב-Windows 11 ה-context menu של File Explorer מפוצל לשתי שכבות. בתפריט החדש יכולים להופיע רק commands שמממשים IExplorerCommand ורשומים ב-manifest של חבילת MSIX (כלומר יש להם package identity). shell extension קלאסי שמבוסס על IContextMenu עובר לתפריט הישן שנפתח מ-"הצג אפשרויות נוספות" (Shift+F10). ה-extension עצמו לא שבור, ולכן הוא ממשיך לעבוד כרגע. אם רוצים אותו בתפריט החדש צריך לעבור ל-IExplorerCommand, ולתת identity דרך MSIX או sparse package.
האם ה-installer יכול לקבוע את האפליקציה שלנו כ-default app לקובץ (זו שנפתחת ב-double-click)?
לא. בחירת default app מיועדת למשתמש, ו-Windows לא תומכת בשינוי default app מחוץ ל-Settings של המערכת. נתוני UserChoice ששומרים את הבחירה לכל משתמש הם obfuscated, ו-filter driver (UCPD.sys) חוסם כתיבה מאפליקציות. מה ש-installer יכול לעשות הוא לרשום ProgID ו-verbs, להוסיף את עצמו ל-OpenWithProgIds כדי להופיע כמועמד תחת "פתיחה באמצעות", ולהפנות את המשתמש לדף הגדרות default apps. המימוש הנכון אינו לגנוב את ה-default, אלא להיות מוכן להיבחר.
מותר לכתוב shell extension ב-managed code כמו C#?
Microsoft כתבה במפורש ש-shell extension in-process (context menu handler, icon handler וכדומה) ב-managed code אינו מומלץ ואינו נתמך. ה-extension נטען ל-Explorer ול-process של כל אפליקציה שפותחת common file dialog, ולכן התנגשות גרסאות CLR, reentrancy ו-object lifetime לא-דטרמיניסטי הופכים את אפליקציית ה-host ללא יציבה. הכלל הוא native C++. EXE רגיל שמופעל מ-verb command, או out-of-process extension כמו preview handler שרץ ב-process נפרד, תקינים ב-managed code.
מה זה sparse package (MSIX עם external location)?
חבילת MSIX קטנה בלי קבצי אפליקציה, רק manifest (identity). לאפליקציה שכבר מותקנת עם installer קיים (MSI, Inno Setup וכדומה) רושמים אותה עם Add-AppxPackage -ExternalLocation שמצביע לתיקיית ההתקנה. האפליקציה מקבלת package identity ויכולה להשתמש בתכונות שדורשות identity, כמו רישום ל-context menu החדש של Windows 11 ו-toast notifications. זמין מ-Windows 10 version 2004 ואילך, והחבילה צריכה code signing מהימן במחשב היעד. זו האפשרות המציאותית כשרוצים את התפריט החדש בלי להעביר את כל ה-deployment ל-MSIX.
מה עושים כשפריט context menu מופיע פעמיים, או לא נעלם?
קודם מבודדים לפי התפריט: החדש או הישן (הצג אפשרויות נוספות). כפילות טיפוסית היא רישום Registry קלאסי ורישום MSIX manifest שחיים יחד, או ProgID / CLSID של extension שנשארו אחרי uninstall. אחרי שינוי file association חשדו גם ב-SHChangeNotify(SHCNE_ASSOCCHANGED) שפוספס; מיד אחרי רישום חבילה חשדו ב-Explorer שלא עשה restart. אם זה עדיין לא נפתר, משביתים זמנית extensions שאינם של Microsoft ב-ShellExView ומאתרים את ה-DLL בחיפוש בינארי.

פרופיל הכותב

עמוד היכרות עם כותב המאמר.

Go Komura

מנהל KomuraSoft LLC

מתמחה בפיתוח תוכנה עבור Windows, ייעוץ טכני וחקירת תקלות, בעיקר בפרויקטים עם מערכות קיימות ובאגים שקשה לשחזר.

קישורים ציבוריים

חזרה לבלוג