القائمة النموذجية لـ PowerShell: وصول سريع إلى النصوص الخاصة بك

وقت مشروع نهاية الأسبوع مرة أخرى واليوم ستتعلم كيفية بناء قائمة نموذج PowerShell خفيفة الوزن في صينية النظام حيث يمكنك بسرعة وسهولة تشغيل نصوص PowerShell الأكثر رغبة لديك. يمكنك رؤية النتيجة النهائية أدناه.

Launching PowerShell scripts via a system tray menu icon

في هذه المقالة، ستتعلم كيفية بناء واجهة مستخدم النصوص الخاصة بك بواسطة PowerShell عن طريق تقسيم العملية إلى خطوات.

متطلبات البيئة والمعرفة

قبل أن تنغمس، يرجى التأكد من تلبية الحد الأدنى من المتطلبات التالية:

  • Windows 7 أو أحدث
  • Windows PowerShell 3 أو أحدث – قد يعمل أحدث إصدار من .NET Core 3.0 مع PowerShell 7 المعاينة على Windows بسبب الدعم المضاف حديثًا لـ WPF و WinForm، ولكنه لم يتم اختباره.
  • .NET Framework 4.5 أو أحدث
  • A familiarity with Windows Forms (WinForms)  You can, however, due this with WPF too though.

لهذا المشروع، الخبر السار هو أنك لن تحتاج حقًا إلى الاعتماد على Visual Studio، PoshGUI، أو أي أداة تطوير واجهة مستخدم أخرى كأن العناصر الأساسية التي سيعتمد عليها هذا المشروع هي الآتية:

  • NotifyIcon – سيمثل هذا أيقونة صينية النظام قابلة للتخصيص للمستخدم للتفاعل معها.
  • قائمة السياق – حاوية عندما يقوم المستخدم بالنقر بزر الماوس الأيمن على أيقونة العلبة.
  • عنصر القائمة – كائنات فردية لكل خيار ضمن قائمة النقر بزر الماوس الأيمن.

افتح محرر النصوص الخاص بك في PowerShell المفضل لديك ولنبدأ!

لهذا المشروع، ستقوم ببناء ثلاث وظائف: وظيفتان لإظهار/إخفاء النافذة لتوفير تجربة مستخدم أنظف ووظيفة واحدة لإضافة عناصر إلى قائمة النظام الخاصة بك. ستكون هذه الوظائف أساسًا للاستخدام لاحقًا لتسهيل حياتك كما ستتعلم قليلاً في هذا المقال.

إظهار/إخفاء نافذة الكونسول

ما لم يتم إخفاؤه، عندما تقوم بتشغيل نص PowerShell، ستظهر نافذة الكونسول PowerShell المألوفة. نظرًا لأن عناصر القائمة في النموذج PowerShell الذي ستقوم بإنشائه ستشغل النصوص، يجب عليك التأكد من أن الكونسول لا تظهر. تريد فقط تنفيذها.

عند تنفيذ نص، يمكنك تبديل إظهار أو عدم إظهار نافذة الكونسول PowerShell باستخدام .NET الصغير.

أضف أولاً نوع الـ .NET Window إلى الجلسة الحالية. للقيام بذلك، ستستخدم بعض C# كما سترى أدناه. الطريقتين التي تحتاج إلى تحميلهما في السياق هما GetConsoleWindow و ShowWindow. من خلال تحميل هذه المكتبات الديناميكية في الذاكرة، أنت تكشف عن أجزاء معينة من واجهة البرمجة، مما يتيح لك استخدامها في سياق سيناريو PowerShell الخاص بك:

 # قم بتحميل المكتبات الديناميكية في سياق جلسة الكونسول الحالية
 Add-Type -Name Window -Namespace Console -MemberDefinition '
    [DllImport("Kernel32.dll")]
    public static extern IntPtr GetConsoleWindow();
 
    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);

أنشئ وظيفتين باستخدام الأساليب المحملة أعلاه باستخدام GetConsoleWindow() و ShowWindow() كما هو موضح أدناه.

 function Start-ShowConsole {
    $PSConsole = [Console.Window]::GetConsoleWindow()
    [Console.Window]::ShowWindow($PSConsole, 5)
 }
 
 function Start-HideConsole {
    $PSConsole = [Console.Window]::GetConsoleWindow()
    [Console.Window]::ShowWindow($PSConsole, 0)
 }

باستخدام هاتين الوظيفتين، لديك الآن وسيلة يمكنك من خلالها إظهار أو إخفاء نافذة الكونسول حسب الرغبة.

ملحوظة: إذا كنت ترغب في رؤية الإخراج من السيناريوهات التي تم تنفيذها عبر القائمة، يمكنك استخدام سجلات PowerShell أو ميزات تسجيل النصوص الأخرى. يتيح لك ذلك السيطرة مقابل تشغيل جلسة PowerShell فقط بمعلمة WindowStyle للإخفاء.

ابدأ الآن في بناء شيفرة السيناريو عن طريق استدعاء Start-HideConsole. عند تنفيذ سيناريو PowerShell الذي يعتمد على القائمة، سيضمن ذلك أن نافذة وحدة التحكم في PowerShell لا تظهر.

<#
تهيئة الوظائف وتحميل الكائنات في الذاكرة
عرض شريط التحميل بناءً على النص أو كتابة تقدم إلى المضيف
#>
 
Start-HideConsole
 
<#
الكود لعرض نافذة النظام / الرمز في شريط المهام الخاص بك
سيحتفظ بالوحدة النافذة هنا حتى تُغلق
#>

إنشاء خيارات القائمة

حان الوقت الآن لإنشاء خيارات القائمة. من أجل ضمان إمكانية إنشاء خيارات جديدة بسهولة لاحقًا، قم بإنشاء وظيفة أخرى هذه المرة تُسمى New-MenuItem. عند استدعاء هذه الوظيفة، ستقوم بإنشاء كائن MenuItem جديد .NET يمكنك بعد ذلك إضافته إلى القائمة لاحقًا.

سيقوم كل خيار في القائمة بتشغيل سكريبت آخر أو الخروج من المشغل. لتوفير هذه الوظيفة، تحتوي الوظيفة New-MenuItem على ثلاثة معلمات:

  • Text – التسمية التي سينقر عليها المستخدم
  • MyScriptPath – المسار إلى النص البرمجي PowerShell للتنفيذ
  • ExitOnly – الخيار للخروج من المشغل.

أضف مقتطف الوظيفة التالي إلى نص القائمة.

 function New-MenuItem{
     param(
         [string]
         $Text = "Placeholder Text",
 
         $MyScriptPath,
         
         [switch]
         $ExitOnly = $false
     )

مواصلة بناء الوظيفة New-MenuItem، قم بإنشاء كائن MenuItem عن طريق تعيينه إلى متغير.

 #التهيئة
 $MenuItem = New-Object System.Windows.Forms.MenuItem

بعد ذلك، قم بتعيين التسمية النصية لعنصر القائمة.

 # قم بتطبيق النص المطلوب
 if($Text) {
 	$MenuItem.Text = $Text
 }

الآن أضف خاصية مخصصة إلى MenuItem تسمى MyScriptPath. سيتم استدعاء هذا المسار عند النقر فوق العنصر في القائمة.

 #تطبيق منطق حدث النقر
 if($MyScriptPath -and !$ExitOnly){
 	$MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty

أضف حدث النقر إلى MenuItem الذي يطلق النص الذي تريده. يوفر Start-Process طريقة نظيفة للقيام بذلك ضمن كتلة try/catch بحيث يمكنك التأكد من أن أي أخطاء في تشغيل النص (مثل عدم توفر PowerShell أو عدم وجود النص في المسار المحدد) تتوجه إلى كتلة الـ catch الخاصة بك.

   $MenuItem.Add_Click({
        try{
            $MyScriptPath = $This.MyScriptPath #يستخدم للعثور على المسار الصحيح أثناء حدث النقر
            
            if(Test-Path $MyScriptPath){
                Start-Process -FilePath "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ArgumentList "-NoProfile -NoLogo -ExecutionPolicy Bypass -File `"$MyScriptPath`"" -ErrorAction Stop
            } else {
                throw "Could not find at path: $MyScriptPath"
            }
        } catch {
          $Text = $This.Text
          [System.Windows.Forms.MessageBox]::Show("Failed to launch $Text`n`n$_") > $null
        }
  })

أضف المنطق المتبقي لتوفير شرط الخروج للمشغل تليها إرجاع MenuItem الجديد الخاص بك ليتم تعيينه إلى متغير آخر في وقت التشغيل.

    #توفير طريقة للخروج من المشغل
    if($ExitOnly -and !$MyScriptPath){
        $MenuItem.Add_Click({
            $Form.Close()
    
            #معالجة أي عمليات معلقة
            Stop-Process $PID
        })
    }
 
 	 #إرجاع MenuItem الجديد الخاص بنا
    $MenuItem
 }

يجب أن يكون لديك الآن الوظيفة New-MenuItem تم إنشاؤها! يجب أن تبدو الوظيفة النهائية مثل هذا:

 function New-MenuItem{
     param(
         [string]
         $Text = "Placeholder Text",
 
         $MyScriptPath,
         
         [switch]
         $ExitOnly = $false
     )
 
     #تهيئة
     $MenuItem = New-Object System.Windows.Forms.MenuItem
 
     #تطبيق النص المرغوب
     if($Text){
         $MenuItem.Text = $Text
     }
 
     #تطبيق منطق حدث النقر
     if($MyScriptPath -and !$ExitOnly){
         $MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty
     }
 
     $MenuItem.Add_Click({
             try{
                 $MyScriptPath = $This.MyScriptPath #تستخدم للعثور على مسار مناسب أثناء حدث النقر
             
                 if(Test-Path $MyScriptPath){
                     Start-Process -FilePath "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ArgumentList "-NoProfile -NoLogo -ExecutionPolicy Bypass -File `"$MyScriptPath`"" -ErrorAction Stop
                 } else {
                     throw "Could not find at path: $MyScriptPath"
                 }
             } catch {
                 $Text = $This.Text
                 [System.Windows.Forms.MessageBox]::Show("Failed to launch $Text`n`n$_") > $null
             }
         })
 
     #توفير طريقة للخروج من المشغل
     if($ExitOnly -and !$MyScriptPath){
         $MenuItem.Add_Click({
                 $Form.Close()
    
                 #التعامل مع أي عمليات معلقة
                 Stop-Process $PID
             })
     }
 
     #إرجاع عنصر القائمة الجديد الخاص بنا
     $MenuItem
 }

اختبار وظيفة New-MenuItem عن طريق نسخ ولصق الكود أعلاه في نافذة تحكم PowerShell الخاصة بك وتشغيل الوظيفة مع توفير بعض قيم المعلمات الوهمية. سترى أنه يتم إرجاع كائن MenuItem من .NET.

 PS51> (New-MenuItem -Text "Test" -MyScriptPath "C:\test.ps1").GetType()
 
 IsPublic IsSerial Name                                     BaseType
 -------- -------- ----                                     --------
 True     False    MenuItem                                 System.Windows.Forms.Menu

إنشاء نموذج مشغل

هل ترغب في المزيد من النصائح مثل هذه؟ تحقق من مدونتي الشخصية لـ PowerShell على: https://nkasco.com/FriendsOfATA

الآن بما أنه يمكنك بسهولة إنشاء عناصر قائمة جديدة، حان الوقت لإنشاء مشغل لشريط النظام الذي سيعرض القائمة.

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

 #إنشاء نموذج ليكون كحاوية لمكوناتنا
 $Form = New-Object System.Windows.Forms.Form
 ​
 #تكوين نموذجنا ليكون مخفيًا
 $Form.BackColor = "Magenta" #تطابق هذا اللون مع خاصية TransparencyKey للشفافية في نموذجك
 $Form.TransparencyKey = "Magenta"
 $Form.ShowInTaskbar = $false
 $Form.FormBorderStyle = "None"

بعد ذلك، قم بإنشاء الرمز الذي سيظهر في علبة النظام. فيما يلي الرمز الذي اخترته لاستخدامه PowerShell. في وقت التشغيل، يقوم الكود أدناه بإنشاء رمز فعلي في علبة النظام. يمكن تخصيص هذا الرمز حسب رغبتك عن طريق تعيين متغير SystrayIcon إلى الرمز المرغوب.

تحقق من وثائق فئة System.Drawing.Icon لرؤية طرق أخرى يمكنك من خلالها تحميل رمز إلى الذاكرة.

 # قم بتهيئة وتكوين المكونات اللازمة
 $SystrayLauncher = New-Object System.Windows.Forms.NotifyIcon
 $SystrayIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe")
 $SystrayLauncher.Icon = $SystrayIcon
 $SystrayLauncher.Text = "PowerShell Launcher"
 $SystrayLauncher.Visible = $true

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

الآن، قم بإنشاء حاوية لعناصر القائمة الخاصة بك باستخدام كائن ContextMenu جديد وقم بإنشاء جميع عناصر القائمة الخاصة بك. لهذا المثال، ستحتوي القائمة على سكريبتين للتشغيل وخيار للخروج.

 $ContextMenu = New-Object System.Windows.Forms.ContextMenu
 ​
 $LoggedOnUser = New-MenuItem -Text "Get Logged On User" -MyScriptPath "C:\scripts\GetLoggedOn.ps1"
 $RestartRemoteComputer = New-MenuItem -Text "Restart Remote PC" -MyScriptPath "C:\scripts\restartpc.ps1"
 $ExitLauncher = New-MenuItem -Text "Exit" -ExitOnly

بعد ذلك، أضف جميع عناصر القائمة التي تم إنشاؤها إلى قائمة السياق. سيتأكد ذلك من ظهور كل خيار في قائمة النموذج.

 # أضف عناصر القائمة إلى قائمة السياق
 $ContextMenu.MenuItems.AddRange($LoggedOnUser)
 $ContextMenu.MenuItems.AddRange($RestartRemoteComputer)
 $ContextMenu.MenuItems.AddRange($ExitLauncher)# أضف المكونات إلى النموذج الخاص بنا
 $SystrayLauncher.ContextMenu = $ContextMenu

أظهر نموذج الإطلاق

الآن بعد أن تم الانتهاء من النموذج، آخر شيء يجب فعله هو عرضه مع التأكد من عدم ظهور نافذة وحدة التحكم PowerShell. قم بذلك باستخدام Start-HideConsole، عرض نموذج الإطلاق، ثم عرض وحدة التحكم مرة أخرى باستخدام Start-ShowConsole لتجنب تعليق عملية powershell.exe.

#الإطلاق
Start-HideConsole
$Form.ShowDialog() > $null
Start-ShowConsole

هل ترغب في المزيد من النصائح مثل هذه؟ تفضل بزيارة مدونتي الشخصية حول PowerShell على: https://nkasco.com/FriendsOfATA

يمكن العثور على الشيفرة الكاملة هنا: https://github.com/nkasco/PSSystrayLauncher

ملخصك

تهانينا، لقد أكملت هذا المشروع! في هذا المقال تعلمت:

  1. كيفية كشف مكونات واجهة برمجة التطبيقات في نظام Windows.
  2. كيفية العمل مع قوائم السياق عبر WinForms وإضافة عناصر القائمة الفرعية.
  3. كيفية إنشاء رمز في علبة النظام باستخدام PowerShell.

يجب أن يمنحك هذا المشروع فهمًا كافيًا وتجربة لإنشاء قائمة systray الخاصة بك لنصوص PowerShell!

Source:
https://adamtheautomator.com/powershell-form/