WinRT هو COM ── IInspectable و.winmd وإسقاط اللغات، ولماذا لا يزال WinUI يركب عقداً ثنائياً
· آخر تحديث: · غو كومورا · Windows, WinRT, COM, WinUI, Windows App SDK, تطوير Windows
سجل التعديلات (النسخة الأولى، نُشرت في 29 Aug، 2026)
- النشر الأول
تريد إضافة وظائف Windows جديدة إلى تطبيق WPF أو WinForms. لكن استدعاء ملتقط WinRT يرمي استثناء. وقراءة حديث WinUI تجعلك تظنّ أنك تحتاج إعادة بناء الواجهة. يُرتَّب هذا الحيرة بـ فصل آلية WinRT عن اختيار إطار الواجهة.
نقطة انطلاق هذه المقالة أن WinRT أيضاً يقوم على العقد الثنائي لـ COM. تنصّ Microsoft نفسها على «The Windows Runtime is based on COM (Windows Runtime قائم على COM)».1 تضمين جدول Excel في Word الذي رأيناه في مقالة كائن OLE السابقة، وWinRT وWinUI الحاليان، في الجذر IUnknown نفسه.
هنا نمسك أوّلاً العناصر الثلاثة التي تكوّن WinRT، ثم نقاط الانتباه عند الاستخدام من تطبيق سطح مكتب، وأخيراً كيف نعامل الأصول القائمة. القرّاء المستهدفون مطوّرون بخبرة COM أو تطوير سطح مكتب Windows، البيئة المفترضة Windows 10/11 و.NET 6 فما بعده (C#) أو C++17 (C++/WinRT)، الصعوبة متوسّطة.
1. الخلاصة أوّلاً
ثلاث خلاصات تُمسَك.
- WinRT ليس وقت تشغيل مُداراً؛ بل ABI (عقد ثنائي) قائم على COM. أُضيف إلى ذلك العقد
.winmdالذي ينقل معلومات النوع، وإسقاط لغات للاستدعاء الطبيعي من كلّ لغة.1234 - معظم واجهات WinRT يمكن استخدامها من تطبيقات WPF وWinForms وWin32 القائمة. لكن يلزم تأكيد ثلاث فرضيات: تمرير HWND، وpackage identity، وتهيئة الخيط.5678
- الترحيل الشامل للواجهة إلى WinUI والاستخدام الجزئي لواجهات WinRT حكمان مختلفان. WinUI أيضاً يقوم على ABI الخاص بـ WinRT، ويمكن لأصول COM/ActiveX القائمة وWinRT التعايش على الأساس نفسه.921
فيما يلي يسهل تتبّع الصلة بهذا الترتيب.
| ما تريد معرفته | الفصل الذي تقرؤه |
|---|---|
| لماذا يمكن القول «WinRT هو COM» | الفصلان 2–3: المشترك مع COM وIInspectable |
| لماذا يمكن الاستدعاء الطبيعي من C# وC++ | الفصلان 4–5: .winmd وإسقاط اللغات |
| ما تؤكّده للاستخدام من تطبيق قائم | الفصل 6: HWND وpackage identity والشقّة |
| هل ينبغي الترحيل إلى WinUI | الفصول 7–9: أساس WinUI، حكم الترحيل، شروط تسجيل الإشعار |
خريطة المعرفة التالية لمراجعة علاقة العناصر. إن أردت القراءة أوّلاً فامضِ من الفصل 2 بالترتيب.
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 20، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
2. OLE وWinRT أيضاً، الجذر العقد الثنائي نفسه
تتبّع هذا المدوّنة حتّى الآن عالم COM الكلاسيكي: فكر تصميم COM، ونموذج خيوط STA/MTA، ومعاملة ActiveX/OCX، والمستند المركَّب OLE. هذه كلّها تقنيات من التسعينيات.
أمّا WinRT فأساس واجهة أُدخل في Windows 8 (2012). يقدّم حالياً إشعارات التوست والمشاركة وBluetooth وOCR كمجموعة واجهات في فضاء الأسماء Windows.*، ويصير أيضاً أساس WinUI وWindows App SDK.10 يبدوان عالمين قديم وجديد منفصلين تماماً، لكن الكيان متّصل.
المشترك الوعد بالاستدعاء عبر واجهة
مكوّن COM وصنف WinRT كلاهما يكشف الوظائف عبر واجهة. بمقارنة الواجهة الأساس تصير العلاقة كما يلي.1
| COM الكلاسيكي | WinRT |
|---|---|
أساس الواجهة IUnknown |
أساس الواجهة IInspectable. وأساسه IUnknown |
أي أن WinRT لم يُستبدل بآلية لا علاقة لها بـ COM، بل أُضيفت طبقة فوق IUnknown. عدّ المراجع وQueryInterface وHRESULT لا تزال حيّة كما هي.
تسمّي وثائق C++/WinRT أيضاً واجهات WinRT «تطوّراً لـ COM (an evolution of COM)»، وتشرح أنّها تصميم يستخدم واجهات قائمة على COM عبر إسقاط لغة.211
flowchart TB
accTitle: نسب COM الكلاسيكي وWinRT
accDescr: فوق أساس مشترك هو العقد الثنائي لـ COM عبر IUnknown وvtable يقوم عالم COM الكلاسيكي مثل OLE وActiveX من التسعينيات وعالم WinRT من 2012 فما بعده وWinUI وWindows App SDK فوقه جنباً إلى جنب، وليسا حصريين بل متّصلين
base["العقد الثنائي لـ COM (IUnknown وvtable)"]
base --> classic["COM الكلاسيكي (OLE وActiveX وCOM ذاتي)"]
base --> winrt["WinRT (IInspectable و.winmd)"]
winrt --> winui["WinUI / Windows App SDK"]
classic -.-> coexist["يمكن التعايش على الأساس نفسه"]
winrt -.-> coexist
الشكل 1: COM الكلاسيكي وWinRT ليسا عالمين منفصلين بل جيلين قديم وجديد فوق العقد الثنائي نفسه.
لذا ليست هذه المقالة مجرّد «تقديم واجهة جديدة». إنّها مقالة تتأكّد أين تسري معرفة COM التي تملكها أصلاً في تطوير Windows لعام 2026.
3. IInspectable فوق IUnknown
الأساس الذي لا يتغيّر والثلاث دوال المضافة
تحدّد مواصفة نظام أنواع WinRT الرسمية أن كلّ واجهة WinRT تتطلّب IInspectable ضمناً، وIInspectable يتطلّب IUnknown. ما يعرّفه IUnknown هو الدوال الثلاث التقليدية QueryInterface وAddRef وRelease.12
فوق ذلك يضيف IInspectable الدوال الثلاث التالية.13
| الدالّة | الدور |
|---|---|
GetIids |
تعيد قائمة IID للواجهات التي ينفّذها هذا الكائن |
GetRuntimeClassName |
تعيد اسم نوع WinRT مؤهَّلاً بالكامل (Windows.Storage.StorageFile وغيره) كـ HSTRING |
GetTrustLevel |
تعيد مستوى ثقة الكائن |
flowchart TB
accTitle: IInspectable يركب فوق IUnknown
accDescr: تتطلّب كلّ واجهة WinRT IInspectable، ويتطلّب IInspectable IUnknown. يقدّم IUnknown QueryInterface وAddRef وRelease، ويقدّم IInspectable GetIids وGetRuntimeClassName وGetTrustLevel، وتركب فوقها دوال كلّ واجهة WinRT
unk["IUnknown (QI وAddRef وRelease)"]
insp["IInspectable (GetIids واسم النوع ومستوى الثقة)"]
api["دوال كلّ واجهة WinRT"]
unk --> insp
insp --> api
الشكل 2: كائن WinRT يرصف ثلاث دوال IUnknown ثم ثلاث دوال IInspectable ثم الواجهات الفردية.
من اسم النوع يمكن جلب تعريفات الدوال والخاصّيات والأحداث
المهمّ ليس عدد الدوال المضافة بقدر إمكان ربط اسم النوع بالبيانات الوصفية.
في COM الكلاسيكي كانت الطريقة القياسية لمعرفة هوية الكائن وقت التشغيل «معرفة IID والسؤال بـ QueryInterface». للغات السكربت كان هناك مسار آخر IDispatch.
في WinRT يمكن حلّ اسم النوع الحاصل بـ GetRuntimeClassName بالبيانات الوصفية .winmd في الفصل التالي. ومنها تُؤخذ التعريفات الكاملة للدوال والخاصّيات والأحداث. تقول المواصفة نفسها إن إمكان الحصول على اسم نوع WinRT قابل للحلّ بالبيانات الوصفية هو ما «يمكّن إسقاط اللغة (enables language projection)».12
flowchart TB
accTitle: من GetRuntimeClassName إلى إسقاط اللغة
accDescr: عندما يستدعي الطرف المستدعي GetRuntimeClassName للكائن يُعاد اسم نوع WinRT مؤهَّل بالكامل، وحلّ ذلك الاسم بـ Windows Metadata يعطي التعريف الكامل للنوع، وهذا ما يمكّن الإسقاط إلى كلّ لغة
obj["كائن WinRT"] --> name["اسم النوع (GetRuntimeClassName)"]
name --> md["حلّ تعريف النوع بـ .winmd"]
md --> proj["يصير إسقاط اللغة ممكناً"]
الشكل 3: «أخذ اسم النوع وقت التشغيل وجلب البيانات الوصفية من اسم النوع» في مركز آلية WinRT.
افصل «الوراثة» و«الطلب» لواجهات يعرّفها المستخدم
هناك فرق ينتبه إليه من اعتاد COM. ليس في نظام أنواع WinRT وراثة بين واجهات يعرّفها المستخدم. الاشتقاق مثل IFileSystemBindData2 : IFileSystemBindData في COM الكلاسيكي غير موجود قصداً، ويُعبَّر بدلاً منه بتصريح «الواجهة A تتطلّب (requires) الواجهة B».121
هذا حديث مختلف عن سلسلة ABI الأساس IUnknown ← IInspectable التي رأيناها حتّى الآن. تبقى هذه الأساس أساس كلّ واجهة WinRT.
عُقد المستخدم أُميل إلى شكل أخلخ لا يعتمد على تخطيط وراثة vtable. وفي الوقت نفسه كيان الاستدعاء لا يزال عبر vtable. المهمّ عدم خلط فرق كتابة العقد بآلية الاستدعاء.
4. ما حلّه .winmd ── جحيم ربط معلومات النوع
صعوبة COM الكلاسيكي كانت في «طريقة توزيع معلومات النوع»
في عمل COM الكلاسيكي كان العبء أكبر في كيف تُوزَّع معلومات النوع منه في تنفيذ الواجهة نفسها.
| جهة الاستخدام | مسار إيصال معلومات النوع |
|---|---|
| C++ | اكتب العقد بـ IDL، وولّد الترويسة والوكيل/البديل بـ MIDL |
| VB6 والسكربت | وزّع مكتبة أنواع (TLB) |
| .NET | اصنع تجميع تشغيل بيني على حدة |
لـ TLB قيود أنواع أميل إلى الأتمتة، ومعلومات تُكتب في IDL ولا تدخل TLB. علاوة على ذلك، لأن المسار ينقسم لكلّ لغة، يقدُم أحدها فيؤدّي إلى عدم اتّساق النوع. هذا العناء كما عولج في مقالة مكتبة الأنواع وdscom ومقالة التوافق الخلفي لواجهات DLL وCOM.
.winmd عقد مشترك تقرؤه كلّ اللغات
جواب WinRT هو Windows Metadata (.winmd). تُوصف الواجهة بيانات وصفية قابلة للقراءة آلياً، وتقرأها الأدوات وإسقاطات اللغات لتوليد إسقاط نحو كلّ لغة.3
يضمّ Windows بيانات وصفية لكلّ واجهات WinRT المقدَّمة من النظام، ويقدّم أيضاً واجهات لحلّ فضاءات الأسماء والأنواع وقت التشغيل. يتضمّن Windows SDK نسخة لوقت الترجمة. يمكن لطرف ثالث أيضاً، بإرفاق .winmd بمكوّن WinRT الخاص به، المشاركة في إسقاط اللغة بالآلية نفسها لواجهات النظام.3
ما تغيّر هنا هو شكل معلومات النوع الموزَّعة. بدلاً من TLB والترويسة وتجميع التشغيل البيني المتفرّقة لكلّ لغة، صارت إسقاطات كلّ اللغات تقرأ .winmd واحداً.
لكن IDL لم يصر غير لازم. عند صنع مكوّن WinRT ما زلت تصف العقد بـ IDL (MIDL 3.0 المحدَّث نحو WinRT)، ويولّد مترجم MIDL .winmd.14
flowchart TB
accTitle: خطّ إنتاج صنع مكوّن WinRT
accDescr: ما زال عقد مكوّن WinRT يُوصف بـ IDL أي MIDL 3.0، ويترجمه مترجم MIDL إلى .winmd. الموزَّع هو هذا .winmd، وتقرؤه إسقاطات كلّ لغة مثل cppwinrt.exe وcswinrt.exe لتوليد الإسقاط. ما استُبدل ليس IDL بل شكل معلومات النوع الموزَّعة
idl2["وصف العقد (IDL وMIDL 3.0)"]
midl2["مترجم MIDL"]
winmd4[".winmd (معلومات النوع الموزَّعة)"]
proj3["توليد إسقاط كلّ لغة"]
idl2 --> midl2
midl2 --> winmd4
winmd4 --> proj3
الشكل 4: مدخل العقد (IDL) لا يزال قائماً، ومخرج معلومات النوع الموزَّعة وُحِّد في .winmd.
التنسيق نفسه لملفّ .NET لكنّه ليس وقت تشغيل مُداراً
يستخدم التنسيق المادي لـ .winmd مواصفة ECMA-335 نفسها لتجميعات CLR. لكن قواعد تركيب البيانات الصالحة تختلف عن تجميع CLR. استعارة التنسيق وضرورة CLR للتنفيذ أمران مختلفان.3
هنا يلزم القراءة بفصل واجهات النظام عن مكوّنات الطرف الثالث.
| الهدف | علاقة .winmd والتنفيذ |
|---|---|
| واجهات WinRT المقدَّمة من النظام | .winmd بيانات وصفية خالصة بلا شيفرة تنفيذ. التنفيذ في DLL أصلي لنظام التشغيل، ولا يلزم CLR للتنفيذ |
| مكوّن WinRT لطرف ثالث | قد يتضمّن .winmd شيفرة تنفيذ. لمكوّن مُدار (مكتوب بـ C#) يتضمّن MSIL يلزم وقت تشغيل .NET المقابل للتنفيذ |
فتح .winmd بأداة يجعله يبدو كتجميع .NET، فيسهل سوء الفهم «WinRT = مُدار». لكن محتوى .winmd المقدَّم من النظام عقد واجهات COM.3
flowchart TB
accTitle: فصل .winmd عن التنفيذ
accDescr: .winmd بيانات وصفية تستعير التنسيق المادي لـ ECMA-335، والمقدَّم من النظام عقد بلا شيفرة تنفيذ، وتنفيذ واجهات WinRT المقدَّمة من النظام في DLL أصلي لنظام التشغيل. بسبب هذا الفصل لا يلزم CLR لتنفيذ واجهات نظام WinRT حتّى إن بدا .winmd كتجميع .NET (و.winmd مكوّن طرف ثالث مُدار يتضمّن MSIL ويتطلّب وقت تشغيل .NET)
winmd3[".winmd (عقد وتنسيق ECMA-335)"]
impl["DLL أصلي لنظام التشغيل (التنفيذ)"]
winmd3 -.->|"المقدَّم من النظام بلا شيفرة"| note3["واجهات النظام لا تحتاج CLR"]
impl --> note3
winmd3 ---|"تعريف النوع والتنفيذ يتقابلان"| impl
الشكل 5: في واجهات WinRT المقدَّمة من النظام .winmd عقد والتنفيذ DLL أصلي لنظام التشغيل. التنسيق شبيه بـ .NET لكن التنفيذ COM أصلي.
flowchart TB
accTitle: مقابلة معلومات نوع COM الكلاسيكي و.winmd
accDescr: في COM الكلاسيكي انقسم مسار معلومات النوع لكلّ لغة، من IDL إلى ترويسة C++، ومن مكتبة الأنواع إلى VB6 والسكربت، ومن تجميع التشغيل البيني إلى .NET، وصار سبب تباين، بينما في WinRT تقرأ إسقاطات كلّ اللغات .winmd واحداً مشتركاً
subgraph old["COM الكلاسيكي: المسار لكلّ لغة"]
idl["IDL ← ترويسة C++"]
tlb["TLB ← VB6 والسكربت"]
ia["تجميع تشغيل بيني ← .NET"]
end
winmd[".winmd (بيانات وصفية واحدة)"]
winmd --> all["تقرأ إسقاطات كلّ اللغات مشتركاً"]
الشكل 6: أعاد .winmd طيّ توزيع معلومات النوع المتفرّق لكلّ لغة إلى شكل «بيانات وصفية واحدة يقرأها الجميع».
لم تختفِ القيود؛ تغيّرت حول سهولة الإسقاط
لمن يعرف COM الكلاسيكي بجملة: .winmd هو «إعادة عمل مكتبة الأنواع». إن فهمته إعادة تصميم لدور حاولت TLB أداءه، فوق تنسيق مجرَّب ECMA-335، كأصل مشترك لكلّ اللغات من البداية، اتّضح الموقع.
لكن القيود لم تختفِ. قيود TLB الأميل إلى الأتمتة استُبدلت بقيود نظام أنواع WinRT الخاص المتمحور حول «إمكان الإسقاط الآمن إلى كلّ اللغات». غياب وراثة واجهات يعرّفها المستخدم الذي رأيناه في الفصل 3 مثال على ذلك.12
لذا لا يمكن بالضرورة إدخال عقد COM/IDL قائم إلى WinRT كما هو. قد يلزم إعادة تصميم الواجهة.
flowchart TB
accTitle: الاستبدال من قيود مكتبة الأنواع إلى قيود نظام أنواع WinRT
accDescr: قيود التعبير الأميل إلى الأتمتة التي كانت لـ TLB (مكتبة الأنواع) لم تُرفَع بـ .winmd بل استُبدلت بقيود نظام أنواع WinRT الخاص المتمحور حول إمكان الإسقاط الآمن إلى كلّ اللغات. مثال ذلك غياب وراثة واجهات يعرّفها المستخدم، ولا يمكن بالضرورة إدخال عقد COM/IDL قائم كما هو وقد يلزم إعادة تصميم الواجهة
tlb["قيود TLB (أميل إلى الأتمتة)"] -->|"استبدال"| wrt["قيود نظام أنواع WinRT (محور قابلية الإسقاط)"]
wrt -.-> ex["مثال: وراثة يعرّفها المستخدم غير جائزة"]
wrt -.-> re["عقد COM قائم قد يحتاج مراجعة تصميم"]
الشكل 7: قيود TLB لم «تختفِ» بل استُبدلت بقيود أخرى محورها قابلية الإسقاط إلى كلّ اللغات.
5. C++/WinRT وC#/WinRT ليسا «غلافاً» بل إسقاطاً
إظهار العقد المشترك بشكل طبيعي لكلّ لغة
مع عقد مشترك .winmd يمكن توليد طريقة الإظهار في كلّ لغة تلقائياً بأداة. هذا هو إسقاط اللغة (language projection). ينشر واجهات WinRT وفق أسلوب كلّ لغة ويخفي تفاصيل COM، فيقدّم تجربة برمجة طبيعية لتلك اللغة.411
الإسقاطان اللذان تدعمهما Microsoft حالياً هما التاليان.4
| الإسقاط | ما يولّده | السمات |
|---|---|---|
| C++/WinRT | يولّد cppwinrt.exe ترويسة إسقاط نحو C++ من .winmd |
إسقاط قائم على ملفّات ترويسة C++17 القياسي. لا حاجة لامتداد لغة مثل C++/CX، وهو خلف C++/CX وWRL11 |
| C#/WinRT (CsWinRT) | يولّد cswinrt.exe شيفرة C# من .winmd ويجعلها تجميع تشغيل بيني |
إسقاط نحو .NET. سلسلة أدوات مستقلّة عن وقت التشغيل1516 |
مسار جهة C# مربك قليلاً فنفرزه. حتّى .NET Core 3.x كان وقت تشغيل .NET يدعم استخدام WinRT/winmd تضميناً. في .NET 5 حُذف ذلك الدعم المضمَّن وانتقل إلى C#/WinRT.16 ليست واجهات WinRT التي صارت غير قابلة للاستخدام؛ تغيّر مكان تولّي الإسقاط.
الآن عند تحديد TFM مثل net8.0-windows10.0.19041.0 في C# تُشار تجميعات إسقاط Windows SDK تلقائياً.10
flowchart TB
accTitle: توليد الإسقاط إلى كلّ لغة من .winmd
accDescr: عندما يقرأ cppwinrt.exe .winmd واحداً تُولَّد ترويسة إسقاط نحو C++17، وعندما يقرأ cswinrt.exe يُولَّد تجميع تشغيل بيني نحو C#، فتُنشر واجهات WinRT بشكل وفق أسلوب كلّ لغة
winmd2[".winmd (عقد الواجهة)"]
winmd2 --> cpp["cppwinrt.exe ← ترويسة C++17"]
winmd2 --> cs["cswinrt.exe ← تجميع تشغيل بيني C#"]
cpp --> cppcode["يمكن الاستدعاء بأسلوب C++"]
cs --> cscode["يمكن الاستدعاء بأسلوب C#"]
الشكل 8: الإسقاط ليس غلافاً يُكتب يدوياً؛ تولّده الأداة آلياً من العقد (.winmd).
حتّى مع التوليد التلقائي يبقى كيان الاستدعاء COM
نسمّي في هذه المقالة «إسقاطاً» لا «غلافاً» للتأكيد أنّه آلية تُشتق آلياً من البيانات الوصفية، لا طبقة ترجمة يتابعها إنسان واجهة واجهة. يمكن جعل الواجهات المحمولة على .winmd قابلة للاستخدام في كلّ اللغات المدعومة من البداية.
وأيّاً كانت اللغة المستدعِية، ما يحدث تحت الإسقاط استدعاء COM نفسه. مثلاً كتابة await picker.PickSingleFolderAsync() في C# تجعل الإسقاط يجسر IAsyncOperation في WinRT إلى عالم Task في .NET. ومع ذلك يُستخدم في جهة ABI استدعاء دالّة عبر vtable وHRESULT. ظهور الخطأ بشكل استثناء COM (HRESULT مثل 0x80070005) لهذا السبب.
flowchart TB
accTitle: الطبقات من شيفرة C# إلى واجهة WinRT لنظام التشغيل
accDescr: تُحوَّل شيفرة التطبيق C# أو C++ عبر إسقاط اللغة إلى ABI الخاص بـ WinRT أي استدعاء vtable لـ IInspectable، وتصل إلى واجهة WinRT التي ينفّذها نظام التشغيل. الإسقاط يخفي تفاصيل COM فقط، وكيان الاستدعاء COM
code["شيفرة التطبيق (C# وC++)"]
proj2["إسقاط اللغة"]
abi["ABI الخاص بـ WinRT (vtable لـ IInspectable)"]
os["تنفيذ واجهة WinRT لنظام التشغيل"]
code --> proj2
proj2 --> abi
abi --> os
الشكل 9: ما يخفيه الإسقاط «تفاصيل» COM لا COM نفسه.
معرفة هذه البنية تتيح تقسيم الأعطال إلى طبقتين. عدم اتّساق إصدار الشيفرة المولَّدة أو تخلّف إعداد TFM طبقة الإسقاط، وHRESULT والشقّة وعدّ المراجع طبقة ABI. في الأخيرة تصير خبرة تطوير COM سلاحاً كما هي.
6. مواضع الانحشار في تطبيقات سطح المكتب ── HWND والهوية والشقّة
افصل أوّلاً «إمكان الإشارة إلى الواجهة» عن «شروط العمل»
معظم واجهات WinRT يمكن استدعاؤها من تطبيقات سطح المكتب WPF وWinForms و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 فما بعده |
لكن إمكان الإشارة لا يعني أن كلّ واجهة تعمل كما هي. أكّد النقاط الثلاث التالية. شروط تسجيل إشعار التوست مرتَّبة على حدة في الفصل 9.
| ما تؤكّده | التدبير الرئيس |
|---|---|
| هل تحتاج نافذة وجهة عرض | مرّر HWND عبر COM interop موافق للواجهة |
| هل تحتاج package identity | امنح الهوية بـ MSIX أو حزمة تشير إلى موقع خارجي |
| هل هُيِّئ الخيط لـ WinRT | في الشيفرة الأصلية هيّئ بتحديد STA/MTA. في C# يتولّى وقت التشغيل ذلك عادة |
موضع الانحشار 1: مرّر HWND لأدوات الالتقاط ونحوها من الواجهة
تفترض بعض أدوات الالتقاط والحوارات وواجهة المشاركة CoreWindow في UWP كوجهة عرض. ليس لتطبيق سطح المكتب CoreWindow، لذا يلزم تمرير HWND نافذة المالك صراحة قبل العرض.6
المدخل المستخدم في أدوات الالتقاط ونحوها واجهة COM IInitializeWithWindow. ترث IUnknown وتقدّم نافذة مالك لكائن WinRT يُستخدم في تطبيق سطح مكتب.17
في C# اجلب HWND أوّلاً حسب إطار الواجهة المستخدم.186
| نافذة المالك | طريقة جلب HWND |
|---|---|
Window في WinUI |
WinRT.Interop.WindowNative.GetWindowHandle |
| نافذة WPF | WindowInteropHelper |
| نموذج WinForms | خاصّية Handle للنموذج |
ثم مرّره إلى الملتقط بـ WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd) واعرض بعده. في C++/WinRT اجلب الكائن بـ as<IInitializeWithWindow>() ثم استدعِ Initialize(hwnd). إن أُغفلت التهيئة يحدث استثناء أو فشل صامت.1819
الاستعلام بـ QueryInterface عن واجهة COM كلاسيكية على كائن WinRT حديث وتهيئته. أن هذا الجسر صار الأسلوب الرسمي يظهر أيضاً أن WinRT هو COM.
واجهة المشاركة واجهة أخرى. لا تستخدم IInitializeWithWindow تجاه DataTransferManager. استخدم IDataTransferManagerInterop المخصَّص ومرّر HWND إلى ShowShareUIForWindow.6
ملتقط Windows App SDK الجديد مسار آخر أيضاً. يتلقّى Microsoft.Windows.Storage.Pickers WindowId في المنشئ، لذا لا تلزم صيغة InitializeWithWindow. لكنّها ليست واجهة تُستخدم بإعداد TFM وحده. يفترض إدخال Windows App SDK، وللتطبيق unpackaged نشر وقت التشغيل وتهيئته على جهة التوزيع.19
sequenceDiagram
accTitle: إجراء عرض ملتقط من تطبيق سطح مكتب
accDescr: بعد أن يولّد تطبيق سطح المكتب الملتقط، استدعاء PickSingleFolderAsync كما هو يرمي استثناء أو فشلاً صامتاً، لذا يلزم أوّلاً جلب HWND نافذة المالك وتمريره بـ Initialize لـ IInitializeWithWindow ثم العرض
participant A as تطبيق سطح المكتب
participant P as الملتقط (WinRT)
A->>P: توليد
alt عرض دون تمرير HWND
A->>P: PickSingleFolderAsync
P-->>A: استثناء أو فشل صامت
else تمرير HWND أوّلاً
A->>P: اضبط HWND بـ IInitializeWithWindow
A->>P: PickSingleFolderAsync
P-->>A: يظهر الملتقط
end
الشكل 10: الأسلوب الرسمي سدّ «ليس لسطح المكتب CoreWindow» بتمرير HWND صريح.
موضع الانحشار 2: واجهات تحتاج package identity
بعض واجهات WinRT مثل سجلّ إشعارات التوست (ToastNotificationHistory) وقائمة القفز وأهداف المشاركة لا تعمل إلّا في تطبيق يملك package identity (تطبيق معبّأ). تفشل عند الاستدعاء من تطبيق unpackaged يُوزَّع بمثبّت تقليدي.7
التدبير اثنان. التعبئة بـ MSIX، أو استخدام «حزمة تشير إلى موقع خارجي (ما يُسمَّى sparse package)» تمنح معرّفاً مع الإبقاء على المثبّت القائم.20 أيّ واجهة تتطلّب الهوية يمكن تأكيدها من القائمة الرسمية.7
flowchart TB
accTitle: مساران إلى واجهة تحتاج package identity
accDescr: استدعاء واجهة WinRT تتطلّب package identity من تطبيق unpackaged يفشل، لذا يُمنح package identity إمّا بالتعبئة بـ MSIX أو بحزمة تشير إلى موقع خارجي تمنح المعرّف فقط مع الإبقاء على المثبّت القائم
need["تريد استخدام واجهة تتطلّب الهوية"]
need --> m1["عبّئ بـ MSIX"]
need --> m2["حزمة تشير إلى موقع خارجي"]
m1 --> id2["اكتساب package identity"]
m2 --> id2
id2 -.-> okid["يعمل سجلّ الإشعارات وغيره"]
الشكل 11: تدبير الواجهة التي تتطلّب الهوية خياران: «MSIX» أو «مثبّت قائم + منح معرّف».
موضع الانحشار 3: أكّد تهيئة الخيط وSTA/MTA
الخيط الذي يعالج كائنات WinRT يحتاج تهيئة مسبقة لـ WinRT. في الشيفرة الأصلية استخدم RoInitialize أو winrt::init_apartment وحدّد نموذج التزامن STA/MTA. في تطبيقات C# WPF/WinForms يتولّى وقت التشغيل التهيئة عادة.8
RoInitialize مدخل جيل WinRT في الإطار نفسه لـ CoInitializeEx في COM. ترشد وثائق CoInitialize نفسها أيضاً، عند استخدام Windows Runtime، إلى استدعاء RoInitialize أو Windows::Foundation::Initialize بدلاً منه.21
خيط واجهة WPF/WinForms هو STA، وكائنات الواجهة تُلمَس في خيط الواجهة، والانتظار الحاجب في STA يجلب قفل انتظار ميت. التفكير المعالج في مقالة STA/MTA يبقى حيّاً كما هو مع واجهات WinRT أيضاً.
هناك واجهات لا تُستخدم حتّى مع تجهيز HWND والهوية
التدابير حتّى الآن حديث واجهات لها مدخل لاستخدام سطح المكتب. الواجهات التي تعتمد على CoreWindow أو ApplicationView نفسها لا يمكن استخدامها في تطبيق سطح مكتب. في هذه الحالة لا تضف تهيئة بل ابحث عن واجهة بديلة.57
flowchart TB
accTitle: تفرّع مواضع الانحشار عند استدعاء واجهة WinRT من سطح المكتب
accDescr: أكّد أوّلاً أن الخيط مهيَّأ لـ WinRT (في الشيفرة الأصلية حدّد STA/MTA صراحة بـ RoInitialize ونحوه. في C# يتولّى وقت التشغيل ذلك عادة)، ثم إن كانت واجهة WinRT المراد استدعاؤها واجهة تفترض CoreWindow فمرّر HWND بـ IInitializeWithWindow أو استخدم الملتقط الجديد، وإن كانت تتطلّب package identity فامنح معرّفاً بـ MSIX أو حزمة تشير إلى موقع خارجي، والواجهات التي تعتمد على CoreWindow أو ApplicationView نفسها لا تُستخدم على سطح المكتب فابحث عن واجهة بديلة، ومعظم الواجهات الأخرى يمكن استدعاؤها كما هي بإعداد TFM أو C++/WinRT فقط
pre["تهيئة الخيط"] --> q{"ما طبيعة الواجهة المراد استدعاؤها؟"}
pre -.-> auto["الأصلي تحديد صريح، C# عادة تلقائي"]
q -->|"واجهة"| h["HWND أو الملتقط الجديد"]
q -->|"تتطلّب الهوية"| p2["MSIX أو منح معرّف"]
q -->|"تعتمد على CoreWindow نفسه"| x["ابحث عن واجهة بديلة"]
q -->|"غير ذلك"| ok3["يمكن الاستدعاء كما هو"]
الشكل 12: على فرض تهيئة الخيط (صريحة في الشيفرة الأصلية، وموكولة عادة إلى وقت التشغيل في C#) يمكن تصنيف مواضع الانحشار إلى ثلاث سلالات ولكلّ منها تدبير نمطي.
flowchart TB
accTitle: تقابل تهيئة خيط COM وWinRT
accDescr: في COM الكلاسيكي تهيّئ الخيط بتحديد STA أو MTA بـ CoInitializeEx، وفي WinRT تهيّئ بتحديد نموذج التزامن STA أو MTA نفسه بـ RoInitialize. ترشد وثائق CoInitialize أيضاً إلى استدعاء RoInitialize عند استخدام WinRT، ومفهوم الشقّة مشترك
com3["COM الكلاسيكي: CoInitializeEx"] --> apt["الشقّة (STA / MTA)"]
wrt["WinRT: RoInitialize"] --> apt
apt -.-> rule["خيط الواجهة STA وانتبه للانتظار"]
الشكل 13: تغيّر اسم واجهة التهيئة، لكن مفهوم الشقّة نفسه يُستخدم باستمرار.
7. أساس WinUI ── حتّى مع التطوير العلني لا يتغيّر العقد
الانتقال إلى التطوير العلني ليس تغييراً للعقد الثنائي
أعلنت Microsoft رسمياً في صيف 2025 نهجاً تدريجياً لنقل التطوير الرئيس لـ WinUI إلى ساحة علنية على GitHub. أربع مراحل: رفع تواتر تحديث المرآة، وتمكين البناء المحلّي، وترتيب الاختبارات ثم قبول مساهمات المجتمع، وأخيراً جعل GitHub المقرّ الرئيس للتطوير.22
عند نشر هذه المقالة (نهاية أغسطس 2026) تنصّ الوثائق الرسمية أيضاً على أن «WinUI يُطوَّر في العلن (built in the open)». صار ممكناً تتبّع تقدّم الهندسة اليومية في المستودع العلني.9
من الطبيعي أن يشعر مطوّر تابع تعاقب الأجيال WinForms ← WPF ← UWP ← WinUI أن «الإطار يتغيّر من جديد». لكن يلزم النظر بفصل إطار الواجهة الذي تغيّر عن الأساس تحته. بقي Win32 وCOM، وABI الخاص بـ WinRT من 2012 فما بعده، في المكان نفسه.
نافذة WinUI أيضاً مسندة بـ HWND
WinUI مجموعة واجهات WinRT تُقدَّم كجزء من Windows App SDK.923 Microsoft.UI.Xaml.Window الخاص به نافذة مسندة بـ HWND تحلّ محلّ نموذج النافذة القائم على CoreWindow في جيل UWP. يبدأ درس التشغيل البيني الرسمي أيضاً من جلب مقبض النافذة.24
أي أن تطبيق WinUI تطبيق Win32 تعمل فيه شجرة كائنات تتبع عقد IInspectable فوق نافذة HWND. معرفة QueryInterface وعدّ المراجع والشقّة المكتسبة في COM، ومعرفة HWND وحلقة الرسائل المكتسبة في Win32، تسريان كما هما في تحقيق أعطال WinUI.
flowchart TB
accTitle: تعاقب أجيال إطار الواجهة والأساس الذي لا يتغيّر
accDescr: تعاقبت أطر الواجهة WinForms وWPF وXAML في UWP وWinUI، لكن WinForms وWPF يركبان أساس Win32 وCOM مباشرة، وXAML في UWP وWinUI يركبان أساس Win32 وCOM نفسه عبر ABI الخاص بـ WinRT. ما تعاقب الطبقة العليا، وعقد الأساس لم يتغيّر
gen["تعاقب أجيال إطار الواجهة"]
gen --> f1["WinForms وWPF"]
gen --> f2["XAML في UWP"]
gen --> f3["WinUI (الحالي)"]
f2 --> abi3["ABI الخاص بـ WinRT (من 2012)"]
f3 --> abi3
f1 --> stable["الأساس الذي لا يتغيّر (Win32 + COM)"]
abi3 --> stable
الشكل 14: ما علا وهبط طبقة الإطار. يركب WinForms وWPF Win32+COM مباشرة، وUWP وWinUI عبر ABI الخاص بـ WinRT، الأساس نفسه.
flowchart TB
accTitle: الطبقات التي تسند تطبيق WinUI
accDescr: يُقدَّم XAML وعناصر WinUI كجزء من Windows App SDK، ويعمل فوق ABI الخاص بـ WinRT أي عقد IInspectable، وتحته أساس COM وHWND في Win32. تحت تعاقب أجيال إطار الواجهة لم يتغيّر عقد هذا الأساس
ui["WinUI (XAML والعناصر)"]
sdk["Windows App SDK"]
abi2["ABI الخاص بـ WinRT (IInspectable)"]
base2["COM + Win32 (HWND)"]
ui --> sdk
sdk --> abi2
abi2 --> base2
الشكل 15: تحت WinUI ABI الخاص بـ WinRT، وتحته COM الكلاسيكي وWin32. تغيّر الرصف لا الأساس.
الخلط الجزئي بـ XAML Islands يحتاج تأكيد قيود كلّ جيل
استراتيجية «خلط عناصر WinUI فقط في شاشات WPF/WinForms القائمة» تحتاج تقييماً بفصل جيل XAML Islands.
| الجيل | الوضع عند الاستخدام من WPF/WinForms |
|---|---|
| XAML Islands جيل UWP | توجد عناصر غلاف في Windows Community Toolkit. لكن نحو WPF/WinForms يتوقّف عند جيل .NET Core 3.x ولا يُدعم في .NET الحالي25 |
| جيل WinUI 3 | يمكن الاستضافة من WPF وWinForms وWin32 بـ DesktopWindowXamlSource في Windows App SDK. لكن لا عناصر غلاف مريحة مثل جيل UWP، فعبء التنفيذ والتحقّق بمعاملة واجهة الاستضافة مباشرة قائم26 |
إن خطّطت للخلط التدريجي فأأمن ألّا تفترض خلطاً لكلّ عنصر فقط. محور استخدام واجهات WinRT لكلّ وظيفة (الفصل 6) أو الفصل لكلّ شاشة أو عملية أصلب في 2026.
8. الدلالة لتطبيقات الأعمال ── الترحيل الشامل والاستخدام الجزئي مشكلتان مختلفتان
«WinRT عالم جديد منفصل، لذا الاستخدام لا يكون إلّا بهجر الأصول القائمة وإعادة البناء». يُحَلّ هذا سوء الفهم الذي يُلتقى في مواقع Custom Software Development بقسمه قسمين.
أصول COM القائمة وWinRT يمكنهما التعايش
يمكن إضافة واجهات WinRT إلى تطبيق WPF يستخدم أتمتة Excel COM ويحمل عناصر ActiveX ويستدعي مكوّنات COM خاصّة بالشركة. ليس بهلوانية خاصّة بل جمع وظائف فوق أساس COM نفسه.
لإشعار توست مثلاً اضبط TFM للإشارة إلى الواجهة واستوفِ شروط التسجيل لإصدار الإشعار. حتّى لا تُنسى الأخيرة رتّبناها حسب المسار في الفصل 9. في C++/WinRT يمكن معاملة واجهات WinRT وCOM الكلاسيكي كليهما بالآلية نفسها winrt::com_ptr وwinrt::implements.21
قدّر تجديد الواجهة وإضافة الوظيفة على حدة
«هل ترحّل الواجهة شاملة إلى WinUI» و«هل تستخدم واجهات WinRT في المواضع اللازمة فقط» حكمان يختلفان في الحجم والمدّة والمخاطر.
الترحيل الشامل للواجهة مسألة اختيار إطار تعتمد على أصول الشاشات وعناصر الطرف الثالث وهيئة التطوير. عولج هذا الحكم في اختيار WinForms/WPF/WinUI. أمّا الاستخدام الجزئي لواجهات WinRT فتحسين صغير يمكن الشروع فيه اليوم على تطبيق قائم.
لا حاجة لتقدير ترحيل شامل للواجهة في قضية تريد إشعارات توست فقط. وبالعكس لا حاجة لتأجيل استخدام واجهات WinRT أيضاً بسبب «لن نرحّل إلى WinUI».
flowchart TB
accTitle: الترحيل الشامل والاستخدام الجزئي حكمان مختلفان
accDescr: الترحيل الشامل للواجهة إلى WinUI حكم كبير لاختيار الإطار يعتمد على أصول الشاشات والهيئة، بينما الاستخدام الجزئي لواجهات WinRT حكم صغير يمكن إضافته اليوم إلى تطبيق WPF أو WinForms قائم بإعداد TFM ونحوه، ويُدرَس هذان بفصل دون خلط
goal["«تريد استخدام وظائف Windows جديدة»"]
goal --> big["ترحيل شامل للواجهة (حكم كبير)"]
goal --> small["استخدام جزئي لواجهات WinRT (حكم صغير)"]
big -.-> dep["يعتمد على أصول الشاشات والهيئة"]
small -.-> today["يمكن الإضافة اليوم إلى تطبيق قائم"]
الشكل 16: إلى «وظيفة جديدة» طريقان، وخلطهما يُفسد التقدير والحكم.
9. جدول قرار ── نسخة WinRT من الإبقاء والتغليف والاستبدال
اختر التغيير اللازم فقط حسب الموقف
كامتداد لـ جدول قرار ActiveX نلخّص الحكم حسب الموقف في جهة WinRT.
| الموقف | التوصية | السبب |
|---|---|---|
| تريد استخدام واجهات WinRT مثل التوست والمشاركة وBluetooth من WPF/WinForms | استخدام جزئي بإعداد TFM (أو إدخال C++/WinRT) | يمكن الاستدعاء اليوم بلا ترحيل واجهة. لكن واجهة المشاركة عبر IDataTransferManagerInterop (الفصل 6)، والتوست انظر التكملة تحت الجدول106 |
| استثناء / فشل صامت في ملتقط أو حوار | مرّر HWND بـ IInitializeWithWindow. للجديد الملتقط الموافق لـ WindowId (يفترض إدخال Windows App SDK) |
الأسلوب الرسمي سدّ تصميم يفترض CoreWindow بـ HWND619 |
| لا يعمل سجلّ الإشعارات أو قائمة القفز وغيرهما | امنح package identity بـ MSIX أو حزمة تشير إلى موقع خارجي | الواجهة التي تتطلّب الهوية تفترض التعبئة720 |
| أصول COM/ActiveX/OLE قائمة | لا تهجر. احكم الإبقاء/التغليف/الاستبدال لكلّ أصل | ليست حصرية مع WinRT ويمكن التعايش على الأساس نفسه (الفصل 8) |
| واجهة تطبيق سطح مكتب جديد | قيّم WinUI كمرشّح أوّل (WPF لا يزال قائماً) | التطوير العلني أوضح اتجاه الاستثمار. الأساس ABI الخاص بـ WinRT229 |
| خلط عناصر WinUI جزئياً في شاشات WPF/WinForms قائمة | قيّم بحذر مع تقدير عبء التنفيذ بلا غلاف | Islands جيل UWP يتوقّف عند .NET Core 3.x، وجيل WinUI 3 واجهة استضافة فقط2526 |
| خطّة تفترض «انتهى WinRT مع UWP» | صحّح الفرض | WinRT أساس واجهة حالي يمكن استدعاؤه من سطح المكتب5 |
إشعارات التوست لا تُعرض بإعداد TFM وحده
إعداد جعل الواجهة قابلة للاستدعاء وتسجيل عرض الإشعار أمران مختلفان. عندما «يمرّ البناء لكن الإشعار لا يظهر» أكّد لا جهة الاستدعاء فقط بل المسار المستخدم وشكل الحزمة ووجود الترقية.
عند استخدام ToastNotificationManager التقليدي
في تطبيق unpackaged بلا package identity يفترض تسجيل اختصار قائمة ابدأ مُسنَد إليه AppUserModelID (AUMID). دونه لا يمكن إصدار توست.27
تطبيق معبّأ بـ MSIX ونحوه تمنح هوية الحزمة AUMID، لذا لا يلزم هذا التسجيل اليدوي.
عند استخدام AppNotificationManager في Windows App SDK
في هذا المسار الموصى به حالياً يلزم إدخال Windows App SDK واستدعاء Register() عند البدء. ثم تنقسم الشروط حسب شكل الحزمة.28
| شكل الحزمة | ما تؤكّده إضافياً |
|---|---|
| unpackaged | يلزم نشر وقت تشغيل Windows App SDK على كلّ جهاز توزيع. يقوم Register() بتسجيل خادم COM |
| معبّأ بـ MSIX | لا يعمل التسجيل التلقائي بـ Register(). أعلن منشّط COM في Package.appxmanifest |
علاوة على ذلك في مسار Windows App SDK الإشعار من عملية ترقّت كمسؤول غير مدعوم. Show يفشل بهدوء دون استثناء. لتطبيق يحتاج ترقية انظر في فصل الإشعار إلى عملية غير مُرقّاة.28
تفاصيل تنفيذ التسجيل مشمولة في دليل تنفيذ الإقامة في علبة النظام والإشعار.
flowchart TB
accTitle: مسارا إشعار التوست والتسجيل اللازم
accDescr: لإصدار إشعار توست من تطبيق سطح مكتب يتفرّع مسار ToastNotificationManager التقليدي حسب packaged أم لا، ويحتاج تطبيق unpackaged المرور بتسجيل اختصار قائمة ابدأ مُسنَد إليه AppUserModelID، ويعطي تطبيق packaged هوية الحزمة AppUserModelID. في مسار AppNotificationManager لـ Windows App SDK بالإضافة إلى إدخال SDK واستدعاء Register عند البدء يستوفي تطبيق unpackaged نشر وقت التشغيل على كلّ جهاز توزيع وتطبيق معبّأ بـ MSIX إعلان منشّط COM في البيان قبل المضيّ. علاوة على ذلك لمسار Windows App SDK تفرّع هل العملية مُرقّاة، والإشعار من عملية ترقّت كمسؤول غير مدعوم وShow يفشل بهدوء دون استثناء، لذا العرض في هذا المسار مقصور على عملية غير مُرقّاة، وتطبيق يحتاج ترقية يفصل الإشعار إلى عملية غير مُرقّاة
want["تريد إصدار توست"]
want --> c1["تقليدي: ToastNotificationManager"]
want --> c2["WASDK: AppNotificationManager"]
c1 --> q1{"packaged؟"}
q1 -->|"لا"| s1["تسجيل اختصار AUMID"]
q1 -->|"نعم"| s2["الهوية تقدّم AUMID"]
c2 --> r2["إدخال SDK + Register()"]
r2 --> q2{"packaged؟"}
q2 -->|"لا"| s3["نشر وقت التشغيل"]
q2 -->|"نعم"| s4["إعلان COM في البيان"]
s1 --> shown["يُعرض الإشعار"]
s2 --> shown
s3 --> elev{"عملية مُرقّاة؟"}
s4 --> elev
elev -->|"لا"| shown
elev -->|"نعم"| fail["غير مدعوم: Show يفشل بهدوء"]
fail -.-> comp["افصل الإشعار إلى عملية غير مُرقّاة"]
الشكل 17: أيّاً كان المسار لا يُبلَغ العرض إلّا بعد استيفاء الفرض حسب شكل التعبئة. مسار Windows App SDK لا يُعرض من عملية مُرقّاة حتّى مع اكتمال الفرض كلّه.
أخيراً عُد إلى الإبقاء والتغليف والاستبدال
هل تحتاج وظيفة جديدة، أم تريد تجديد الواجهة نفسها. بالعودة إلى هذا السؤال يمكن الحكم بفصل استخدام WinRT عن استبدال الأصول القائمة.
flowchart TB
accTitle: تدفّق حكم معاملة الأصول القائمة وWinRT
accDescr: انطلاقاً من تطبيق سطح مكتب قائم، إن لم تلزم وظائف Windows جديدة فأبقِ، وإن لزمَت وظيفة فغلّف بالاستخدام الجزئي لواجهات WinRT (مع الانتباه إلى مواضع انحشار HWND وpackage identity وتهيئة الخيط في الفصل 6)، ولا تنظر في الاستبدال بـ WinUI إلّا عندما يلزم تجديد الواجهة نفسها، وهو تدفّق حكم تدريجي
start["تطبيق سطح مكتب قائم"] --> q2{"ما اللازم؟"}
q2 -->|"الوضع الحالي يكفي"| keep2["أبقِ (صيانة كما هي)"]
q2 -->|"وظيفة جديدة"| wrap2["غلّف (استخدام جزئي لواجهات WinRT)"]
q2 -->|"تجديد الواجهة"| rep["استبدل (قيّم WinUI)"]
wrap2 -.-> note2["انتبه إلى HWND والهوية والتهيئة (الفصل 6)"]
الشكل 18: هيكل «أبقِ أو غلّف أو استبدل» نفسه لجدول قرار ActiveX ينطبق كما هو على جهة WinRT أيضاً.
10. خلاصة
WinRT ليس بيئة تنفيذ جديدة منفصلة عن COM. إنّه أساس واجهة يجمع العقد الثنائي لـ COM مع بيانات وصفية وإسقاط إلى كلّ لغة.
| العنصر | الدور الذي أُمسك في هذه المقالة |
|---|---|
IUnknown وIInspectable |
يجعلان QueryInterface وعدّ المراجع أساساً، ويضيفان ثلاث دوال لجلب اسم النوع وغيره |
.winmd |
عقد مشترك لكلّ اللغات يمكن منه جلب التعريف من اسم النوع. يستعير تنسيق ECMA-335، لكن المقدَّم من النظام لا يحتوي شيفرة تنفيذ |
| C++/WinRT وC#/WinRT | يولّدان واجهة نحو كلّ لغة من العقد. صار إسقاط C# من .NET 5 فما بعده سلسلة أدوات مستقلّة عن وقت التشغيل |
حتّى إن صار المظهر واجهة طبيعية لـ C# أو C++، يُستخدم تحتها استدعاء COM عبر vtable وHRESULT. لذا فهم تمرير HWND على سطح المكتب وpackage identity وSTA/MTA وتهيئة الخيط يسهّل أيضاً عزل أعطال WinRT.
أُعلن مسعى نقل التطوير الرئيس لـ WinUI إلى ساحة علنية في صيف 2025 كنهج تدريجي. عند نشر هذه المقالة تنصّ الوثائق الرسمية أيضاً على «built in the open»، لكن عقد IUnknown ← IInspectable تحته لم يتغيّر.229
الخلاصة لتطبيقات الأعمال هي نفسها. أصول COM/ActiveX القائمة وWinRT يمكنهما التعايش. عدم خلط الترحيل الشامل للواجهة والاستخدام الجزئي لواجهات WinRT يحفظ دقّة التقدير والحكم.
المستند المركَّب في التسعينيات الذي رأيناه في مقالة OLE، وWinUI الحالي، يقومان على IUnknown نفسه. معرفة COM ليست مجرّد «الإلمام بالقديم». إنّها قراءة أساس Windows الحالي.
مقالات ذات صلة
- ما COM - لماذا لا يزال تصميم Windows COM جميلاً
- ما COM / ActiveX / OCX - شرح الفروق والعلاقة مجتمعة
- أساسيات COM STA/MTA - نموذج الخيوط وتفكير تجنّب التعليق
- كيف نعامل ActiveX / OCX الآن - جدول قرار الإبقاء أو التغليف أو الاستبدال
- اختيار WinForms/WPF/WinUI - جدول قرار عملي
- ما كائن OLE؟ ── آلية التضمين والربط ومطبّات مستندات الأعمال
- استخدام DLL لـ .NET 8 بكتابة أنواع من VBA - نشر COM وTLB بـ dscom
مجالات الاستشارة ذات الصلة
تتعامل شركة كومورا سوفت ذ.م.م. مع تطوير مكوّنات COM وصيانة تطبيقات أعمال Windows التي تتضمّن أصول COM وتعديلها واستبدالها. يمكن الاستشارة من مرحلة تصير فيها محتويات هذه المقالة محور النقاش كما هي: «أريد استخدام إشعارات توست وملتقط مع الإبقاء على WPF»، «استدعاء واجهة WinRT توقف باستثناء»، «هل ينبغي الترحيل إلى WinUI أم الاستفادة من الأصول القائمة».
روابط مرجعيّة
-
Microsoft Learn, Consume COM components with C++/WinRT. حول البرمجة عبر واجهة لا كائن في COM، وسريان ذلك خلف كواليس واجهات WinRT التي هي تطوّر لـ COM (an evolution of COM)، وإمكان معاملة WinRT وCOM الكلاسيكي بالأسلوب نفسه بمؤشّر COM ذكي winrt::com_ptr. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Windows Metadata (WinMD) files. حول وصف واجهات WinRT بيانات وصفية قابلة للقراءة آلياً .winmd واستخدام الأدوات وإسقاطات اللغات لها، وتضمين Windows بيانات وصفية لكلّ واجهات WinRT المقدَّمة من النظام وتقديم واجهات حلّ، وإمكان طرف ثالث المشاركة في إسقاط اللغة بالتنسيق نفسه، وكون التنسيق المادي مواصفة ECMA-335 (نفسه لتجميعات CLR) وكون WinMD المقدَّم من النظام بيانات وصفية خالصة. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Windows Runtime (WinRT) language projections. حول نشر إسقاط اللغة واجهات WinRT وفق أسلوب كلّ لغة، وتعريف .winmd لواجهات WinRT وقراءة الإسقاط لها، ودعم Microsoft C++/WinRT (C++17 فما بعده) وC#/WinRT (.NET) فقط. ↩ ↩2 ↩3
-
Microsoft Learn, WinRT APIs callable from a desktop app. حول إمكان استخدام معظم واجهات WinRT من تطبيقات سطح المكتب .NET وC++ الأصلي، واستثناء أصناف صُمِّمت خصّيصاً لـ UWP مثل CoreDispatcher وCoreWindow وApplicationView. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Display WinRT UI objects that depend on CoreWindow. حول اعتماد بعض أدوات الالتقاط والنوافذ المنبثقة والحوارات على CoreWindow، وعدم دعم CoreWindow في تطبيقات سطح المكتب، وإمكان ضبط HWND نافذة المالك قبل العرض للأصناف التي تنفّذ IInitializeWithWindow (أو IDataTransferManagerInterop المكافئ)، والإجراء في كلّ من WinUI 3 وWPF وWinForms. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, WinRT APIs not supported in desktop apps. حول وجود سلالتين لواجهات WinRT لا تُستخدم في تطبيقات سطح المكتب، واجهات تعتمد على وظائف واجهة خاصّة بـ UWP وواجهات تتطلّب package identity (ToastNotificationHistory وJumpList وغيرهما)، ودعم الأخيرة في تطبيقات معبّأة بـ MSIX فقط. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, RoInitialize function (roapi.h). حول تهيئة RoInitialize الخيط الحالي لـ Windows Runtime بنموذج التزامن المحدَّد (RO_INIT_SINGLETHREADED/RO_INIT_MULTITHREADED)، وحاجة كلّ خيط ينشّط كائنات WinRT أو يشغّلها إلى تهيئة مسبقة، وصيرورة RPC_E_CHANGED_MODE عند تحديد متعارض على خيط مهيَّأ كـ MTA. ↩ ↩2
-
Microsoft Learn, WinUI 3. حول كون WinUI إطار الواجهة الأصلية الموصى به لتطبيقات سطح مكتب Windows الجديدة، وتقديمه كجزء من Windows App SDK، وعمله على Windows 10 الإصدار 1809 فما بعده، وتطويره في العلن. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Call Windows Runtime APIs in desktop apps. حول إمكان استدعاء واجهات WinRT عند تحديد TFM يحمل إصدار نظام تشغيل Windows (مثل net10.0-windows10.0.22621.0) من .NET 6 فما بعده فتُشار حزمة استهداف Windows SDK، واستخدام C++ حزمة NuGet Microsoft.Windows.CppWinRT وC++/WinRT بـ C++17 فما بعده. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Introduction to C++/WinRT. حول كون C++/WinRT إسقاط لغة بـ C++17 الحديث القياسي بالكامل وتنفيذه مكتبة قائمة على ملفّات ترويسة، وكونه الخلف الموصى به لـ C++/CX وWRL، وتصميم WinRT قائماً على واجهات COM ويُوصَل إليه عبر إسقاط لغة، وإخفاء الإسقاط تفاصيل COM، وتوليد cppwinrt.exe ترويسة إسقاط من .winmd. ↩ ↩2 ↩3
-
Microsoft Learn, The Windows Runtime (WinRT) type system. حول تتطلّب كلّ واجهة WinRT IInspectable ضمناً وتتطلّب IInspectable IUnknown، وتعريف IUnknown لـ QueryInterface وAddRef وRelease، والدوال الثلاث التي يضيفها IInspectable GetIids وGetRuntimeClassName وGetTrustLevel، وتمكين إسقاط اللغة بإعادة GetRuntimeClassName اسماً قابلاً للحلّ بالبيانات الوصفية، وغياب وراثة واجهات يعرّفها المستخدم في نظام أنواع WinRT والتعبير عنها بـ requires. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, IInspectable interface (inspectable.h). حول تقديم IInspectable الوظائف اللازمة لكلّ أصناف WinRT، ووراثته IUnknown، وامتلاكه الدوال الثلاث GetIids وGetRuntimeClassName وGetTrustLevel. ↩
-
Microsoft Learn, Introduction to Microsoft Interface Definition Language 3.0. حول كون MIDL 3.0 نحواً حديثاً موجزاً لإعلان أنواع WinRT، ووصف عقد WinRT لا يزال بـ IDL وتوليد مترجم MIDL Windows Metadata (.winmd). ↩
-
Microsoft Learn, C#/WinRT. حول معالجة cswinrt.exe المتضمَّن في حزمة NuGet لـ C#/WinRT .winmd وتوليد شيفرة C# وترجمتها إلى تجميع تشغيل بيني، وموقعه المماثل لتوليد C++/WinRT ترويسة نحو C++. ↩
-
Microsoft Learn, Built-in support for WinRT is removed from .NET. حول حذف الدعم المضمَّن لـ Windows Runtime من .NET في .NET 5 والانتقال إلى سلسلة أدوات CsWinRT. ↩ ↩2
-
Microsoft Learn, IInitializeWithWindow interface (shobjidl_core.h). حول كونها واجهة تقدّم نافذة مالك لكائن WinRT يُستخدم في تطبيق سطح مكتب، ووراثتها IUnknown وامتلاكها الدالّة Initialize(HWND). ↩
-
Microsoft Learn, Use WinRT COM interop classes in .NET. حول حاجة بعض كائنات WinRT مثل ملتقط الملفّ والحوار إلى HWND قبل العمل في تطبيق سطح مكتب، وإمكان التهيئة بأصناف C# آمنة الأنواع WinRT.Interop.WindowNative وWinRT.Interop.InitializeWithWindow دون استدعاء QueryInterface مكتوب يدوياً. ↩ ↩2
-
Microsoft Learn, Tutorial: Open files and folders with pickers in WinUI. حول صيرورة استثناء أو فشل صامت عند استخدام Windows.Storage.Pickers التقليدي في تطبيق سطح مكتب (WinUI 3) دون تهيئة بـ HWND قبل العرض، وغياب CoreWindow في تطبيق سطح مكتب WinUI 3، وتلقّي ملتقط Windows App SDK الجديد (Microsoft.Windows.Storage.Pickers) WindowId في المنشئ وعدم لزوم صيغة InitializeWithWindow. ↩ ↩2 ↩3
-
Microsoft Learn, Features that require package identity. حول تتطلّب بعض وظائف Windows وواجهات WinRT package identity وقت التشغيل، وإمكان الحصول على المعرّف بالإضافة إلى التوزيع بحزمة MSIX بحزمة تشير إلى موقع خارجي (packaged with external location). ↩ ↩2
-
Microsoft Learn, CoInitialize function (objbase.h). حول تهيئة CoInitialize مكتبة COM كـ STA، ووجوب استدعاء التطبيقات الجديدة CoInitializeEx، ووجوب استدعاء RoInitialize أو Windows::Foundation::Initialize بدلاً منه عند استخدام Windows Runtime. ↩
-
GitHub, WinUI: Now Developing in the Open (microsoft/microsoft-ui-xaml Discussion #10700). الإعلان الرسمي نهاية يوليو 2025. حول عرض نهج تدريجي لجعل مستودع WinUI علنياً (رفع تواتر تحديث المرآة، وتحقيق البناء المحلّي، وقبول مساهمات المجتمع بعد ترتيب الاختبارات، وجعل GitHub أخيراً المقرّ الرئيس للتطوير). ↩ ↩2 ↩3
-
Microsoft Learn, Windows App SDK. حول كون Windows App SDK مجموعة مكتبات تطوير تطبيقات Windows الحالية بما فيها WinUI. ↩
-
Microsoft Learn, Walkthrough: WinUI 3 app with Win32 interop. حول توسيع صنف Window في WinUI لدعم نافذة سطح المكتب، وإسناد Window في تطبيق سطح مكتب WinUI 3 بمقبض نافذة Win32 (HWND) وإمكان جلب مقبض النافذة وتشغيله بواجهات Win32. ↩
-
Microsoft Learn, Host UWP XAML controls in desktop apps (UWP XAML Islands). حول كون XAML Islands جيل UWP آلية تحميل عناصر UWP XAML في تطبيقات سطح مكتب WPF وWinForms وC++، واقتصار الاستخدام في WPF/WinForms على تطبيقات تستهدف .NET Core 3.x وعدم الدعم في .NET الحالي أو .NET Framework. ↩ ↩2
-
Microsoft Learn, DesktopWindowXamlSource Class (Microsoft.UI.Xaml.Hosting). حول كونه الصنف المركزي لواجهة استضافة XAML في Windows App SDK، وإمكان استضافة عناصر WinUI في أيّ عنصر واجهة مرتبط بـ HWND، وإمكان الاستخدام من تطبيقات سطح مكتب مصنوعة بـ WPF وWindows Forms وWin32 (Windows API). ↩ ↩2
-
Microsoft Learn, Quickstart: Sending a toast notification from the desktop. حول افتراض إرسال توست من تطبيق سطح مكتب اختصار قائمة ابدأ مضبوطاً عليه System.AppUserModel.ID، وضرورة تمرير ذلك AppUserModelID إلى استدعاء CreateToastNotifier وإلّا لا يُعرض التوست. ↩
-
Microsoft Learn, Use app notifications with a .NET app. حول وجوب استدعاء Register() بعد تسجيل معالج NotificationInvoked لاستخدام AppNotificationManager في Windows App SDK من تطبيق WPF/WinForms، وقيام Register() في تطبيق unpackaged تلقائياً بتسجيل خادم COM لتشغيل التطبيق عند نقر الإشعار، وافتراض إدخال Windows App SDK وتكوين استدعاء واجهات WinRT. ↩ ↩2
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
ما ينبغي تنظيمه قبل طلب التعهيد أو التطوير التعاقديّ لتطبيق Windows
قبل تكليف جهة خارجية بتطوير تطبيق Windows تعاقديّاً، إليك النقاط التي ينبغي تنظيمها: تعديل البرمجيّات القائمة، وتكامل الأجهزة، وCOM/Activ...
ما كائن OLE؟ ── آلية التضمين والربط ومطبّات مستندات الأعمال
حقيقة ميزة تضمين جدول Excel في Word هي كائن OLE. نشرح الفرق بين التضمين والربط، والملفّ المركَّب والتخزين المنظَّم، وآلية In-Place Activa...
كيف تعمل الحافظة والسحب والإفلات ── معالجة نقل بيانات OLE بشكل صحيح في تطبيقات الأعمال
لماذا ينهار لصق Excel ويفشل اللصق بعد إغلاق المصدر: تنسيقات الحافظة، والعرض المؤجَّل، وسحب وإفلات OLE، وسياسات السجلّ ومزامنة السحابة.
ما الذي يفعله بدء التشغيل السريع فعليّاً ── لماذا «إيقاف التشغيل» في Windows ليس إعادة التشغيل
إيقاف التشغيل في Windows إغلاق هجين افتراضيّاً، فيُحفَظ النواة وبرامج التشغيل في hiberfil.sys. لماذا لا تُعاد تهيئتها إلا بإعادة التشغيل،...
Time Travel Debugging ── تسجيل الأخطاء التي لا تتكرّر في التطبيقات طويلة التشغيل وإرجاعها
خطأ يظهر مرّة في الشهر لا يترك في تفريغ الانهيار سوى نتيجته. سجّل التنفيذ وأرجعه بـ WinDbg Time Travel Debugging (TTD): TTD.exe والمخزن ا...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
دعم إعادة استخدام الأصول القديمة وترحيلها
ندعم إعادة استخدام وترحيل الأصول التي تحمل قيود COM / ActiveX / OCX أو 32bit / 64bit.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- هل WinRT وقت تشغيل مُدار مثل .NET؟
- لا. WinRT (Windows Runtime) ليس بيئة تنفيذ تملك آلة افتراضية أو جامع قمامة؛ بل ABI (عقد ثنائي) قائم على COM. تنصّ وثائق Microsoft نفسها على «The Windows Runtime is based on COM»، وتتطلّب كلّ واجهة WinRT IInspectable المشتق من IUnknown. كيان الاستدعاء لا يزال استدعاء COM عبر vtable، ويقوم على عدّ المراجع (AddRef/Release) وQueryInterface. يسهل سوء الفهم أنّه بيئة مُدارة بسبب اسم «.winmd» والألفة مع .NET، لكن .winmd ملفّ بيانات وصفية يستعير التنسيق المادي نفسه لـ ECMA-335، والـ .winmd المقدَّم من النظام لا يحتوي شيفرة تنفيذ. إمكان الاستدعاء الطبيعي من C# لأن إسقاط اللغة يولّد إسقاطاً نحو C# من البيانات الوصفية.
- سمعت أن UWP لم يعد التيار الرئيس. هل لا يزال لتعلّم WinRT معنى الآن؟
- نعم. نموذج تطبيق UWP وأساس واجهة WinRT شيئان مختلفان. بعد انكماش UWP ما زالت معظم واجهات WinRT تُقدَّم بشكل يمكن استدعاؤه من تطبيقات سطح المكتب WPF وWinForms وWin32، وكثير من «وظائف Windows الحالية» مثل إشعارات التوست والمشاركة وBluetooth وOCR تُنشر كواجهات WinRT. علاوة على ذلك يقوم WinUI (Windows App SDK)، إطار الواجهة الأصلية الذي توصي به Microsoft حالياً، على ABI الخاص بـ WinRT. أي أن آليات WinRT (IInspectable و.winmd وإسقاط اللغات) ليست إرث UWP بل أساس تطوير تطبيقات Windows الحالي نفسه.
- هل يمكن استدعاء واجهات WinRT من تطبيق WPF أو WinForms؟
- نعم. من .NET 6 فما بعده يكفي جعل TargetFramework في ملفّ المشروع TFM يحمل إصدار نظام تشغيل Windows مثل net8.0-windows10.0.19041.0، فتُشار تجميعات إسقاط Windows SDK ويمكن استدعاء واجهات WinRT في فضاء الأسماء Windows.* مباشرة من C#. في C++ ثبّت حزمة NuGet Microsoft.Windows.CppWinRT واستخدم C++/WinRT بـ C++17 فما بعده. لكن هناك ثلاثة مواضع انحشار. الأوّل أن أصناف الواجهة التي تفترض CoreWindow مثل أدوات الالتقاط والحوارات تحتاج تمرير HWND نافذة المالك بـ IInitializeWithWindow قبل العرض (لكن DataTransferManager لواجهة المشاركة استثناء: لا يستخدم IInitializeWithWindow بل IDataTransferManagerInterop المخصَّص ويمرّر HWND إلى ShowShareUIForWindow مساراً آخر). الثاني أن بعض الواجهات مثل سجلّ الإشعارات وقائمة القفز تتطلّب package identity (تعبئة MSIX أو حزمة تشير إلى موقع خارجي). الثالث أن الخيط الذي يعالج كائنات WinRT يحتاج تهيئة مسبقة. في الشيفرة الأصلية تحدّد نموذج التزامن STA/MTA بـ winrt::init_apartment أو RoInitialize (وفي تطبيقات C# WPF/WinForms يتولّى وقت التشغيل ذلك عادة). الواجهات التي تعتمد على CoreWindow أو ApplicationView نفسها لا يمكن استخدامها في تطبيقات سطح المكتب أصلاً.
- استدعاء ملتقط مثل FolderPicker من تطبيق سطح مكتب يرمي استثناء. لماذا؟
- لأن بعض أصناف WinRT لأدوات الالتقاط والحوارات صُمِّمت بافتراض CoreWindow في UWP كوجهة عرض. ليس لتطبيق سطح المكتب CoreWindow، لذا يلزم إخباره بنافذة المالك صراحة قبل العرض. تحديداً اجلب أوّلاً HWND نافذة المالك (لـ Window في WinUI: WinRT.Interop.WindowNative.GetWindowHandle، وفي WPF: WindowInteropHelper، وفي WinForms: خاصّية Handle للنموذج)، وفي C# مرّره إلى الملتقط بـ WinRT.Interop.InitializeWithWindow.Initialize. في C++/WinRT استعلم عن الكائن كـ IInitializeWithWindow (as<IInitializeWithWindow>()) واستدعِ Initialize(hwnd). دون هذه التهيئة يحدث استثناء أو فشل صامت. ملتقطات Windows App SDK الجديدة (Microsoft.Windows.Storage.Pickers) صُمِّمت لتلقّي WindowId في المنشئ، فلم تعد صيغة التهيئة هذه لازمة (لكنّها واجهة Windows App SDK، لذا لا تكفي إعدادات TFM وحدها؛ يفترض إدخال SDK، وللتطبيق unpackaged نشر وقت التشغيل وتهيئته على جهة التوزيع).
- يُقال إن تطوير WinUI نُشر على GitHub. هل ينبغي التخلّي عن تطبيقات WPF/WinForms القائمة؟
- لا حاجة للتخلّي على عجل. نشر التطوير الرئيس لـ WinUI إعلان اتجاه «Microsoft تستثمر جدّياً في الإطار الأصلي»، وليس إعلان إنهاء الأطر القائمة. ما زال WPF وWinForms مدعومين كجزء من .NET. اقسم الحكم قسمين. الأوّل هل ترحّل إطار الواجهة، وهذا حديث كبير يعتمد على حجم أصول الشاشات وعناصر الطرف الثالث وهيئة التطوير. الثاني هل تستخدم واجهات WinRT جزئياً من التطبيق القائم، وهذا يمكن البدء به اليوم بإعداد TFM. لكن TFM يجعل الاستدعاء ممكناً فقط؛ فإشعارات التوست مثلاً تحتاج بالإضافة إلى الاستدعاء تسجيلاً للإشعار (المسار التقليدي لتطبيق unpackaged تسجيل اختصار بـ AppUserModelID ── وإن كان معبّأ فهوية الحزمة تعطي AppUserModelID فلا يلزم ── ومسار Windows App SDK إدخال SDK ── وللتطبيق unpackaged أيضاً إدخال وقت تشغيل Windows App SDK على كلّ جهاز توزيع ── واستدعاء AppNotificationManager.Register()، وفي تطبيق معبّأ بـ MSIX لا يعمل التسجيل التلقائي بـ Register() ويلزم علاوة على ذلك إعلان منشّط COM في Package.appxmanifest). وفي مسار Windows App SDK الإشعار من عملية ترقّت كمسؤول غير مدعوم، وShow يفشل بهدوء دون استثناء ── لتطبيق يحتاج ترقية انظر في فصل الإشعار إلى عملية غير مُرقّاة. ومع ذلك إن أردت إشعارات توست فقط فلا يلزم الترحيل إلى WinUI. وخلط عناصر WinUI جزئياً في شاشات WPF/WinForms القائمة (XAML Islands) يحتاج تمييز الجيل. Islands جيل UWP يتوقّف دعم WPF/WinForms عند .NET Core 3.x، وفي جيل WinUI 3 يمكن استخدام واجهة استضافة XAML في Windows App SDK (DesktopWindowXamlSource) من WPF/WinForms أيضاً لكن بلا عناصر غلاف مريحة فعبء التنفيذ كبير، لذا التقييم الحذر آمن في الوقت الحالي.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.