تعرف على Windows Pseudo Console (ConPTY)

تم نشر المقالة في 2 أغسطس 2018

هذه هي المقالة الثانية حول سطر أوامر Windows ، حيث سنناقش البنية الأساسية الجديدة وواجهات البرمجة الخاصة بوحدة التحكم الزائفة Windows ، أي Windows Pseudo Console (ConPTY): لماذا طورناها ، ولماذا هناك حاجة إليها ، وكيف تعمل ، وكيفية استخدامها وأكثر من ذلك بكثير.

في المقال الأخير "إرث الماضي الخطير. مشاكل سطر أوامر Windows " تحدثنا عن المتطلبات الأساسية لظهور المحطة الطرفية وتطور سطر الأوامر في Windows ، وبدأنا أيضًا في دراسة البنية الداخلية لوحدة تحكم Windows والبنية التحتية لسطر أوامر Windows. ناقشنا أيضًا العديد من المزايا والعيوب الرئيسية لوحدة تحكم Windows.

أحد العوائق هو أن Windows يحاول أن يكون "مفيدًا" ، ولكنه يتعارض مع مطوري وحدات التحكم البديلة والغير خارجية ، ومطوري الخدمة ، وما إلى ذلك. عند إنشاء وحدة تحكم أو خدمة ، يحتاج المطورون إلى الوصول إلى قنوات الاتصال التي من خلالها تتبادل طرفية / خدمة البيانات مع تطبيقات سطر الأوامر ، أو توفر الوصول إليها. في عالم * NIX ، هذه ليست مشكلة لأن * NIX يوفر بنية تحتية طرفية زائفة (PTY) تجعل من السهل إنشاء قنوات اتصال لوحدة تحكم أو خدمة. ولكن في نظام Windows لم يكن ...

... حتى الآن!

من TTY إلى PTY


قبل التحدث بالتفصيل عن تطورنا ، دعنا نعود لفترة وجيزة إلى تطوير المحطات.

في البداية كانت TTY


كما تمت مناقشته في مقال سابق ، في الأيام الأولى من الحوسبة ، كان المستخدمون يتحكمون في أجهزة الكمبيوتر باستخدام أنواع teletypes الكهروميكانيكية (TTYs) المتصلة بجهاز كمبيوتر من خلال نوع من قنوات الاتصال التسلسلي (عادة من خلال حلقة تيار 20 مللي أمبير ).


يعمل كين طومسون ودينيس ريتشي (واقفا) على Teletype DEC PDP-11 (رسائل بدون شاشة إلكترونية)

توزيع المحطة


تم استبدال Teletypes بمطاريف محوسبة مع شاشات إلكترونية (عادة شاشات CRT). عادةً ما تكون المحطات الطرفية أجهزة بسيطة للغاية (ومن ثم مصطلح "الطرف البكم") ، وتحتوي فقط على الإلكترونيات وقوة المعالجة اللازمة للمهام التالية:

  1. استقبال إدخال النص من لوحة المفاتيح.
  2. تخزين النص المدخل مؤقتًا في سطر واحد (بما في ذلك التحرير المحلي قبل الإرسال).
  3. إرسال / استقبال نص على قناة تسلسلية (عادة من خلال واجهة RS-232 المنتشرة في كل مكان).
  4. عرض النص المستلم على شاشة المحطة الطرفية.

على الرغم من بساطتها (أو ربما بفضلها) ، سرعان ما أصبحت المحطات الطرفية الوسيلة الرئيسية لإدارة الحواسيب الصغيرة ، والحواسيب المركزية والخوادم: معظم مشغلي إدخال البيانات ، ومشغلي الكمبيوتر ، ومسؤولي النظام ، والعلماء ، والباحثين ، ومطوري البرمجيات والنجوم الصناعية يعملون على محطات DEC ، IBM و Wyse وغيرها الكثير.


الأدميرال غريس هوبر في مكتبه مع محطة DEC VT220 على مكتبه

توزيع محطات البرمجيات


منذ منتصف الثمانينيات ، بدلاً من المحطات المتخصصة ، بدأ استخدام أجهزة الكمبيوتر ذات الأغراض العامة تدريجياً ، والتي أصبحت أكثر بأسعار معقولة وشعبية وقوة. كان لدى العديد من أجهزة الكمبيوتر القديمة وأجهزة الكمبيوتر الأخرى في الثمانينيات تطبيقات طرفية فتحت اتصال RS-232 بجهاز الكمبيوتر وتبادلت البيانات مع أي شخص على الطرف الآخر من الاتصال.

عندما أصبحت أجهزة الكمبيوتر ذات الأغراض العامة أكثر تعقيدًا ، ظهرت واجهة مستخدم رسومية (GUI) وعالم جديد تمامًا من التطبيقات المتزامنة ، بما في ذلك التطبيقات الطرفية.

ولكن كانت هناك مشكلة: كيف يمكن أن يتفاعل تطبيق طرفي مع تطبيق سطر أوامر آخر يعمل على نفس الجهاز؟ وكيف يمكن توصيل كبل تسلسلي فعليًا بين تطبيقين يعملان على نفس جهاز الكمبيوتر؟

مظهر المحطة الزائفة (PTY)


في عالم * NIX ، تم حل المشكلة من خلال إدخال محطة زائفة (PTY) .

يحاكي PTY معدات الاتصالات التسلسلية في جهاز الكمبيوتر عن طريق الكشف عن الأجهزة الزائفة الرئيسية والرقيق ("الرئيسية" و "الرقيق"): تتصل التطبيقات الطرفية بالجهاز الزائف الرئيسي ، وتطبيقات سطر الأوامر (على سبيل المثال ، الأصداف مثل cmd و PowerShell و bash) تتصل بجهاز الزائفة الرقيق. عندما يرسل عميل طرفي أوامر نصية و / أو أوامر تحكم (مشفرة كنص) إلى الجهاز الزائف الرئيسي ، يتم ترجمة النص إلى الرقيق المرتبط به. يتم إرسال النص من التطبيق إلى الجهاز الزائف الرقيق ، ثم يعود إلى الرئيسي ، وبالتالي إلى المحطة الطرفية. يتم دائمًا إرسال / استقبال البيانات بشكل غير متزامن.


تطبيق محطة وهمية / شل

من المهم ملاحظة أن الجهاز الزائف "التابع" يحاكي سلوك المطراف المادي ويحول أحرف الأوامر إلى إشارات POSIX. على سبيل المثال ، إذا أدخل المستخدم CTRL + C في النهاية الطرفية ، فسيتم إرسال قيمة ASCII لـ CTRL + C (0x03) عبر الشريحة الرئيسية. عند استلامها على جهاز زائف تابع ، تتم إزالة القيمة 0x03 من تدفق الإدخال ويتم إنشاء إشارة SIGINT .

يتم استخدام البنية التحتية لـ PTY على نطاق واسع من قبل * تطبيقات محطة NIX ، مديري لوحة النص (مثل الشاشة ، tmux) ، إلخ. تستدعي هذه التطبيقات openpty() ، والتي تُرجع زوجًا من واصفات الملفات (fd) للسيد PTY والعبد. بعد ذلك يمكن للتطبيق أن ينفذ / ينفذ تطبيق سطر أوامر تابع (على سبيل المثال ، bash) ، والذي يستخدم عبيده fd للاستماع وإرجاع النص إلى المحطة الطرفية المتصلة.

تتيح هذه الآلية للتطبيقات الطرفية "التحدث" مباشرة مع تطبيقات سطر الأوامر التي تعمل محليًا ، تمامًا كما تتحدث المحطة الطرفية مع كمبيوتر بعيد عبر اتصال تسلسلي / شبكة.

ماذا ، لا يوجد Windows وحدة تحكم زائفة؟


كما ناقشنا في المقالة السابقة ، في حين أن وحدة تحكم Windows تشبه من الناحية المفاهيمية محطة * NIX التقليدية ، فإنها تختلف بعدة طرق رئيسية ، خاصة عند أدنى المستويات ، والتي يمكن أن تسبب مشاكل لمطوري تطبيقات سطر أوامر Windows ، الطرفية / وحدات التحكم الطرفية والخادم التطبيقات:

  1. لا توجد بنية تحتية لـ PTY على Windows : عندما يقوم المستخدم بتشغيل تطبيق سطر الأوامر (على سبيل المثال ، Cmd ، PowerShell ، wsl ، ipconfig ، إلخ.) ، يقوم Windows نفسه "بتوصيل" مثيل جديد أو موجود من وحدة التحكم بالتطبيق.
  2. يتداخل Windows مع وحدات تحكم الطرف الثالث وتطبيقات الخادم : لا يمنح Windows (حاليًا) المحطات الطرفية طريقة لتوفير قنوات اتصال يريدون من خلالها التفاعل مع تطبيق سطر الأوامر. يجب على المحطات الطرفية الخارجية إنشاء وحدات تحكم خارج الشاشة ، وإرسال البيانات التي أدخلها المستخدم هناك وإلغاء الناتج عن طريق إعادة رسمه على الشاشة الخاصة بوحدة تحكم الطرف الثالث!
  3. فقط Windows لديه واجهة برمجة تطبيقات وحدة التحكم : تعتمد تطبيقات سطر الأوامر في Windows على Win32 Consol API ، مما يقلل من إمكانية نقل التعليمات البرمجية ، نظرًا لأن جميع الأنظمة الأساسية الأخرى تدعم النص / VT ، وليس API.
  4. الوصول عن بعد غير القياسي : يؤدي اعتماد تطبيقات سطر الأوامر على واجهة برمجة تطبيقات Consol إلى تعقيد التفاعل والبرامج النصية للوصول عن بعد بشكل كبير.

ماذا تفعل


غالبًا ما يطلب الكثير من المطورين آلية تشبه PTY في نظام Windows ، خاصةً أولئك الذين يعملون مع أدوات ConEmu / Cmder و Console2 / ConsoleZ و Hyper و VSCode و Visual Studio و WSL و Docker و OpenSSH.

حتى بيتر برايت ، محرر التكنولوجيا في Ars Technica ، طلب مني تنفيذ آلية PTY بعد بضعة أيام عندما بدأت العمل في فريق وحدة التحكم:



ومؤخرا مرة أخرى:



حسنًا ، لقد فعلنا ذلك أخيرًا: لقد أنشأنا وحدة تحكم زائفة لنظام التشغيل Windows :

مرحبًا بك في Windows Pseudo Console (ConPTY)


منذ تشكيل فريق وحدة التحكم قبل حوالي أربع سنوات ، انخرطت المجموعة في إصلاح وحدة تحكم Windows والآليات الداخلية لسطر الأوامر. في الوقت نفسه ، درسنا بانتظام وبعناية القضايا الموضحة أعلاه والعديد من القضايا والمشكلات الأخرى ذات الصلة. لكن البنية التحتية والرمز لم يكونوا مستعدين لجعل إصدار وحدة التحكم المزيفة ممكنًا ... حتى الآن!

ستقوم البنية التحتية الجديدة لوحدة التحكم الزائفة في Windows (ConPTY) وواجهة برمجة التطبيقات وبعض التغييرات الأخرى ذات الصلة بإزالة / تخفيف فئة كاملة من المشكلات ... دون كسر التوافق العكسي مع تطبيقات سطر الأوامر الحالية !

واجهة Win32 ConPTY API الجديدة (سيتم نشر الوثائق الرسمية قريبًا) متاحة الآن في أحدث إصدارات المطلعين من Windows 10 و Windows 10 Insider Preview SDK المطابق. ستظهر في الإصدار الرئيسي التالي من Windows 10 (في مكان ما في خريف / شتاء 2018).

وحدة التحكم / هندسة ConHost


لفهم ConPTY ، تحتاج إلى دراسة بنية وحدة تحكم Windows ، أو بالأحرى ... ConHost!

من المهم أن نفهم أنه على الرغم من أن ConHost يطبق كل ما تراه وتعرفه كتطبيق وحدة تحكم Windows ، فإن ConHost يحتوي أيضًا على معظم البنية الأساسية لسطر أوامر Windows وينفذها! من الآن فصاعدًا ، تصبح ConHost "عقدة وحدة تحكم" حقيقية ، تدعم جميع تطبيقات سطر الأوامر و / أو تطبيقات واجهة المستخدم الرسومية التي تتفاعل مع تطبيقات سطر الأوامر!

كيف؟ لماذا؟ ماذا؟ دعونا نلقي نظرة فاحصة.

فيما يلي عرض عالي المستوى لبنية وحدة التحكم الداخلية / ConHost:



مقارنةً بالبنية من المقالة السابقة ، يحتوي ConHost الآن على عدة وحدات إضافية لمعالجة VT ووحدة ConPTY الجديدة التي تنفذ واجهات برمجة التطبيقات المفتوحة:

  • ConPTY API : توفر واجهات برمجة تطبيقات Win32 ConPTY الجديدة آلية مشابهة لنموذج POSIX PTY ، ولكن في انكسار Windows.
  • تفاعل VT : يتلقى نص الإدخال بترميز UTF-8 ، ويحول كل حرف نص معروض إلى سجل INPUT_RECORD المقابل INPUT_RECORD في مخزن الإدخال المؤقت. يعالج أيضًا تسلسلات الهروب ، مثل 0x03 (CTRL + C) ، KEY_EVENT_RECORDS إلى KEY_EVENT_RECORDS ، والتي تنتج إجراء الهروب المناسب.
  • VT Renderer : يقوم بإنشاء تسلسلات VT اللازمة لتحريك المؤشر وتقديم النص والنمط في مناطق المخزن المؤقت للإخراج التي تم تغييرها من الإطار السابق.

حسنًا ، ولكن ماذا يعني ذلك حقًا؟

كيف تعمل تطبيقات سطر أوامر Windows؟


لفهم تأثير البنية التحتية الجديدة لـ ConPTY بشكل أفضل ، فلنلقِ نظرة على كيفية عمل وحدة تحكم Windows وتطبيقات سطر الأوامر حتى الآن.

عندما يقوم مستخدم بتشغيل تطبيق سطر أوامر مثل Cmd أو PowerShell أو ssh ، يقوم Windows بإنشاء عملية Win32 جديدة يقوم من خلالها بتحميل ثنائي التطبيق القابل للتنفيذ وأي تبعيات (موارد أو مكتبات).

عادة ما ترث العملية التي تم إنشاؤها حديثًا واصفات stdin و stdout من أصلها. إذا كانت العملية الأصل عملية Windows GUI ، فإن واصفات stdin و stdout مفقودة ، لذلك سيقوم Windows بنشر التطبيق الجديد وإرفاقه بمثيل وحدة التحكم الجديد. يتم نقل الاتصال بين تطبيقات سطر الأوامر ووحدة التحكم الخاصة بهم من خلال ConDrv.

على سبيل المثال ، إذا كنت تعمل من مثيل PowerShell بدون امتيازات مرتفعة ، فسوف ترث عملية التطبيق الجديدة الواصفات الأصلية stdin / stdout ، وبالتالي ، تتلقى بيانات الإدخال وتخرج الناتج إلى نفس وحدة التحكم مثل الأصل.

هنا نحتاج إلى إجراء حجز ، لأنه في بعض الحالات يتم تشغيل تطبيقات سطر الأوامر مرفقة بمثيل جديد من وحدة التحكم ، خاصة لأسباب أمنية ، ولكن الوصف أعلاه صحيح عادةً.

في النهاية ، عند بدء تشغيل تطبيق سطر الأوامر / shell ، يقوم Windows بتوصيله بمثيل وحدة التحكم (ConHost.exe) عبر ConDrv:



كيف يعمل ConHost؟


عندما يتم تشغيل تطبيق سطر الأوامر ، يقوم Windows بتوصيل التطبيق بمثيل جديد أو موجود من ConHost. يتم توصيل التطبيق ومثيل وحدة التحكم الخاصة به عبر برنامج تشغيل وحدة التحكم في وضع kernel (ConDrv) ، الذي يرسل / يستقبل رسائل IOCTL التي تحتوي على طلبات مكالمات API المسلسلة و / أو البيانات النصية.

تاريخياً ، كما ورد في مقال سابق ، فإن عمل ConHost بسيط نسبيًا اليوم:

  • يقوم المستخدم بإنشاء إدخال من لوحة المفاتيح / الماوس / القلم / لوحة اللمس ، والتي يتم تحويلها إلى KEY_EVENT_RECORD أو MOUSE_EVENT_RECORD وتخزينها في مخزن الإدخال المؤقت.
  • يتم إفراغ مخزن الإدخال المؤقت بسجل واحد في كل مرة ، وتنفيذ إجراءات الإدخال المطلوبة ، مثل عرض النص على الشاشة ، وتحريك المؤشر ، ونسخ / لصق النص ، إلخ. تغير العديد من هذه الإجراءات محتويات المخزن المؤقت للإخراج. يتم تسجيل هذه المناطق المتغيرة بواسطة محرك حالة ConHost.
  • في كل إطار ، تعرض وحدة التحكم المساحات المتغيرة من المخزن المؤقت للإخراج.

عندما يستدعي تطبيق سطر الأوامر Windows Console API ، يتم إجراء تسلسل لمكالمات API في رسائل IOCTL وإرسالها من خلال برنامج تشغيل ConDrv. ثم تقوم بتسليم رسائل IOCTL إلى وحدة التحكم المرفقة ، والتي تقوم بفك تشفير وتنفيذ مكالمة API المطلوبة. يتم إرجاع تسلسل القيم المرتجعة / الإخراج إلى رسالة IOCTL وإرسالها إلى التطبيق عبر ConDrv.

ConHost: المساهمة في الماضي من أجل المستقبل


تسعى Microsoft للمحافظة على التوافق مع الإصدارات السابقة مع التطبيقات والأدوات الموجودة كلما أمكن ذلك. خاصة بالنسبة لسطر الأوامر. في الواقع ، لا يزال بإمكان إصدارات 32 بت من Windows 10 تشغيل العديد / معظم تطبيقات Win16 والملفات التنفيذية 16 بت!

كما ذكر أعلاه ، فإن أحد أدوار ConHost الرئيسية هو تقديم الخدمات لتطبيقات سطر الأوامر ، خاصة التطبيقات القديمة التي تستدعي وتعتمد على واجهة برمجة تطبيقات وحدة التحكم Win32. يقدم ConHost الآن خدمات جديدة:

  • بنية تحتية سلسة تشبه PTY للتواصل مع وحدات التحكم والمحطات الطرفية الحديثة
  • ترقية تطبيقات سطر الأوامر التقليدية / القديمة
    • استلام وتحويل نص UTF-8 / VT إلى سجلات الإدخال (كما لو تم إدخاله من قبل المستخدم)
    • تستدعي واجهة برمجة تطبيقات وحدة التحكم إلى التطبيق المستضاف ، لتحديث المخزن المؤقت للإخراج وفقًا لذلك
    • عرض المناطق المعدلة من المخزن المؤقت للإخراج في ترميز UTF-8 ، نص / VT

فيما يلي مثال على كيفية تواصل تطبيق وحدة التحكم الحديثة مع تطبيق سطر الأوامر من خلال ConPTY ConHost.



في هذا النموذج الجديد:

  1. وحدة التحكم:
    1. ينشئ قنوات اتصال خاصة
    2. استدعاء ConPTY API لإنشاء ConPTY ، مما يجبر Windows على تشغيل مثيل ConHost المتصل بالطرف الآخر للقنوات
    3. لإنشاء مثيل لتطبيق سطر الأوامر (مثل PowerShell) المتصل بـ ConHost ، كالمعتاد
  2. Conhost:
    1. قراءة نص UTF-8 / VT عند الإدخال INPUT_RECORD إلى سجلات INPUT_RECORD ، التي يتم إرسالها إلى تطبيق سطر الأوامر
    2. إجراء مكالمات API من تطبيق سطر الأوامر الذي يمكنه تعديل محتويات المخزن المؤقت للإخراج
    3. عرض التغييرات في المخزن المؤقت للإخراج في ترميز UTF-8 (text / VT) وإرسال النص المستلم إلى وحدة التحكم الخاصة به
  3. تطبيق سطر الأوامر:
    1. يعمل كالمعتاد ، ويقرأ الإدخال ويستدعي واجهة برمجة تطبيقات وحدة التحكم ، دون أدنى فكرة أن ConPTY ConHost يترجم المدخلات والمخرجات من / إلى UTF-8!

اللحظة الأخيرة مهمة! عندما يستخدم تطبيق سطر الأوامر القديم مكالمات إلى وحدة تحكم API مثل WriteConsoleOutput(...) ، تتم كتابة النص المحدد إلى المخزن المؤقت للإخراج ConHost المقابل. بشكل دوري ، يعرض ConHost المناطق المتغيرة من المخزن المؤقت للإخراج كنص / VT ، والتي يتم إرسالها عبر stdout مرة أخرى إلى وحدة التحكم.

في النهاية ، حتى تطبيقات سطر الأوامر التقليدية من الخارج "تتحدث" النص / VT دون أي تغييرات !

باستخدام البنية التحتية الجديدة لـ ConPTY ، يمكن لوحدات تحكم الطرف الثالث الآن التفاعل بشكل مباشر مع تطبيقات سطر الأوامر الحديثة والتقليدية وتبادلها جميعًا في نص / VT.

التفاعل عن بعد مع تطبيقات سطر أوامر Windows


تعمل الآلية الموضحة أعلاه بشكل جيد على جهاز كمبيوتر واحد ، ولكنها تساعد أيضًا عند التفاعل ، على سبيل المثال ، مع مثيل PowerShell على جهاز كمبيوتر يعمل بنظام التشغيل Windows أو في حاوية.

هناك مشكلة عند بدء تشغيل تطبيق سطر الأوامر عن بُعد (أي على أجهزة الكمبيوتر البعيدة أو الخوادم أو الحاويات). والحقيقة هي أن تطبيقات سطر الأوامر على الأجهزة البعيدة تتواصل مع مثيل ConHost المحلي ، لأن رسائل IOCTL غير مصممة ليتم نقلها عبر الشبكة. كيفية نقل الإدخال من وحدة التحكم المحلية إلى الجهاز البعيد وكيفية الحصول على الإخراج من التطبيق قيد التشغيل هناك؟ علاوة على ذلك ، ماذا تفعل مع أجهزة Mac و Linux ، حيث توجد محطات طرفية ولكن لا توجد وحدات تحكم متوافقة مع Windows؟

وبالتالي ، من أجل التحكم عن بعد في جهاز Windows ، نحتاج إلى نوع من وسيط الاتصالات الذي يمكنه إجراء تسلسل للبيانات بشفافية عبر الشبكة ، وإدارة عمر مثيل التطبيق ، وما إلى ذلك.

ربما شيء مثل ssh ؟

لحسن الحظ ، تم نقل OpenSSH مؤخرًا إلى Windows وأضيف كخيار إضافي إلى Windows 10 . يستخدم PowerShell Core أيضًا ssh كواحد من بروتوكولات العمل عن بُعد PowerShell Core المدعومة. وبالنسبة لأولئك الذين يقومون بتشغيل Windows PowerShell ، لا يزال خيار Windows PowerShell عن بُعد خيارًا مقبولاً.

دعونا نرى كيف يسمح لك OpenSSH لـ Windows الآن بالتحكم عن بعد في قذائف Windows وتطبيقات سطر الأوامر:



يتضمن OpenSSH حاليًا بعض المضاعفات غير المرغوب فيها:

  1. المستخدم:
    1. يبدأ عميل ssh ، ويربط Windows مثيل وحدة التحكم كالمعتاد
    2. يدخل النص إلى وحدة التحكم ، والذي يرسل ضغطات المفاتيح إلى عميل ssh
  2. عميل ssh:
    1. يقرأ الإدخال كبايت من البيانات النصية
    2. يرسل البيانات النصية عبر الشبكة إلى خدمة الاستماع sshd
  3. تمر خدمة sshd بعدة مراحل:
    1. يشغل الصدفة الافتراضية (على سبيل المثال ، Cmd) ، مما يجبر Windows على إنشاء وتوصيل نسخة جديدة من وحدة التحكم
    2. يبحث عن وحدة تحكم مثيل Cmd ويتصل بها
    3. تحريك وحدة التحكم خارج الشاشة (و / أو إخفاءها)
    4. يرسل المدخلات الواردة من عميل ssh إلى وحدة التحكم خارج الشاشة كمدخل
  4. يعمل مثيل cmd كما هو الحال دائمًا:
    1. يجمع المدخلات من خدمة sshd
    2. هل العمل
    3. يستدعي واجهة برمجة تطبيقات وحدة التحكم لتقديم النص / النمط ، وتحريك المؤشر ، إلخ.
  5. وحدة التحكم المرفقة [خارج الشاشة]:
    1. إجراء مكالمات API عن طريق تحديث المخزن المؤقت للإخراج.
  6. خدمة Sshd:
    1. يزيل المخزن المؤقت للإخراج لوحدة التحكم خارج الشاشة ، ويجد الاختلافات ، ويرمزها إلى نص / VT ويرسل مرة أخرى ...
  7. عميل ssh يرسل النص ...
  8. وحدة تحكم تعرض النص

مرح ، صحيح؟ لا على الاطلاق! في هذه الحالة ، قد ينحرف الكثير ، خاصة في عملية محاكاة وإرسال مدخلات المستخدم ومسح المخزن المؤقت للإخراج لوحدة التحكم خارج الشاشة. وهذا يؤدي إلى عدم الاستقرار ، والأعطال ، وتلف البيانات ، والاستهلاك المفرط للطاقة ، إلخ. بالإضافة إلى ذلك ، لا تقوم جميع التطبيقات بمهمة إزالة ليس النص نفسه فحسب ، بل أيضًا خصائصه ، وهذا هو سبب فقدان التنسيق واللون!

العمل عن بعد باستخدام ConHost و ConPTY الحديث


بالتأكيد يمكننا تحسين الوضع؟ نعم ، بالطبع يمكننا - دعنا نجري بعض التغييرات المعمارية ونطبق ConPTY الجديد:



يوضح الرسم البياني أن الدائرة تغيرت على النحو التالي:

  1. المستخدم:
    1. يبدأ عميل ssh ، ويربط Windows مثيل وحدة التحكم كالمعتاد
    2. يدخل النص إلى وحدة التحكم ، والذي يرسل ضغطات المفاتيح إلى عميل ssh
  2. عميل ssh:
    1. يقرأ الإدخال كبايت من البيانات النصية
    2. يرسل البيانات النصية عبر الشبكة إلى خدمة الاستماع sshd
  3. خدمة Sshd:
    1. ينشئ قنوات stdin / stdout
    2. استدعاء API ConPTY لبدء ConPTY
    3. يبدأ تشغيل مثيل Cmd متصلاً بالنهاية الأخرى لـ ConPTY. يقوم Windows ببدء وتثبيت نسخة جديدة من ConHost
  4. يعمل مثيل cmd كما هو الحال دائمًا:
    1. يجمع المدخلات من خدمة sshd
    2. هل العمل
    3. يستدعي واجهة برمجة تطبيقات وحدة التحكم لتقديم النص / النمط ، وتحريك المؤشر ، إلخ.
  5. المثيل ConPTY ConHost:
    1. إجراء مكالمات API عن طريق تحديث المخزن المؤقت للإخراج.
    2. يعرض المناطق التي تم تغييرها من المخزن المؤقت للإخراج كنص مشفر UTF-8 / VT ، والذي يتم إرساله مرة أخرى إلى وحدة التحكم / المحطة الطرفية عبر ssh

من الواضح أن هذا النهج باستخدام ConPTY أكثر نظافة وبساطة لخدمة sshd. يتم إجراء المكالمات إلى Windows Console API بالكامل في مثيل ConHost لتطبيق سطر الأوامر ، والذي يحول جميع التغييرات المرئية إلى نص / VT. من يتصل بـ ConHost ، فإنه لا يحتاج إلى معرفة أن التطبيق هناك يستدعي API Console ، ولا يقوم بإنشاء نص / VT!

توافق على أن آلية التحكم عن بعد الجديدة ConPTY هذه تؤدي إلى بنية أنيقة ومتسقة وبسيطة. إلى جانب الميزات القوية المدمجة في ConHost ، دعم التطبيقات القديمة وعرض التغييرات من التطبيقات التي تستدعي واجهات برمجة تطبيقات وحدة التحكم كنص / VT ، تساعدنا البنية التحتية ConHost و ConPTY الجديدة على نقل الماضي إلى المستقبل.

ConPTY API وكيفية استخدامه


تتوفر ConPTY API في الإصدار الحالي من Windows 10 Insider Preview SDK .

الآن ، أنا متأكد من أنه لا يمكنك الانتظار لرؤية بعض الرموز ؛)

ألق نظرة على إعلانات API:

 // Creates a "Pseudo Console" (ConPTY). HRESULT WINAPI CreatePseudoConsole( _In_ COORD size, // ConPty Dimensions _In_ HANDLE hInput, // ConPty Input _In_ HANDLE hOutput, // ConPty Output _In_ DWORD dwFlags, // ConPty Flags _Out_ HPCON* phPC); // ConPty Reference // Resizes the given ConPTY to the specified size, in characters. HRESULT WINAPI ResizePseudoConsole(_In_ HPCON hPC, _In_ COORD size); // Closes the ConPTY and all associated handles. Client applications attached // to the ConPTY will also terminated. VOID WINAPI ClosePseudoConsole(_In_ HPCON hPC); 

يكشف API ConPTY أعلاه بشكل أساسي ثلاث وظائف جديدة للاستخدام:

  • CreatePseudoConsole(size, hInput, hOutput, dwFlags, phPC)
    • ينشئ pty مع البعد في الأعمدة w والخطوط h ، باستخدام القنوات التي أنشأها المتصل:
      • size : العرض والارتفاع (بالأحرف) للمخزن المؤقت ConPTY
      • hInput : لكتابة بيانات الإدخال إلى PTY كتسلسلات نصية / VT بترميز UTF-8
      • hOutput : لقراءة الإخراج من PTY كتسلسلات نصية / VT في ترميز UTF-8
      • dwFlags : القيم المحتملة:
        • PSEUDOCONSOLE_INHERIT_CURSOR : PSEUDOCONSOLE_INHERIT_CURSOR تم إنشاؤه أن يرث موضع المؤشر لتطبيق المحطة الطرفية الرئيسي
      • phPC : مقبض وحدة التحكم التي تم إنشاؤها بواسطة ConPty
    • العوائد : النجاح / الفشل. في حالة نجاح ذلك ، تحتوي phPC على مقبض ConPty الجديد

    ResizePseudoConsole(hPC, size)
    • تغيير حجم المخزن المؤقت الداخلي ConPTY لعرض عرض وارتفاع معينين

    ClosePseudoConsole (hPC)
    • ConPTY . , ConPTY, , ,

    ConPTY API


    ConPTY API ConPTY.

    : GitHub

      // Note: Most error checking removed for brevity. // ... // Initializes the specified startup info struct with the required properties and // updates its thread attribute list with the specified ConPTY handle HRESULT InitializeStartupInfoAttachedToConPTY(STARTUPINFOEX* siEx, HPCON hPC) { HRESULT hr = E_UNEXPECTED; size_t size; siEx->StartupInfo.cb = sizeof(STARTUPINFOEX); // Create the appropriately sized thread attribute list InitializeProcThreadAttributeList(NULL, 1, 0, &size); std::unique_ptr<BYTE[]> attrList = std::make_unique<BYTE[]>(size); // Set startup info's attribute list & initialize it siEx->lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>( attrList.get()); bool fSuccess = InitializeProcThreadAttributeList( siEx->lpAttributeList, 1, 0, (PSIZE_T)&size); if (fSuccess) { // Set thread attribute list's Pseudo Console to the specified ConPTY fSuccess = UpdateProcThreadAttribute( lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, hPC, sizeof(HPCON), NULL, NULL); return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError()); } else { hr = HRESULT_FROM_WIN32(GetLastError()); } return hr; } // ... HANDLE hOut, hIn; HANDLE outPipeOurSide, inPipeOurSide; HANDLE outPipePseudoConsoleSide, inPipePseudoConsoleSide; HPCON hPC = 0; // Create the in/out pipes: CreatePipe(&inPipePseudoConsoleSide, &inPipeOurSide, NULL, 0); CreatePipe(&outPipeOurSide, &outPipePseudoConsoleSide, NULL, 0); // Create the Pseudo Console, using the pipes CreatePseudoConsole( {80, 32}, inPipePseudoConsoleSide, outPipePseudoConsoleSide, 0, &hPC); // Prepare the StartupInfoEx structure attached to the ConPTY. STARTUPINFOEX siEx{}; InitializeStartupInfoAttachedToConPTY(&siEx, hPC); // Create the client application, using startup info containing ConPTY info wchar_t* commandline = L"c:\\windows\\system32\\cmd.exe"; PROCESS_INFORMATION piClient{}; fSuccess = CreateProcessW( nullptr, commandline, nullptr, nullptr, TRUE, EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr, &siEx->StartupInfo, &piClient); // ... 

    cmd.exe ConPTY, CreatePseudoConsole() . ConPTY / Cmd. ResizePseudoConsole() , — ClosePseudoConsole() .


    ConPTY :

     // Input "echo Hello, World!", press enter to have cmd process the command, // input an up arrow (to get the previous command), and enter again to execute. std::string helloWorld = "echo Hello, World!\n\x1b[A\n"; DWORD dwWritten; WriteFile(hIn, helloWorld.c_str(), (DWORD)helloWorld.length(), &dwWritten, nullptr); 


    , ConPTY:

     // Suppose some other async callback triggered us to resize. // This call will update the Terminal with the size we received. HRESULT hr = ResizePseudoConsole(hPC, {120, 30}); 


    ConPTY:

     ClosePseudoConsole(hPC); 

    : ConPTY ConHost .

    !


    ConPTY API — , , Windows … !

    ConPTY API Microsoft, Microsoft ( Windows Linux (WSL), Windows Containers, VSCode, Visual Studio .), , @ConEmuMaximus5ConEmu Windows.

    , ConPTY API.


    , : ConHost . Console API. , , .

    , VT, , — .

    , Windows, /VT UTF-8 Console API: « VT» , Console API (, 16M RGB True Color ).

    /


    / , ConPTY API: , , , , .

    VSCode ( GitHub #45693 ) , Windows.

    ConPTY API


    ConPTY API Windows 10 / 2018 .

    Windows, , , ConPTY. Win32 API, API Runtime Dynamic Linking LoadLibrary() GetProcAddress() .

    Windows ConPTY, API ConPTY. , , .

    , ?


    … ! , , ! : د

    , , , . — , Windows , .

    . Windows Console GitHub . , .

Source: https://habr.com/ru/post/ar420853/


All Articles