WinRT הוא COM — IInspectable, .winmd, language projections, ולמה WinUI עדיין יושב על חוזה בינארי

· עודכן בתאריך: · · Windows, WinRT, COM, WinUI, Windows App SDK, פיתוח Windows

היסטוריית עדכונים (גרסה ראשונה, פורסמה בתאריך 29 Aug 2026)
פרסום ראשון

רוצים להוסיף יכולת Windows חדשה לאפליקציית WPF או WinForms. אבל קריאה ל-WinRT picker זורקת exception. אחר כך קוראים על WinUI, ונראה כאילו צריך לבנות מחדש את ה-UI. הבלבול הזה מתבהר ברגע ש-מפרידים איך WinRT עובד מבחירת UI framework.

נקודת ההתחלה של המאמר הזה היא ש-גם WinRT יושב על החוזה הבינארי של COM. Microsoft עצמה קובעת במפורש ש-“The Windows Runtime is based on COM”.1 טבלת Excel שמוטמעת ב-Word שראינו ב-מאמר הקודם על OLE objects, ו-WinRT ו-WinUI של היום, יש להם אותו IUnknown בשורש.

כאן קודם נועצים את שלושת האלמנטים שמרכיבים את WinRT, אחר כך מסתכלים על מה לשים לב כשמשתמשים בו מאפליקציית desktop, ולבסוף שוקלים איך לטפל בנכסים קיימים. קהל היעד הוא מפתחים עם ניסיון ב-COM או בפיתוח desktop ל-Windows, סביבת הבסיס היא Windows 10/11 ו-.NET 6 ואילך (C#) או C++17 (C++/WinRT), ו-רמת הקושי היא בינונית.

1. קודם המסקנות

יש שלוש מסקנות להחזיק.

  • WinRT אינו managed runtime; זה ABI (חוזה בינארי) שנבנה על COM. לחוזה הזה נוספו .winmd, שנושא מידע טיפוסים, ו-language projections, שמאפשרים לכל שפה לקרוא לו באופן טבעי.1234
  • רוב WinRT APIs ניתנים לשימוש מאפליקציות WPF, WinForms ו-Win32 קיימות. אבל שלושה תנאים מוקדמים צריך לבדוק: העברת HWND, package identity, ואתחול thread.5678
  • מעבר UI מלא ל-WinUI ושימוש בררני ב-WinRT APIs הם החלטות נפרדות. גם WinUI יושב על ה-ABI של WinRT, ונכסי COM/ActiveX קיימים ו-WinRT יכולים להתקיים יחד על אותו יסוד.921

מכאן ואילך, קריאה בסדר הבא מקלה ביותר לעקוב אחרי הקשרים.

מה רוצים לדעת איפה לקרוא
למה אפשר לומר “WinRT הוא COM”? פרקים 2–3: מה הוא חולק עם COM, ו-IInspectable
למה C# ו-C++ יכולים לקרוא לו כל כך טבעית? פרקים 4–5: .winmd ו-language projections
מה חייבים לבדוק כדי להשתמש בו מאפליקציה קיימת? פרק 6: HWND, package identity, apartments
האם לעבור ל-WinUI? פרקים 7–9: מה יושב מתחת ל-WinUI, החלטת המעבר, תנאי הרישום להתראות

מפת הידע למטה היא לסקירת איך האלמנטים קשורים. אם מעדיפים לקרוא את ההסבר קודם, ממשיכים מפרק 2 בסדר.

ב-diagram, solid line מציינת relation שתמיד מתקיים ו-dashed line מציינת relation מותנה (התנאים מופיעים בהסבר של כל relation ב-detail page). הרשימה המלאה של ה-relations (סה”כ 20, כולל evidence ו-certainty) וההגדרות של ה-concepts המרכזיים נמצאות ב-detail page של ה-knowledge map (ביפנית). Data: JSON-LD / Turtle

2. גם ל-OLE וגם ל-WinRT יש אותו חוזה בינארי בשורש

בבלוג הזה עד כה עקבנו אחרי עולם ה-COM הקלאסי: פילוסופיית התכנון של COM, מודל ה-threading של STA/MTA, טיפול ב-ActiveX/OCX, ו-OLE compound documents. כל אלה הן טכנולוגיות משנות ה-90.

WinRT, לעומת זאת, הוא יסוד ה-API שהוצג עם Windows 8 (2012). היום הוא מספק toast notifications, Share, Bluetooth, OCR ועוד כ-APIs במרחבי השמות Windows.*, והוא גם מה ש-WinUI ו-Windows App SDK עומדים עליו.10 ישן וחדש נראים כמו עולמות נפרדים לגמרי, אבל במהות הם רציפים.

מה שהם חולקים הוא ההבטחה לקרוא דרך ממשקים

רכיבי COM ומחלקות WinRT שניהם חושפים את הפונקציונליות שלהם דרך ממשקים. בהשוואת ממשסי הבסיס, הקשר נראה כך.1

COM קלאסי WinRT
הבסיס של כל ממשק הוא IUnknown הבסיס של כל ממשק הוא IInspectable, שהבסיס שלו הוא IUnknown

כלומר, WinRT לא החליף את COM במנגנון לא קשור; זו שכבה נוספת שמונחת מעל IUnknown. Reference counting, QueryInterface ו-HRESULT כולם ממשיכים לחיות כמו שהיו.

התיעוד של C++/WinRT גם קורא ל-WinRT APIs “evolution of COM” ומסביר שהם מתוכננים לצריכה דרך language projections כ-APIs מבוססי COM.211

ייחוס COM הקלאסי ו-WinRTעל היסוד המשותף של החוזה הבינארי של COM העשוי מ-IUnknown ומ-vtables עומדים, זה לצד זה, עולם ה-COM הקלאסי של OLE ו-ActiveX משנות ה-90 ועולם WinRT מ-2012 ואילך עם WinUI ו-Windows App SDK מעליו; השניים אינם הדדיים-בלעדיים אלא רציפיםחוזה בינארי של COM (IUnknown, vtable)COM קלאסי (OLE, ActiveX, COM מותאם)WinRT (IInspectable, .winmd)WinUI / Windows App SDKיכולים להתקיים יחד על אותו יסוד

איור 1: COM קלאסי ו-WinRT אינם עולמות נפרדים אלא שני דורות, ישן וחדש, על אותו חוזה בינארי.

לכן המאמר הזה אינו רק “מבוא ל-API חדש”. זה מאמר שמאשר איפה ידע ה-COM שכבר יש לכם חל בפיתוח Windows ב-2026.

3. IInspectable מעל IUnknown

היסוד שלא השתנה ושלוש השיטות שנוספו

המפרט הרשמי של מערכת הטיפוסים של WinRT קובע ש-כל ממשק WinRT דורש במרומז IInspectable, ו-IInspectable דורש IUnknown. מה ש-IUnknown מגדיר הוא, כמו תמיד, שלוש השיטות QueryInterface, AddRef ו-Release.12

מעל זה, IInspectable מוסיף את שלוש השיטות הבאות.13

שיטה תפקיד
GetIids מחזירה את רשימת ה-IIDs של הממשקים שהאובייקט הזה מממש
GetRuntimeClassName מחזירה את שם טיפוס WinRT המלא (כמו Windows.Storage.StorageFile) כ-HSTRING
GetTrustLevel מחזירה את רמת ה-trust של האובייקט
IInspectable שיושב על IUnknownכל ממשק WinRT דורש IInspectable, ו-IInspectable דורש IUnknown. IUnknown מספק QueryInterface, AddRef ו-Release; IInspectable מספק GetIids, GetRuntimeClassName ו-GetTrustLevel; ושיטות כל ממשק WinRT יושבות מעליהןIUnknown (QI, AddRef, Release)IInspectable (GetIids, שם טיפוס, trust level)שיטות כל ממשק WinRT

איור 2: אובייקט WinRT עורם את שלוש השיטות של IInspectable על שלוש השיטות של IUnknown, וה-APIs הבודדים יושבים מעל זה.

משם טיפוס אפשר לחפש את ההגדרות של שיטות, מאפיינים ואירועים

מה שחשוב אינו מספר השיטות שנוספו אלא היכולת לחבר שם טיפוס ל-metadata.

ב-COM קלאסי, הדרך הסטנדרטית ללמוד את זהות האובייקט בזמן ריצה הייתה “לדעת את ה-IID ולשאול דרך QueryInterface”. לשפות סקריפט היה נתיב נפרד, IDispatch.

ב-WinRT, שם הטיפוס שמתקבל מ-GetRuntimeClassName ניתן לפתרון מול metadata של .winmd שמכוסה בפרק הבא. משם מקבלים את ההגדרה המלאה של השיטות, המאפיינים והאירועים שלו. המפרט עצמו אומר שהיכולת לקבל שם טיפוס WinRT שניתן לפתרון דרך metadata “מאפשרת language projection”.12

מ-GetRuntimeClassName עד language projectionכשהקורא קורא ל-GetRuntimeClassName על אובייקט, שם טיפוס WinRT המלא חוזר; פתרון שם הטיפוס הזה מול Windows Metadata נותן את ההגדרה המלאה של הטיפוס, וזה מה שמאפשר projection לכל שפהאובייקט WinRTשם טיפוס (GetRuntimeClassName)פותרים את הגדרת הטיפוס ב-.winmdlanguage projection הופך לאפשרי

איור 3: “שם הטיפוס זמין בזמן ריצה, ושם הטיפוס מוביל ל-metadata” הוא לב המנגנון של WinRT.

מפרידים “ירושה” מ”דורש” לממשקים מוגדרים-משתמש

יש גם הבדל שמי שמורגל ב-COM צריך לשים לב אליו. למערכת הטיפוסים של WinRT אין ירושה בין ממשקים מוגדרים-משתמש. גזירה כמו IFileSystemBindData2 : IFileSystemBindData של COM קלאסי נעדרת במכוון; במקום זאת זה מבוטא בהצהרה “ממשק A דורש ממשק B”.121

זה עניין נפרד משרשרת ה-ABI הבסיסית IUnknownIInspectable שראינו עד כאן. הבסיס הזה נשאר כיסוד של כל ממשק WinRT.

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

4. מה .winmd פתר — גיהינום ה-binding של מידע טיפוסים

הקושי של COM קלאסי היה ב”איך מפיצים מידע טיפוסים”

בפרקטיקה של COM קלאסי, יותר מאמץ הלך ל-איך להפיץ מידע טיפוסים מאשר למימוש הממשקים עצמם.

צרכן נתיב למסירת מידע טיפוסים
C++ כותבים את החוזה ב-IDL, מייצרים headers ו-proxy/stub עם MIDL
VB6, סקריפטים מפיצים type library (TLB)
.NET בונים interop assembly נפרד

ל-TLB יש מגבלות טיפוס מכוונות אוטומציה: חלק מהמידע שאפשר לכתוב ב-IDL לא נכנס ל-TLB. וכי לכל שפה יש נתיב משלה, כשאחד מהם מתיישן מגיעים לאי-התאמות טיפוס. כיסינו את הצרות האלה ב-מאמר על type libraries ו-dscom וב-מאמר על תאימות לאחור של ממשקי DLL ו-COM.

.winmd הוא החוזה המשותף שכל שפה קוראת

התשובה של WinRT היא Windows Metadata (.winmd). ה-APIs מתוארים כ-metadata קריא למכונה, וכלים ו-language projections קוראים אותו כדי לייצר projection לכל שפה.3

Windows משווק את ה-metadata לכל WinRT API שמסופק על ידי המערכת וגם מספק APIs שפותרים namespaces וטיפוסים בזמן ריצה. Windows SDK מכיל עותק לזמן קומפילציה. גם צדדים שלישיים יכולים לקחת חלק ב-language projection דרך אותו מנגנון כמו APIs המערכת על ידי צירוף .winmd לרכיב WinRT שלהם.3

מה שהשתנה כאן הוא הצורה שבה מידע טיפוסים מופץ. במקום TLBs, headers ו-interop assemblies שמפוזרים לפי שפה, .winmd אחד נקרא כעת על ידי ה-projections של כל שפה.

עם זאת, IDL לא הפך למיותר. כשכותבים רכיב WinRT, עדיין מתארים את החוזה ב-IDL (MIDL 3.0, שעודכן ל-WinRT), ומהדר MIDL מייצר את ה-.winmd.14

צינור הייצור של רכיב WinRTהחוזה של רכיב WinRT עדיין נכתב ב-IDL, כלומר MIDL 3.0, ומהדר MIDL מקמפל אותו ל-.winmd. מה שמופץ הוא ה-.winmd הזה, וכלי ה-projection של כל שפה, כמו cppwinrt.exe או cswinrt.exe, קורא אותו כדי לייצר את ה-projection. מה שהוחלף אינו IDL אלא הצורה שבה מידע טיפוסים מופץכותבים את החוזה (IDL, MIDL 3.0)מהדר MIDL.winmd (מידע טיפוסים מופץ)מייצרים את ה-projection של כל שפה

איור 4: נקודת הכניסה של החוזה (IDL) נשארת בשירות; נקודת היציאה, מידע הטיפוסים המופץ, אוחדה ל-.winmd.

אותו פורמט קובץ כמו .NET, אבל לא managed runtime

הפורמט הפיזי של .winmd משתמש ב-מפרט ECMA-335, אותו מפרט כמו CLR assembly. הכללים לאילו שילובי נתונים תקפים שונים מאלה של CLR assemblies, עם זאת. לשאול את הפורמט ולהזדקק ל-CLR כדי לרוץ הם שני דברים שונים.3

כאן צריך לקרוא APIs של המערכת ורכיבי צד שלישי בנפרד.

נושא הקשר בין ה-.winmd למימוש
WinRT APIs שמסופקים על ידי המערכת ה-.winmd הוא metadata טהור בלי קוד להרצה. המימוש חי ב-DLLs native של מערכת ההפעלה, וה-CLR אינו נחוץ כדי להריץ אותו
רכיבי WinRT של צד שלישי ה-.winmd עשוי גם להכיל קוד מימוש. לרכיב מנוהל (שנכתב ב-C#) שמכיל MSIL, נחוץ .NET runtime המתאים כדי להריץ אותו

כי .winmd נראה כמו .NET assembly כשפותחים אותו בכלי, קל ליפול לתפיסה השגויה “WinRT = managed”. אבל התוכן של .winmd שמסופק על ידי המערכת הוא חוזה לממשקי COM.3

הפרדת .winmd מהמימוש.winmd הוא metadata ששואל את הפורמט הפיזי של ECMA-335; אלה שמסופקים על ידי המערכת הם חוזים בלי קוד להרצה, והמימוש של WinRT APIs שמסופקים על ידי המערכת חי ב-DLLs native של מערכת ההפעלה. בגלל ההפרדה הזו, גם אם .winmd נראה כמו .NET assembly, ה-CLR אינו נחוץ כדי להריץ את WinRT APIs של המערכת (ה-.winmd של רכיב מנוהל של צד שלישי מכיל MSIL ודורש את .NET runtime)מסופק על ידי המערכת: אין קודהגדרות טיפוס ממופות למימוש.winmd (חוזה, פורמט ECMA-335)DLL native של מערכת ההפעלה (מימוש)APIs של המערכת לא צריכים CLR

איור 5: ל-WinRT APIs שמסופקים על ידי המערכת ה-.winmd הוא החוזה והמימוש הוא DLL native של מערכת ההפעלה. הפורמט נראה כמו .NET, אבל ההרצה היא COM native.

מידע טיפוסים של COM קלאסי מול .winmdב-COM קלאסי נתיב מידע הטיפוסים פוצל לפי שפה, מ-IDL ל-headers של C++, מ-type libraries ל-VB6 ולסקריפטים, מ-interop assemblies ל-.NET, מה שגרם לאי-עקביות, ואילו ב-WinRT .winmd אחד נקרא במשותף על ידי ה-projections של כל שפהCOM קלאסי: נתיב אחד לכל שפהIDL ל-headers של C++TLB ל-VB6 ולסקריפטיםInterop assembly ל-.NET.winmd (metadata יחיד)נקרא במשותף על ידי ה-projection של כל שפה

איור 6: .winmd קיפל את הפיזור לפי שפה של מידע טיפוסים ל”קובץ metadata אחד שכולם קוראים”.

המגבלות לא נעלמו; הן עברו לציר של projectability

למי שמכיר COM קלאסי, במשפט: .winmd הוא “ה-type library, שנעשה מחדש”. חושבים עליו כתפקיד ש-TLB ניסה למלא, שתוכנן מחדש מההתחלה כמקור אמת יחיד שמשותף לכל השפות, מעל פורמט ECMA-335 המוכח, ומקומו מתבהר.

המגבלות לא נעלמו, עם זאת. מגבלות ה-TLB המכוונות אוטומציה הוחלפו במגבלות של מערכת הטיפוסים של WinRT עצמה, שהציר שלהן הוא “אפשר להקרין בבטחה לכל שפה”. היעדר ירושה בין ממשקים מוגדרים-משתמש, שנראה בפרק 3, הוא דוגמה אחת.12

כתוצאה, חוזה COM/IDL קיים לא בהכרח ניתן לשאת ל-WinRT כמו שהוא. ייתכן שצריך לתכנן מחדש את ה-API.

החלפת מגבלות type library במגבלות מערכת הטיפוסים של WinRTמגבלות הביטוי המכוונות אוטומציה של ה-TLB (type library) לא הוסרו על ידי .winmd אלא הוחלפו במגבלות של מערכת הטיפוסים של WinRT עצמה, שהציר שלהן הוא projection בטוח לכל שפה. דוגמה אחת היא היעדר ירושת ממשק מוגדר-משתמש; חוזה COM/IDL קיים לא בהכרח ניתן לשאת כמו שהוא, ויתכן שצריך לתכנן מחדש את ה-APIהוחלפו במגבלות TLB (מכוונות אוטומציה)מגבלות מערכת הטיפוסים של WinRT (ציר projectability)דוגמה: אין ירושה מוגדרת-משתמשחוזי COM קיימים עשויים להזדקק לתכנון מחדש

איור 7: מגבלות ה-TLB לא “נעלמו”; הן הוחלפו במגבלות שונות שהציר שלהן הוא projectability לכל שפה.

5. C++/WinRT ו-C#/WinRT הם projections, לא “wrappers”

מראים את החוזה המשותף לכל שפה בצורה הטבעית שלה

ברגע שיש חוזה משותף בצורה של .winmd, אפשר לייצר את התצוגה לכל שפה אוטומטית בכלים. זה language projection. הוא חושף WinRT APIs בניב של כל שפה ומסתיר את פרטי COM, ומספק חוויית תכנות שמרגישה טבעית לשפה הזו.411

ה-projections ש-Microsoft תומכת בהם כעת הם השניים הבאים.4

Projection מה הוא מייצר מאפיינים
C++/WinRT cppwinrt.exe מייצר headers של projection ב-C++ מ-.winmd projection מבוסס קבצי header ב-C++17 סטנדרטי. אין צורך בהרחבות שפה כמו אלה של C++/CX; היורש של C++/CX ו-WRL11
C#/WinRT (CsWinRT) cswinrt.exe מייצר קוד C# מ-.winmd והופך אותו ל-interop assembly ה-projection ל-.NET. toolchain עצמאי מה-runtime1516

ההיסטוריה בצד C# קצת מבלבלת, אז נפרק אותה. עד .NET Core 3.x, ל-.NET runtime הייתה תמיכה מובנית בצריכת WinRT/winmd. ב-.NET 5 התמיכה המובנית הזו הוסרה, והתפקיד עבר ל-C#/WinRT.16 WinRT APIs לא הפכו לבלתי שמישים; איפה מטפלים ב-projection השתנה.

היום, כשמציינים TFM כמו net8.0-windows10.0.19041.0 ב-C#, assemblies ה-projection של Windows SDK מקושרים אוטומטית.10

ייצור projections לכל שפה מ-.winmdכש-cppwinrt.exe קורא את ה-.winmd היחיד, הוא מייצר headers של projection ב-C++17; כש-cswinrt.exe קורא אותו, הוא מייצר interop assembly ב-C#; ו-WinRT APIs חשופים בצורה שעוקבת אחרי הניב של כל שפה.winmd (חוזה ה-API)cppwinrt.exe ל-headers של C++17cswinrt.exe ל-interop assembly ב-C#קריא בניב C++קריא בניב C#

איור 8: projection אינו wrapper שנכתב ביד; כלים מייצרים אותו מכנית מהחוזה (.winmd).

גם אם הוא מיוצר, הקריאה עצמה היא עדיין COM

המאמר הזה אומר “projection” ולא “wrapper” כדי להדגיש שזה מנגנון שנגזר מכנית מ-metadata, לא שכבת תרגום שאנשים מתחזקים API-API. כל API שקיים ב-.winmd יכול להיות שמיש בכל שפה נתמכת מההתחלה.

ומאיזו שפה שקוראים, מה שקורה מתחת ל-projection הוא אותה קריאת COM. למשל, כשכותבים await picker.PickSingleFolderAsync() ב-C#, ה-projection מגשר את IAsyncOperation של WinRT לעולם ה-Task של .NET. גם אז, בצד ה-ABI, קריאות שיטות vtable ו-HRESULT הם מה שבשימוש. לכן שגיאות מופיעות בצורה של COM exceptions (HRESULT כמו 0x80070005).

השכבות מקוד C# עד WinRT API של מערכת ההפעלהקוד C# או C++ של האפליקציה מומר דרך language projection לקריאות ABI של WinRT, כלומר קריאות vtable על IInspectable, ומגיע ל-WinRT API שמממשת מערכת ההפעלה. ה-projection רק מסתיר את פרטי COM; הקריאה עצמה היא COMקוד האפליקציה (C#, C++)language projectionABI של WinRT (vtable של IInspectable)מימוש מערכת ההפעלה של WinRT API

איור 9: מה שה-projection מסתיר הוא ה”פרטים” של COM, לא COM עצמו.

ידיעת המבנה הזה מאפשרת לפצל תקלות לשתי שכבות. אי-התאמת גרסה בקוד שנוצר או הגדרת TFM חסרה היא שכבת ה-projection; HRESULTs, apartments ו-reference counting הם שכבת ה-ABI. באחרונה, ניסיון פיתוח COM משרת אתכם ישירות.

6. איפה אפליקציות desktop נתקעות — HWND, identity, apartments

קודם מפרידים “יכולת להפנות ל-API” מ”התנאים שהוא יעבוד”

רוב WinRT APIs ניתנים לקריאה מ-WPF, WinForms ואפליקציות desktop של Win32.5 נקודת הכניסה לקריאה אליהם נבדלת בין C# ל-C++ כך.10

סביבה הגדרה ראשונית
C#/.NET 6 ואילך מגדירים TargetFramework ל-TFM עם גרסת Windows, כמו net8.0-windows10.0.19041.0
C++ מוסיפים את חבילת NuGet Microsoft.Windows.CppWinRT ומשתמשים ב-C++/WinRT עם C++17 ואילך

יכולת להפנות ל-APIs, עם זאת, אינה אומרת שכל API עובד כמו שהוא. בודקים את שלוש הנקודות הבאות. תנאי הרישום ל-toast notifications מכוסים בנפרד בפרק 9.

מה לבדוק תיקון עיקרי
האם צריך חלון להציג עליו? מעבירים HWND דרך COM interop שמתאים ל-UI
האם צריך package identity? נותנים identity עם MSIX או package שמצביע למיקום חיצוני
האם ה-thread מאותחל ל-WinRT? בקוד native, מאתחלים עם STA/MTA שצוין. ב-C# ה-runtime בדרך כלל מטפל בזה

נקודת תקיעה 1: מעבירים HWND ל-UI כמו pickers

חלק מה-pickers, דיאלוגים ו-Share UI מצפים ל-CoreWindow של UWP כמשטח להציג עליו. לאפליקציית desktop אין CoreWindow, כך ש-HWND של חלון הבעלים חייב להיות מועבר במפורש לפני שמראים את האובייקט.6

נקודת הכניסה שמשמשת ל-pickers ודומים היא ממשק COM שנקרא IInitializeWithWindow. הוא יורש מ-IUnknown ומספק חלון בעלים לאובייקטי WinRT שמשמשים באפליקציות desktop.17

ב-C#, קודם מקבלים את ה-HWND לפי ה-UI framework שבשימוש.186

חלון בעלים איך מקבלים את ה-HWND
Window של WinUI WinRT.Interop.WindowNative.GetWindowHandle
חלון WPF WindowInteropHelper
טופס WinForms מאפיין Handle של הטופס

אחר כך, מוסרים אותו ל-picker עם WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd), ורק אז מראים אותו. ב-C++/WinRT, מקבלים את האובייקט כ-as<IInitializeWithWindow>() ואז קוראים ל-Initialize(hwnd). אם מדלגים על האתחול, זה זורק או נכשל בשקט.1819

אתחול אובייקט WinRT מודרני ב-QueryInterface לממשק COM קלאסי: העובדה שהגשר הזה הוא הפרקטיקה הרשמית היא מקום נוסף שבו WinRT מראה שהוא COM.

ה-Share UI משתמש בממשק אחר. ל-DataTransferManager לא משתמשים ב-IInitializeWithWindow. משתמשים ב-IDataTransferManagerInterop הייעודי ומעבירים את ה-HWND ל-ShowShareUIForWindow.6

ה-pickers החדשים יותר של Windows App SDK הם נתיב נפרד נוסף. Microsoft.Windows.Storage.Pickers מקבל WindowId ב-constructor, כך שדפוס InitializeWithWindow מיותר. הם אינם APIs שאפשר להשתמש בהם עם הגדרת TFM לבדה, עם זאת. בנוסף להוספת Windows App SDK, אפליקציה unpackaged צריכה שה-runtime ייפרס ויאותחל במכונות היעד.19

צעדים להצגת picker מאפליקציית desktopאחרי שאפליקציית desktop יוצרת picker, קריאה ל-PickSingleFolderAsync כמו שהיא מסתיימת ב-exception או בכישלון שקט, כך שצריך קודם לקבל את HWND של חלון הבעלים ולהעביר אותו דרך Initialize של IInitializeWithWindow לפני שמראים את ה-pickerPicker (WinRT)אפליקציית desktopPicker (WinRT)אפליקציית desktopalt[מציגים בלי להעביר HWND][מעבירים את ה-HWND קודם]יוצריםPickSingleFolderAsyncexception או כישלון שקטמגדירים את ה-HWND דרך IInitializeWithWindowPickSingleFolderAsyncה-picker מוצג

איור 10: מילוי הפער של “אין CoreWindow ב-desktop” בהעברת HWND במפורש הוא הפרקטיקה הרשמית.

נקודת תקיעה 2: חלק מה-APIs דורשים package identity

חלק מ-WinRT APIs, כמו היסטוריית toast notification (ToastNotificationHistory), jump lists ו-share targets, עובדים רק באפליקציות שיש להן package identity (אפליקציות packaged). קריאה אליהם מאפליקציה unpackaged שמופצת עם installer מסורתי נכשלת.7

יש שני תיקונים. לארוז את האפליקציה ב-MSIX, או להשתמש ב-“package with external location” (מה שנקרא sparse package), שנותן identity תוך שמירה על ה-installer הקיים.20 אילו APIs דורשים identity אפשר לבדוק ברשימה הרשמית.7

שני נתיבים ל-APIs שדורשים package identityקריאה ל-WinRT API שדורש package identity מאפליקציה unpackaged נכשלת, לכן נותנים לאפליקציה package identity או באריזה ב-MSIX או ב-package שמצביע למיקום חיצוני, שנותן רק את המזהה תוך שמירה על ה-installer הקייםרוצים להשתמש ב-API שדורש identityאורזים ב-MSIXpackage שמצביע למיקום חיצונימקבלים package identityהיסטוריית התראות וכו' עובדים

איור 11: התיקון ל-APIs שדורשים identity הוא בחירה משתיים: “MSIX” או “installer קיים + מתן מזהה”.

נקודת תקיעה 3: בודקים אתחול thread ו-STA/MTA

thread שמטפל באובייקטי WinRT חייב להיות מאותחל ל-WinRT מראש. בקוד native, משתמשים ב-RoInitialize או ב-winrt::init_apartment ו-מציינים את מודל ה-concurrency, STA או MTA. באפליקציות C# WPF/WinForms, ה-runtime בדרך כלל מטפל באתחול.8

RoInitialize הוא נקודת הכניסה של דור WinRT בתוך אותו framework כמו CoInitializeEx של COM. התיעוד של CoInitialize עצמו מפנה לקרוא ל-RoInitialize או ל-Windows::Foundation::Initialize במקום כשמשתמשים ב-Windows Runtime.21

ה-UI thread של WPF/WinForms הוא STA; אובייקטי UI חייבים להיגע ב-UI thread; המתנות חוסמות על STA מזמינות deadlocks. החשיבה שכוסתה ב-מאמר STA/MTA עוברת ללא שינוי ל-WinRT APIs.

חלק מה-APIs אינם ניתנים לשימוש גם עם HWND ו-identity במקום

התיקונים עד כאן נוגעים ל-APIs שיש להם נקודת כניסה לשימוש desktop. APIs שתלויים ב-CoreWindow או ב-ApplicationView עצמם אינם ניתנים לשימוש מאפליקציות desktop. במקרה כזה, במקום להוסיף אתחול, מחפשים API חלופי.57

פיצול נקודות התקיעה כשקוראים ל-WinRT APIs מה-desktopקודם מאשרים שה-thread מאותחל ל-WinRT (בקוד native מציינים STA/MTA במפורש עם RoInitialize או דומה; ב-C# ה-runtime בדרך כלל מטפל). אחר כך, אם ה-WinRT API שרוצים לקרוא הוא UI שמניח CoreWindow, מעבירים HWND דרך IInitializeWithWindow או משתמשים ב-pickers החדשים; אם הוא דורש package identity, נותנים מזהה עם MSIX או package שמצביע למיקום חיצוני; APIs שתלויים ב-CoreWindow או ApplicationView עצמם אינם ניתנים לשימוש ב-desktop, לכן מחפשים API חלופי; ורוב ה-APIs הגדול שנשאר ניתן לקריאה כמו שהוא רק עם הגדרת TFM או C++/WinRTUIדורש identityתלוי ב-CoreWindow עצמוכל השאראתחול threadאיזה סוג API זה?native: מפורש; C#: בדרך כלל אוטומטיHWND או ה-pickers החדשיםMSIX או מתן מזההמחפשים API חלופיקריא כמו שהוא

איור 12: עם אתחול thread כהנחה (מפורש בקוד native, בדרך כלל מושאר ל-runtime ב-C#), נקודות התקיעה נופלות לשלוש משפחות, לכל אחת תיקון סטנדרטי.

התאמת אתחול thread בין COM ל-WinRTב-COM קלאסי thread מאותחל עם CoInitializeEx תוך ציון STA או MTA, ואילו ב-WinRT הוא מאותחל עם RoInitialize תוך ציון אותו מודל concurrency של STA או MTA. גם התיעוד של CoInitialize מפנה לקרוא ל-RoInitialize כשמשתמשים ב-WinRT, ומושג ה-apartment משותףCOM קלאסי: CoInitializeExApartment (STA / MTA)WinRT: RoInitializeUI thread הוא STA; זהירות מהמתנות

איור 13: שם ה-API של האתחול השתנה, אבל אותו מושג apartment ממשיך להיות בשימוש.

7. מה יושב מתחת ל-WinUI — פיתוח בגלוי לא משנה את החוזה

מעבר לפיתוח בגלוי אינו שינוי לחוזה הבינארי

בקיץ 2025, Microsoft הכריזה רשמית, כ-גישה מדורגת, על מדיניותה להעביר את פיתוח ה-mainline של WinUI לגלוי ב-GitHub. יש ארבעה שלבים: להגביר את תדירות העדכון של ה-mirror, לאפשר builds מקומיים, לקבל תרומות קהילה ברגע שיש בדיקות, ולבסוף להפוך את GitHub לבית הראשי של הפיתוח.22

נכון לפרסום המאמר הזה (סוף אוגוסט 2026), גם התיעוד הרשמי קובע במפורש ש-WinUI הוא “built in the open”. אפשר כעת לעקוב אחרי התקדמות ההנדסה היומיומית במאגר הציבורי.9

מפתחים שעקבו אחרי מעברי הדור מ-WinForms ל-WPF ל-UWP ל-WinUI מרגישים באופן טבעי “ה-framework משתנה שוב?”. אבל ה-UI frameworks שהמשיכו להשתנות והיסוד מתחתיהם צריכים להיראות בנפרד. Win32 ו-COM, ומ-2012 ה-ABI של WinRT, נשארו באותו מקום.

גם חלונות WinUI מגובים ב-HWND

WinUI הוא קבוצת WinRT APIs שמסופקת כחלק מ-Windows App SDK.923 ה-Microsoft.UI.Xaml.Window שלו הוא חלון שמגובה ב-HWND, שמחליף את מודל החלון מבוסס CoreWindow של דור UWP. גם ה-walkthrough הרשמי של interop מתחיל בקבלת window handle.24

כלומר, אפליקציית WinUI היא אפליקציית Win32 שבה עץ אובייקטים שעוקב אחרי חוזה IInspectable רץ מעל חלון HWND. הידע של QueryInterface, reference counting ו-apartments שנרכש דרך COM, והידע של HWNDs ולולאות הודעות שנרכש דרך Win32, שניהם חלים ישירות על troubleshooting של WinUI.

מעברי דור של UI frameworks והיסוד שלא השתנהה-UI frameworks WinForms, WPF, UWP XAML ו-WinUI עברו דור אחרי דור, אבל WinForms ו-WPF יושבים ישירות על יסוד Win32 ו-COM, בעוד UWP XAML ו-WinUI יושבים על אותו יסוד Win32 ו-COM דרך ה-ABI של WinRT. מה שהחליף דורות הוא השכבה העליונה; החוזה מתחת לא השתנהמעברי דור של UI frameworksWinForms, WPFUWP XAMLWinUI (נוכחי)ABI של WinRT (מ-2012)יסוד שלא השתנה (Win32 + COM)

איור 14: מה שעלה ונפל היה שכבת ה-framework. WinForms ו-WPF יושבים ישירות על Win32 + COM; UWP ו-WinUI יושבים על אותו יסוד דרך ה-ABI של WinRT.

השכבות שתומכות באפליקציית WinUIה-XAML והפקדים של WinUI מסופקים כחלק מ-Windows App SDK, רצים על ה-ABI של WinRT, כלומר חוזה IInspectable, ומתחת לזה יושב היסוד של COM ו-HWND של Win32. תחת מעברי הדור של UI frameworks, החוזה הבסיסי הזה לא השתנהWinUI (XAML, פקדים)Windows App SDKABI של WinRT (IInspectable)COM + Win32 (HWND)

איור 15: מתחת ל-WinUI יושב ה-ABI של WinRT, ומתחת לזה COM קלאסי ו-Win32. רק הערימה השתנתה; היסוד זהה.

ערבוב עם XAML Islands: בודקים את המגבלות של כל דור

האסטרטגיה של “לערבב רק פקדי WinUI במסכי WPF/WinForms קיימים” צריכה להיות מוערכת בנפרד לכל דור של XAML Islands.

דור המצב כשמשתמשים מ-WPF/WinForms
XAML Islands של דור UWP פקדי wrapper מ-Windows Community Toolkit קיימים. אבל גרסאות WPF/WinForms נעצרו בדור .NET Core 3.x ואינן נתמכות ב-.NET הנוכחי25
דור WinUI 3 אפשר לארח מ-WPF, WinForms ו-Win32 עם DesktopWindowXamlSource של Windows App SDK. אבל אין פקדי wrapper נוחים כמו אלה של דור UWP, ועומס המימוש והאימות של טיפול ב-hosting API ישירות נופל עליכם26

אם מתכננים ערבוב הדרגתי, בטוח יותר לא להניח ערבוב ברמת פקד לבדו. ארגון התוכנית סביב שימוש ברמת תכונה ב-WinRT APIs (פרק 6) או הפרדה ברמת מסך או process הוא הגישה הבריאה יותר נכון ל-2026.

8. השלכות לאפליקציות עסקיות — מעבר מלא ושימוש בררני הם בעיות נפרדות

“WinRT הוא עולם חדש ונפרד, כך שהשימוש בו פירושו לזרוק נכסים קיימים ולבנות מחדש.” התפיסה השגויה הזו, שפוגשים בפרויקטי Custom Software Development, מתמוססת ברגע שמפצלים אותה לשתיים.

נכסי COM קיימים ו-WinRT יכולים להתקיים יחד

אפליקציית WPF שמשתמשת באוטומציית COM של Excel, מארחת פקדי ActiveX, וקוראת לרכיבי COM פנימיים יכולה לקבל WinRT APIs שנוספים אליה. זה לא אקרובטיקה מיוחדת, כי משלבים תכונות על אותו יסוד COM.

ל-toast notifications, למשל, מגדירים את ה-TFM כדי להפנות ל-API ומספקים את תנאי הרישום להצגת התראות. כדי שהאחרון לא יישכח, פרק 9 מניח אותו נתיב-נתיב. ב-C++/WinRT, גם ממשקי WinRT וגם ממשקי COM קלאסיים ניתנים לטיפול באותם מנגנונים, winrt::com_ptr ו-winrt::implements.21

מעריכים חידוש UI והוספת תכונה בנפרד

“להעביר את ה-UI במלואו ל-WinUI?” ו”להשתמש ב-WinRT APIs רק איפה שצריך?” הם החלטות שנבדלות בקנה מידה, במשך ובסיכון.

מעבר UI מלא הוא בעיית בחירת framework שתלויה בנכסי מסכים, פקדי צד שלישי וארגון הפיתוח. כיסינו את ההחלטה הזו ב-בחירה בין WinForms, WPF ו-WinUI. שימוש בררני ב-WinRT APIs, לעומת זאת, הוא שיפור קטן שאפשר להתחיל באפליקציה קיימת היום.

לפרויקט שרק רוצה toast notifications, אין צורך להעריך מעבר UI מלא. ולהפך, אין צורך לעכב שימוש ב-WinRT APIs “כי לא עוברים ל-WinUI”.

מעבר מלא ושימוש בררני הם החלטות נפרדותמעבר UI מלא ל-WinUI הוא החלטת בחירת framework גדולה שתלויה בנכסי מסכים ובארגון, ואילו שימוש בררני ב-WinRT APIs הוא החלטה קטנה שאפשר להוסיף לאפליקציית WPF או WinForms קיימת היום עם הגדרת TFM או דומה; שוקלים את השתיים בנפרד בלי לבלבל אותןרוצים להשתמש בתכונות Windows חדשותמעבר UI מלא (החלטה גדולה)שימוש בררני ב-WinRT API (החלטה קטנה)תלוי בנכסי מסכים ובארגוןאפשר להוסיף לאפליקציה הקיימת היום

איור 16: יש שני כבישים ל”תכונות חדשות”, ובלבול ביניהם מעיף גם את ההערכה וגם את ההחלטה.

9. טבלת החלטה — גרסת WinRT של להשאיר, לעטוף, להחליף

בוחרים רק את השינויים שכל מצב צריך

כהרחבה של טבלת ההחלטה של ActiveX, הנה מדריך החלטה לפי מצב בצד WinRT.

מצב המלצה סיבה
רוצים להשתמש ב-WinRT APIs כמו toast, Share או Bluetooth מ-WPF/WinForms שימוש בררני דרך הגדרת TFM (או הוספת C++/WinRT) קריא היום בלי מעבר UI. אבל Share UI עובר ב-IDataTransferManagerInterop (פרק 6), ול-toast ראו את התוספת מתחת לטבלה106
exception או כישלון שקט ב-picker או בדיאלוג מעבירים HWND דרך IInitializeWithWindow. לקוד חדש, ה-pickers החדשים מבוססי WindowId (דורש הוספת Windows App SDK) הפרקטיקה הרשמית של מילוי התכנון מבוסס CoreWindow ב-HWND619
היסטוריית התראות, jump lists ודומים לא עובדים נותנים package identity עם MSIX או package שמצביע למיקום חיצוני APIs שדורשים identity מניחים אריזה720
נכסי COM/ActiveX/OLE קיימים לא זורקים. מחליטים להשאיר, לעטוף או להחליף לפי נכס לא הדדיים-בלעדיים עם WinRT; הם מתקיימים יחד על אותו יסוד (פרק 8)
UI לאפליקציית desktop חדשה מעריכים WinUI כמועמד ראשון (WPF עדיין נוכחי) פיתוח בגלוי הבהיר את כיוון ההשקעה. מתחת יושב ה-ABI של WinRT229
לערבב פקדי WinUI במסכי WPF/WinForms קיימים מעריכים בזהירות, תוך התחשבות בעומס המימוש של wrappers חסרים Islands של דור UWP נעצרו ב-.NET Core 3.x; דור WinUI 3 מציע רק את ה-hosting API2526
תוכנית שמניחה ש-“WinRT הסתיים יחד עם UWP” מתקנים את ההנחה WinRT הוא יסוד ה-API הנוכחי, קריא מה-desktop5

Toast notifications לא מופיעות עם הגדרת TFM לבדה

ההגדרה שהופכת את ה-API לקריא והרישום שהופך התראה למופיעה הם שני דברים שונים. כש”ה-build מצליח אבל אין התראה”, בודקים לא רק את קוד הקריאה אלא את הנתיב שבשימוש, את צורת האריזה, ואם ה-process elevated.

כשמשתמשים ב-ToastNotificationManager הקלאסי

לאפליקציה unpackaged בלי package identity, התנאי המוקדם הוא רישום shortcut בתפריט Start עם AppUserModelID (AUMID) מוקצה. בלעדיו, אי אפשר להציג toasts.27

אפליקציה שמאורזת ב-MSIX או דומה לא צריכה את הרישום הידני הזה, כי package identity מספק את ה-AUMID.

כשמשתמשים ב-AppNotificationManager של Windows App SDK

בנתיב המומלץ כעת הזה, הוספת Windows App SDK וקריאה ל-Register() בהפעלה נדרשות. מעבר לזה, התנאים מתפצלים לפי צורת אריזה.28

צורת אריזה מה לבדוק בנוסף
Unpackaged Windows App SDK runtime חייב להיות פרוס בכל מחשב יעד. Register() מבצע את רישום COM server
Packaged ב-MSIX הרישום האוטומטי של Register() לא עובד. מצהירים על COM activator ב-Package.appxmanifest

יתר על כן, בנתיב Windows App SDK, התראות מתהליך שהועלה ל-administrator אינן נתמכות. Show נכשל בשקט בלי לזרוק. באפליקציה שצריכה elevation, שוקלים להפריד התראות ל-process לא-elevated.28

פרטי המימוש סביב הרישום מכוסים ב-מדריך המימוש של system tray והתראות.

שני הנתיבים ל-toast notifications והרישום שכל אחד צריךכדי להציג toast notification מאפליקציית desktop, נתיב ToastNotificationManager הקלאסי מתפצל לפי אם האפליקציה packaged: אפליקציה unpackaged חייבת לעבור ברישום shortcut בתפריט Start עם AppUserModelID מוקצה, בעוד לאפליקציה packaged ה-package identity מספק את ה-AppUserModelID. בנתיב AppNotificationManager של Windows App SDK, בנוסף להוספת ה-SDK ולקריאה ל-Register בהפעלה, אפליקציה unpackaged חייבת לפרוס את ה-runtime בכל מחשב יעד ואפליקציה שמאורזת ב-MSIX חייבת להצהיר על COM activator במניפסט לפני שהיא יכולה להמשיך. נתיב Windows App SDK מתפצל עוד לפי אם ה-process elevated: התראות מתהליך שהועלה ל-administrator אינן נתמכות ו-Show נכשל בשקט בלי לזרוק, כך שבנתיב הזה התראה מוצגת רק ל-process לא-elevated, ואפליקציה שצריכה elevation מפרידה התראות ל-process לא-elevatedלאכןלאכןלאכןרוצים להציג toastקלאסי: ToastNotificationManagerWASDK: AppNotificationManagerPackaged?רושמים shortcut עם AUMIDIdentity מספק את ה-AUMIDמוסיפים את ה-SDK + Register()Packaged?פורסים את ה-runtimeמצהירים COM במניפסטההתראה מוצגתprocess מוגבה?לא נתמך: Show נכשל בשקטמפרידים התראות ל-process לא-elevated

איור 17: בכל נתיב, ההתראה מופיעה רק אחרי שתנאי המוקדם של צורת האריזה מסופקים. בנתיב Windows App SDK, גם עם כל תנאי המוקדם במקום, שום דבר לא מוצג מ-process elevated.

לבסוף, בחזרה להשאיר, לעטוף, להחליף

האם צריך תכונה חדשה, או רוצים לחדש את ה-UI עצמו? חזרה לשאלה הזו מאפשרת להחליט על שימוש ב-WinRT ועל החלפת נכסים קיימים בנפרד.

זרימת החלטה לאיך נכסים קיימים ו-WinRT מתאימים יחדהחל מאפליקציית desktop קיימת, זרימת החלטה מדורגת: אם אין צורך בתכונת Windows חדשה, משאירים; אם צריך תכונה, עוטפים בשימוש בררני ב-WinRT APIs (עם תשומת לב לנקודות התקיעה של פרק 6 של HWND, package identity ואתחול thread); ורק כשה-UI עצמו צריך חידוש, שוקלים להחליף ב-WinUIהמצב הנוכחי מספיקתכונה חדשהחידוש UIאפליקציית desktop קיימתמה נחוץ?להשאיר (לתחזק כמו שהוא)לעטוף (שימוש בררני ב-WinRT API)להחליף (להעריך WinUI)שים לב ל-HWND, identity, אתחול (פרק 6)

איור 18: אותו מבנה “להשאיר, לעטוף, להחליף” כמו טבלת ההחלטה של ActiveX חל ישירות בצד WinRT.

10. סיכום

WinRT אינו סביבת הרצה חדשה שמנותקת מ-COM. זה יסוד API שמשלב את החוזה הבינארי של COM עם metadata ו-projections לכל שפה.

אלמנט התפקיד שלו כפי שכוסה במאמר הזה
IUnknown ו-IInspectable בנוי על QueryInterface ו-reference counting, ומוסיף שלוש שיטות שמחזירות את שם הטיפוס ועוד
.winmd החוזה שמשותף לכל שפה, שממנו שם טיפוס מוביל להגדרה. שואל את פורמט ECMA-335, אבל אלה שמסופקים על ידי המערכת לא מכילים קוד להרצה
C++/WinRT ו-C#/WinRT מייצרים API לכל שפה מהחוזה. מאז .NET 5 ה-projection של C# הוא toolchain עצמאי מה-runtime

גם כשהמשטח הוא API טבעי ב-C# או ב-C++, מתחתיו קריאות COM דרך vtables ו-HRESULTs. לכן הבנת העברת HWND ב-desktop, package identity, ו-STA/MTA עם אתחול thread מקלה לבודד תקלות WinRT.

המאמץ להעביר את פיתוח ה-mainline של WinUI לגלוי הוכרז בקיץ 2025 כגישה מדורגת. נכון לפרסום המאמר הזה התיעוד הרשמי גם אומר “built in the open”, אבל החוזה מתחתיו, IUnknownIInspectable, לא השתנה.229

המסקנה לאפליקציות עסקיות זהה. נכסי COM/ActiveX קיימים ו-WinRT יכולים להתקיים יחד. לא לבלבל מעבר UI מלא עם שימוש בררני ב-WinRT APIs הוא מה שמגן על דיוק ההערכות וההחלטות.

ה-compound documents של שנות ה-90 שנראו במאמר OLE ו-WinUI של היום עומדים על אותו IUnknown. לדעת COM אינו רק “להיות בקיא ב-legacy”. זה להיות מסוגל לקרוא את היסוד של Windows של היום.

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

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

KomuraSoft LLC מטפלת בפיתוח רכיבי COM ובתחזוקה, שינוי והחלפה של אפליקציות עסקיות ל-Windows שכוללות נכסי COM. אפשר להתייעץ מהשלב שבו תוכן המאמר הזה הוא בדיוק הבעיה שעל הפרק: “רוצים toast notifications ו-pickers תוך הישארות על WPF”, “קריאה ל-WinRT API נעצרה ב-exception”, “רוצים להחליט אם לעבור ל-WinUI או להפיק את המרב מנכסים קיימים”.

קישורים

  1. Microsoft Learn, Author COM components with C++/WinRT. על כך שרכיבי COM ומחלקות WinRT שניהם חושפים פונקציונליות דרך ממשקים; ההצהרה המפורשת “The Windows Runtime is based on COM”; ממשקי COM קלאסיים יורשים מ-IUnknown וממשקי WinRT מ-IInspectable, שיורש מ-IUnknown; וגזירה בין ממשקים מוגדרים-משתמש כמו IFileSystemBindData2 : IFileSystemBindData היא תכונת COM קלאסית שנעדרת במכוון ממערכת הטיפוסים של WinRT.  2 3 4 5 6

  2. Microsoft Learn, Consume COM components with C++/WinRT. על תכנות ב-COM דרך ממשקים ולא אובייקטים; זה גם מחזיק מאחורי הקלעים של WinRT APIs, שהם “evolution of COM”; וטיפול ב-WinRT וב-COM קלאסי באותו סגנון עם ה-COM smart pointer winrt::com_ptr.  2 3 4

  3. Microsoft Learn, Windows Metadata (WinMD) files. על כך ש-WinRT APIs מתוארים ב-metadata קריא למכונה שנקרא .winmd, שמשמש כלים ו-language projections; Windows משווק את ה-metadata לכל WinRT API שמסופק על ידי המערכת ומספק APIs לפתרון; צדדים שלישיים יכולים לקחת חלק ב-language projection עם אותו פורמט; והפורמט הפיזי הוא מפרט ECMA-335 (אותו מפרט כמו CLR assemblies), עם קבצי WinMD שמסופקים על ידי המערכת כ-metadata טהור.  2 3 4 5

  4. Microsoft Learn, Windows Runtime (WinRT) language projections. על כך ש-language projections חושפים WinRT APIs בניב של כל שפה; .winmd מגדיר את WinRT APIs ו-projections קוראים אותו; ושני ה-projections ש-Microsoft תומכת בהם הם C++/WinRT (C++17 ואילך) ו-C#/WinRT (.NET).  2 3

  5. Microsoft Learn, WinRT APIs callable from a desktop app. על כך שרוב WinRT APIs שמישים מאפליקציות desktop של .NET ו-C++ native, עם מחלקות שתוכננו ספציפית ל-UWP, כמו CoreDispatcher, CoreWindow ו-ApplicationView, כחריגים.  2 3 4

  6. Microsoft Learn, Display WinRT UI objects that depend on CoreWindow. על כך שחלק מה-pickers, popups ודיאלוגים תלויים ב-CoreWindow; CoreWindow אינו נתמך באפליקציות desktop; מחלקות שמממשות IInitializeWithWindow (או את IDataTransferManagerInterop המקביל) מאפשרות להגדיר את HWND של חלון הבעלים לפני התצוגה; והצעדים ל-WinUI 3, WPF ו-WinForms בהתאמה.  2 3 4 5 6

  7. Microsoft Learn, WinRT APIs not supported in desktop apps. על שתי המשפחות של WinRT APIs שאינם שמישים באפליקציות desktop, אלה שתלויים בתכונות UI ייחודיות ל-UWP ואלה שדורשים package identity (כמו ToastNotificationHistory ו-JumpList), ועל כך שהאחרונים נתמכים רק באפליקציות שמאורזות ב-MSIX.  2 3 4 5

  8. Microsoft Learn, RoInitialize function (roapi.h). על כך ש-RoInitialize מאתחל את ה-thread הנוכחי ל-Windows Runtime עם מודל ה-concurrency שצוין (RO_INIT_SINGLETHREADED/RO_INIT_MULTITHREADED); כל thread שמפעיל ופועל על אובייקטי WinRT צריך אתחול מראש; וציון מתנגש על thread שכבר אותחל כ-MTA מסתיים ב-RPC_E_CHANGED_MODE.  2

  9. Microsoft Learn, WinUI 3. על כך ש-WinUI הוא ה-UI framework ה-native המומלץ לאפליקציות desktop חדשות ל-Windows, מסופק כחלק מ-Windows App SDK, רץ על Windows 10 גרסה 1809 ואילך, ונבנה בגלוי.  2 3 4 5

  10. Microsoft Learn, Call Windows Runtime APIs in desktop apps. על כך שציון TFM עם גרסת Windows (כמו net10.0-windows10.0.22621.0) ב-.NET 6 ואילך גורם לחבילת ה-targeting של Windows SDK להיות מקושרת כך שאפשר לקרוא ל-WinRT APIs, ועל כך ש-C++ משתמש ב-C++/WinRT עם חבילת NuGet Microsoft.Windows.CppWinRT ו-C++17 ואילך.  2 3 4

  11. Microsoft Learn, Introduction to C++/WinRT. על כך ש-C++/WinRT הוא language projection ב-C++17 סטנדרטי ומודרני לגמרי שממומש כספרייה מבוססת קבצי header; שהוא היורש המומלץ של C++/CX ו-WRL; WinRT מבוסס על COM APIs ומתוכנן לגשת דרך language projections; projections מסתירים את פרטי COM; ו-cppwinrt.exe מייצר headers של projection מ-.winmd.  2 3

  12. Microsoft Learn, The Windows Runtime (WinRT) type system. על כך שכל ממשק WinRT דורש במרומז IInspectable ו-IInspectable דורש IUnknown; IUnknown מגדיר QueryInterface, AddRef ו-Release; שלוש השיטות ש-IInspectable מוסיף, GetIids, GetRuntimeClassName ו-GetTrustLevel; GetRuntimeClassName מחזיר שם טיפוס שניתן לפתרון דרך metadata, מה שמאפשר language projection; וירושה בין ממשקים מוגדרים-משתמש נעדרת ממערכת הטיפוסים של WinRT ומבוטאת ב-requires במקום.  2 3 4

  13. Microsoft Learn, IInspectable interface (inspectable.h). על כך ש-IInspectable מספק פונקציונליות שנדרשת בכל מחלקת WinRT, יורש מ-IUnknown, ויש לו שלוש השיטות GetIids, GetRuntimeClassName ו-GetTrustLevel. 

  14. Microsoft Learn, Introduction to Microsoft Interface Definition Language 3.0. על כך ש-MIDL 3.0 הוא תחביר תמציתי ומודרני להצהרת טיפוסי WinRT, וחוזי WinRT עדיין נכתבים ב-IDL, עם מהדר MIDL שמייצר Windows Metadata (.winmd). 

  15. Microsoft Learn, C#/WinRT. על כך ש-cswinrt.exe, שנכלל בחבילת NuGet של C#/WinRT, מעבד .winmd כדי לייצר קוד C# ומקמפל אותו ל-interop assembly, וזה ממוקם באותו אופן כמו C++/WinRT שמייצר headers ל-C++. 

  16. Microsoft Learn, Built-in support for WinRT is removed from .NET. על כך שהתמיכה המובנית ב-Windows Runtime הוסרה מ-.NET ב-.NET 5 ועברה ל-toolchain של CsWinRT.  2

  17. Microsoft Learn, IInitializeWithWindow interface (shobjidl_core.h). על כך שזה הממשק לאספקת חלון בעלים לאובייקטי WinRT שמשמשים באפליקציות desktop, יורש מ-IUnknown, ויש לו שיטת Initialize(HWND). 

  18. Microsoft Learn, Use WinRT COM interop classes in .NET. על כך שחלק מאובייקטי WinRT, כמו file pickers ודיאלוגים, צריכים HWND לפני שהם עובדים באפליקציית desktop, ועל כך שמחלקות C# בטוחות-טיפוס WinRT.Interop.WindowNative ו-WinRT.Interop.InitializeWithWindow מאפשרות אתחול בלי קריאות QueryInterface שנכתבות ביד.  2

  19. Microsoft Learn, Tutorial: Open files and folders with pickers in WinUI. על כך ש-Windows.Storage.Pickers הקלאסי חייב להיות מאותחל עם HWND לפני תצוגה כשמשתמשים בו באפליקציית desktop (WinUI 3), אחרת זורק או נכשל בשקט; לאפליקציות desktop של WinUI 3 אין CoreWindow; וה-pickers החדשים של Windows App SDK (Microsoft.Windows.Storage.Pickers) מקבלים WindowId ב-constructor כך שדפוס InitializeWithWindow מיותר.  2 3

  20. Microsoft Learn, Features that require package identity. על כך שחלק מתכונות Windows ו-WinRT APIs דורשים package identity בזמן ריצה, ועל כך שאפשר לקבל identity לא רק בהפצה כחבילת MSIX אלא גם עם package שמצביע למיקום חיצוני.  2

  21. Microsoft Learn, CoInitialize function (objbase.h). על כך ש-CoInitialize מאתחל את ספריית COM כ-STA; מאפליקציות חדשות מצפים לקרוא ל-CoInitializeEx; ו-RoInitialize או Windows::Foundation::Initialize חייבים להיקרא במקום כשמשתמשים ב-Windows Runtime. 

  22. GitHub, WinUI: Now Developing in the Open (microsoft/microsoft-ui-xaml Discussion #10700). ההכרזה הרשמית בסוף יולי 2025. על הגישה המדורגת לפתיחת מאגר WinUI (הגברת תדירות עדכון ה-mirror, אפשרות builds מקומיים, קבלת תרומות קהילה ברגע שיש בדיקות, ולבסוף הפיכת GitHub לבית הראשי של הפיתוח).  2 3

  23. Microsoft Learn, Windows App SDK. על כך ש-Windows App SDK הוא קבוצת ספריות הפיתוח הנוכחית לאפליקציות Windows שכוללת WinUI. 

  24. Microsoft Learn, Walkthrough: WinUI 3 app with Win32 interop. על כך שמחלקת Window של WinUI הורחבה לתמוך בחלונות desktop, ועל כך ש-Window באפליקציית desktop של WinUI 3 מגובה ב-window handle של Win32 (HWND), כך שאפשר לקבל את ה-handle ולפעול עליו עם Win32 APIs. 

  25. Microsoft Learn, Host UWP XAML controls in desktop apps (UWP XAML Islands). על כך ש-XAML Islands של דור UWP הוא המנגנון לאירוח פקדי UWP XAML ב-WPF, WinForms ואפליקציות desktop ב-C++, ועל כך שהשימוש בו מ-WPF/WinForms מוגבל לאפליקציות שמכוונות ל-.NET Core 3.x, לא נתמך ב-.NET הנוכחי וב-.NET Framework.  2

  26. Microsoft Learn, DesktopWindowXamlSource Class (Microsoft.UI.Xaml.Hosting). על כך שזו מחלקת הליבה של XAML hosting API של Windows App SDK, שיכולה לארח פקדי WinUI בכל רכיב UI שמשויך ל-HWND, ושמישה מאפליקציות desktop שנבנו עם WPF, Windows Forms ו-Win32 (Windows API).  2

  27. Microsoft Learn, Quickstart: Sending a toast notification from the desktop. על כך ששליחת toast מאפליקציית desktop מניחה shortcut בתפריט Start עם System.AppUserModel.ID מוגדר, ועל כך שאותו AppUserModelID חייב להיות מועבר לקריאה ל-CreateToastNotifier, בלעדיו ה-toast לא מוצג. 

  28. Microsoft Learn, Use app notifications with a .NET app. על כך ששימוש ב-AppNotificationManager של Windows App SDK באפליקציית WPF/WinForms דורש קריאה ל-Register() אחרי רישום ה-handler של NotificationInvoked; Register() מבצע אוטומטית את רישום COM server שמפעיל את האפליקציה כשלוחצים על התראה, לאפליקציות unpackaged; והתנאים המוקדמים הם הוספת Windows App SDK והגדרת קריאות WinRT API.  2

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

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

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

שאלות נפוצות

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

האם WinRT הוא managed runtime כמו .NET?
לא. WinRT (Windows Runtime) אינו סביבת הרצה עם מכונה וירטואלית או garbage collector; זה ABI (חוזה בינארי) שנבנה על COM. התיעוד של Microsoft עצמה קובע במפורש ש-"The Windows Runtime is based on COM", וכל ממשק WinRT דורש IInspectable, שיורש מ-IUnknown. כל קריאה היא עדיין קריאת COM דרך vtable, שנשענת על reference counting (AddRef/Release) ועל QueryInterface. השם ".winmd" וההתאמה הקרובה ל-.NET מקלים לטעות ולחשוב ש-WinRT הוא סביבה מנוהלת, אבל .winmd הוא קובץ metadata ששואל את אותו פורמט פיזי כמו ECMA-335, וקבצי ה-.winmd שמסופקים על ידי המערכת לא מכילים קוד להרצה. הסיבה ש-C# יכול לקרוא לו כל כך טבעית היא ש-language projection מייצר תצוגת C# מה-metadata הזה.
שמעתי ש-UWP כבר לא מיינסטרים. יש עדיין טעם ללמוד WinRT?
כן. UWP, מודל האפליקציה, ו-WinRT, יסוד ה-API, הם שני דברים שונים. גם אחרי ש-UWP צומצם, רוב WinRT APIs ממשיכים להיות מסופקים בצורה שאפליקציות desktop של WPF, WinForms ו-Win32 יכולות לקרוא, והרבה מיכולות Windows של היום, כמו toast notifications, Share, Bluetooth ו-OCR, חשופות כ-WinRT APIs. מעל זה, WinUI (Windows App SDK), ה-UI framework ה-native ש-Microsoft ממליצה עליו כעת, בנוי על ה-ABI של WinRT. כלומר, המכונה של WinRT (IInspectable, .winmd, language projections) אינה שריד של UWP; היא היסוד עצמו של פיתוח אפליקציות Windows הנוכחי.
האם אפליקציית WPF או WinForms יכולה לקרוא ל-WinRT APIs?
כן. ב-.NET 6 ואילך, כל מה שצריך הוא להגדיר את TargetFramework של הפרויקט ל-TFM עם גרסת Windows, כמו net8.0-windows10.0.19041.0; אז assemblies ה-projection של Windows SDK מקושרים ואפשר לקרוא ל-WinRT APIs במרחבי השמות Windows.* ישירות מ-C#. ב-C++, מוסיפים את חבילת NuGet Microsoft.Windows.CppWinRT ומשתמשים ב-C++/WinRT עם C++17 ואילך. יש עם זאת שלוש נקודות תקיעה. ראשית, מחלקות UI שמניחות CoreWindow, כמו pickers ודיאלוגים, צריכות את HWND של חלון הבעלים שיועבר דרך IInitializeWithWindow לפני שהן מוצגות (ה-Share UI של DataTransferManager הוא החריג: במקום IInitializeWithWindow הוא משתמש ב-IDataTransferManagerInterop הייעודי, נתיב נפרד שמעביר את ה-HWND ל-ShowShareUIForWindow). שנית, חלק מה-APIs, כמו היסטוריית ההתראות ו-jump lists, דורשים package identity (אריזת MSIX או package עם מיקום חיצוני). שלישית, thread שמטפל באובייקטי WinRT חייב להיות מאותחל קודם. בקוד native, מציינים את מודל ה-concurrency של STA/MTA עם winrt::init_apartment או RoInitialize (באפליקציות C# WPF/WinForms ה-runtime בדרך כלל מטפל בזה). שימו לב ש-APIs שתלויים ב-CoreWindow או ב-ApplicationView עצמם אינם ניתנים לשימוש מאפליקציות desktop בכלל.
קריאה ל-picker כמו FolderPicker מאפליקציית desktop זורקת exception. למה?
כי חלק ממחלקות picker ודיאלוג של WinRT תוכננו להציג על UWP CoreWindow. לאפליקציית desktop אין CoreWindow, כך שצריך לומר במפורש את חלון הבעלים לפני שמראים את האובייקט. בפועל, קודם מקבלים את HWND של חלון הבעלים (WinRT.Interop.WindowNative.GetWindowHandle ל-WinUI Window, WindowInteropHelper ל-WPF, מאפיין Handle של הטופס ל-WinForms), ואז ב-C# מוסרים אותו ל-picker עם WinRT.Interop.InitializeWithWindow.Initialize. ב-C++/WinRT, עושים QueryInterface לאובייקט ל-IInitializeWithWindow (as<IInitializeWithWindow>()) וקוראים ל-Initialize(hwnd). בלי האתחול הזה, הקריאה זורקת או נכשלת בשקט. שימו לב שה-pickers החדשים יותר של Windows App SDK (Microsoft.Windows.Storage.Pickers) תוכננו מחדש לקבל WindowId ב-constructor, מה שהופך את דפוס האתחול הזה למיותר (אבל כי הם Windows App SDK APIs, הגדרת TFM לבדה אינה מספיקה: צריך להוסיף את ה-SDK, ולאפליקציה unpackaged צריך לפרוס ולאתחל את ה-runtime במכונות היעד).
שמעתי שפיתוח WinUI נפתח ב-GitHub. האם לזרוק אפליקציות WPF/WinForms קיימות?
אין צורך למהר. פתיחת פיתוח ה-mainline של WinUI היא הצהרת כיוון, ש-Microsoft משקיעה ברצינות ב-framework ה-native שלה, לא הודעת סיום ל-frameworks הקיימים. WPF ו-WinForms עדיין נתמכים כחלק מ-.NET היום. מפצלים את ההחלטה לשתיים. אחת היא אם להעביר את ה-UI framework, החלטה גדולה שתלויה בגודל נכסי המסכים, פקדי צד שלישי וארגון הפיתוח. השנייה היא אם להשתמש ב-WinRT APIs באופן בררני מהאפליקציה הקיימת, מה שאפשר להתחיל היום עם הגדרת TFM. שימו לב שה-TFM רק הופך את ה-APIs לקריאים; toast notifications, למשל, דורשים רישום בנוסף לקריאה (בנתיב הקלאסי, אפליקציה unpackaged חייבת לרשום shortcut עם AppUserModelID — אפליקציה packaged לא, כי package identity מספק את ה-AppUserModelID; בנתיב Windows App SDK, צריך להוסיף את ה-SDK — ולאפליקציה unpackaged גם להתקין את Windows App SDK runtime בכל מחשב יעד — ולקרוא ל-Register() של AppNotificationManager, בעוד לאפליקציה שמאורזת ב-MSIX הרישום האוטומטי של Register() לא עובד וצריך בנוסף להצהיר על COM activator ב-Package.appxmanifest). גם, בנתיב Windows App SDK התראות מתהליך שהועלה ל-administrator אינן נתמכות, ו-Show נכשל בשקט בלי לזרוק — אפליקציה שצריכה elevation צריכה לשקול להפריד התראות ל-process לא-elevated. גם אז, אם כל מה שרוצים הוא toast notifications, מעבר ל-WinUI מיותר. שימו לב שערבוב פקדי WinUI במסכי WPF/WinForms קיימים (XAML Islands) דורש הבחנה בין דורות. ה-Islands של דור UWP נעצרו ב-.NET Core 3.x ל-WPF/WinForms, ולדור WinUI 3 ה-XAML hosting API של Windows App SDK (DesktopWindowXamlSource) שמיש מ-WPF/WinForms, אבל אין פקדי wrapper נוחים ועומס המימוש כבד, כך שהערכה זהירה היא המסלול הבטוח כעת.

פרופיל הכותב

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

Go Komura

מנהל KomuraSoft LLC

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

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

חזרה לבלוג