
بمجرد التفكير في كيفية تحويل برنامج نصي أو تطبيق إلى خدمة Windows. على الأرجح ، لن تكون المهمة تافهة للغاية - على الأقل سيحتاج التطبيق إلى واجهة خاصة لتلقي الأوامر من النظام. وبما أن هناك متطلبات وقيود ، أي مخطوطات ، وعكازات ، جميلة للقلب ، للتغلب عليها.
ستكون المقالة مفيدة لأولئك الذين هم مثلي "ليسوا مبرمجين حقيقيين".
لماذا أحتاج إلى خدمة إذا كانت هناك مهام مجدولة
على عكس المهام المعينة ، تعمل الخدمة باستمرار ، وتبدأ في بداية الكمبيوتر الشخصي ويمكن التحكم فيها بواسطة أدوات Windows. قد يحتاج البرنامج النصي الذي يتم تشغيله بانتظام أيضًا إلى بيانات من إطلاق سابق ، وقد يكون من المفيد الحصول على البيانات من مصادر خارجية - على سبيل المثال ، في حالة TCP أو خادم الويب.
أنا شخصياً اضطررت خلال السنوات الخمس الماضية إلى إنشاء خدمة ثلاث مرات ونصف:
- كان مطلوبًا إنشاء خدمة على fail2ban لنظام التشغيل Windows 2003. عملت مع سجلات FileZilla و Apache ، وإذا كان هناك شك في وجود قوة وحشية ، فقد حظرت IP باستخدام أدوات Windows القياسية - ipsec.
- تمثيلية لخادم telnet للإصدارات المنزلية من Windows. استغرق الأمر تنفيذ أوامر على محطات العمل البعيدة التي كانت تعمل بنظام Windows 7 Home. في الواقع ، المحاولة الثانية للعب الخدمات.
- مشغل موسيقى لغرفة التداول تحت Windows. يمكن حل المهمة المتعلقة بالمعارف التقليدية بمساعدة mpd ومجموعة من البرامج النصية ، لكنني قررت - إذا أردنا أن نصنع نصوصًا ، فلماذا لا "كومة" اللاعب بنفسي. بناء على مكتبة BASS.dll .
- عند اختيار خادم ويب مع دعم لتنزيل الملفات ضمن Windows ، كان أحد الخيارات HFS . لا يستطيع العمل بمفرده ، لذلك كان عليه "دفعه" إلى الخدمة. ونتيجة لذلك ، لم يعجبني الحل ، وقمت فقط بتثبيت سمة Apaxy على خادم الويب Apache.
لإنشاء الخدمة ، يمكنك استخدام لغات برمجة الكبار مثل C. ولكن إذا كنت لا تريد الاتصال بـ Visual Studio ، فاستغل أدوات مساعدة جاهزة. هناك حلول مدفوعة مثل FireDaemon Pro أو AlwaysUp ، لكننا سنركز تقليديًا على الحلول المجانية.
الطريقة الأولى. من مايكروسوفت
تتكون هذه الآلية القديمة بالفعل من مكونين: الأداة المساعدة instsrv.exe لتثبيت الخدمة و srvany.exe ، وهي عملية تشغيل أي ملفات قابلة للتنفيذ. لنفترض أننا أنشأنا خادم ويب على PowerShell باستخدام وحدة Polaris . سيكون البرنامج النصي بسيطًا للغاية:
New-PolarisGetRoute -Path '/helloworld' -Scriptblock { $Response.Send('Hello World!') } Start-Polaris -Port 8080 while($true) { Start-Sleep -Milliseconds 10 }

عمل ما يسمى "الخادم".
الآن دعنا نحاول تحويل البرنامج النصي إلى خدمة. للقيام بذلك ، قم بتنزيل أدوات Windows Resource Kit ، حيث ستكون أدواتنا المساعدة. لنبدأ بتثبيت خدمة فارغة بالأمر:
instsrv WebServ C:\temp\rktools\srvany.exe
حيث WebServ هو اسم خدمتنا الجديدة. إذا لزم الأمر ، من خلال الأداة الإضافية services.msc ، يمكنك تحديد المستخدم الذي سيتم تشغيل الخدمة بموجبه والسماح بالتفاعل مع سطح المكتب.
الآن دعنا نكتب المسار إلى برنامجنا النصي باستخدام سحر التسجيل. توجد معلمات الخدمة في مفتاح التسجيل HKLM \ SYSTEM \ CurrentControlSet \ Services \ WebServ . في ذلك ، نحتاج إلى إضافة معلمات قسم جديد وإنشاء هناك معلمة سلسلة Application ، تشير فيه إلى المسار إلى الملف القابل للتنفيذ. في حالة البرنامج النصي PowerShell ، سيبدو كما يلي:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -NoProfile -File C:\temp\Polaris\server.ps1

خدمة مخصصة.
يمكنك الركض والاستمتاع.

خدمة العمل.
ومع ذلك ، فإن هذه الطريقة لها عيوب:
- المرافق قديمة ، تم تطويرها قبل اختراع PowerShell ، UAC وأشياء أخرى.
- لا تتحكم Srvany في تشغيل التطبيق. حتى في حالة وقوع خطأ ، ستواصل الخدمة عملها كما لو لم يحدث شيء.
- سوف تضطر إلى ضبط والخوض في التسجيل. هل تتذكر أن الحفر في التسجيل غير آمن؟
لذلك ، ننتقل إلى طريقة خالية جزئيا من هذه المشاكل.
الطريقة الثانية ، ما يقرب من شخص بالغ
هناك أداة مساعدة تسمى NSSM - Non-Sucking Service Manager ، والتي يمكن ترجمتها كمدير خدمة غير سيء . على عكس السابق ، يتم دعمه من قبل المطور ، ويتم نشر شفرة المصدر على الموقع. بالإضافة إلى الطريقة المعتادة ، يتوفر التثبيت أيضًا من خلال مدير الحزم Chocolately .
يمكنك إنشاء خدمة من سطر الأوامر المعتاد ، مسلحًا بالوثائق الموجودة على موقع المطور. لكننا سنستخدم PowerShell. لأننا نستطيع بالطبع.
$nssm = (Get-Command ./nssm).Source $serviceName = 'WebServ' $powershell = (Get-Command powershell).Source $scriptPath = 'C:\temp\Polaris\server.ps1' $arguments = '-ExecutionPolicy Bypass -NoProfile -File "{0}"' -f $scriptPath & $nssm install $serviceName $powershell $arguments & $nssm status $serviceName Start-Service $serviceName Get-Service $serviceName

التثبيت من خلال PowerShell.
لإجراء تغيير ، سوف نتحقق من عمل الخدمة ليس مع المتصفح ، ولكن أيضًا من خلال PowerShell باستخدام الأمر Invoke-RestMethod.

ويعمل حقا.
على عكس srvany ، تسمح لك هذه الطريقة بإعادة تشغيل التطبيق عند بدء التشغيل ، وإعادة توجيه stdin و stdout وأكثر من ذلك بكثير. على وجه الخصوص ، إذا كنت لا ترغب في كتابة أوامر على سطر الأوامر ، فقم فقط بتشغيل واجهة المستخدم الرسومية وإدخال المعلمات الضرورية من خلال واجهة ملائمة.
يبدأ GUI بالأمر:
nssm.exe install ServiceName

يمكنك حتى تكوين أولوية واستخدام نوى المعالج.
في الواقع ، هناك فرص أكثر بكثير من srvany وعدد من نظائرها الأخرى. من السلبيات ، عدم كفاية السيطرة على العملية برمتها ملفتة للنظر.
هناك نقص في القصدير. لذلك ، سأنتقل إلى الطريقة الأكثر صلابة لجميع اختبارها.
الطريقة الثالثة. أوتويت
نظرًا لأنني من محبي لغة البرمجة هذه لفترة طويلة ، لم أتمكن من تجاوز المكتبة المسماة _Services_UDF v4 . وهي مجهزة بوثائق وأمثلة غنية ، لذا سأعطي تحت النص المفسد النص الكامل للسيناريو الناتج على الفور.
قائمة البرامج النصيةلذا ، دعنا نحاول "تضمين" خدمة الويب لدينا في ذلك:
#NoTrayIcon #RequireAdmin #Region #AutoIt3Wrapper_Version=Beta #AutoIt3Wrapper_UseUpx=n #AutoIt3Wrapper_Compile_Both=y #AutoIt3Wrapper_UseX64=y #EndRegion Dim $MainLog = @ScriptDir & "\test_service.log" #include <services.au3> #include <WindowsConstants.au3> $sServiceName="WebServ" If $cmdline[0] > 0 Then Switch $cmdline[1] Case "install", "-i", "/i" InstallService() Case "remove", "-u", "/u", "uninstall" RemoveService() Case Else ConsoleWrite(" - - - Help - - - " & @CRLF) ConsoleWrite("params : " & @CRLF) ConsoleWrite(" -i : install service" & @CRLF) ConsoleWrite(" -u : remove service" & @CRLF) ConsoleWrite(" - - - - - - - - " & @CRLF) Exit EndSwitch Else _Service_init($sServiceName) Exit EndIf Func _main($iArg, $sArgs) If Not _Service_ReportStatus($SERVICE_RUNNING, $NO_ERROR, 0) Then _Service_ReportStatus($SERVICE_STOPPED, _WinAPI_GetLastError(), 0) Exit EndIf $bServiceRunning = True $PID=Run("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -NoProfile -File C:\temp\Polaris\server.ps1") While $bServiceRunning _sleep(1000) WEnd ProcessClose($PID) _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 1000) DllCallbackFree($tServiceMain) DllCallbackFree($tServiceCtrl) _Service_ReportStatus($SERVICE_STOPPED, $NO_ERROR, 0) DllClose($hAdvapi32_DLL) DllClose($hKernel32_DLL) EndFunc Func _Sleep($delay) Local $result = DllCall($hKernel32_DLL, "none", "Sleep", "dword", $delay) EndFunc Func InstallService() #RequireAdmin Local $bDebug = True If $cmdline[0] > 1 Then $sServiceName = $cmdline[2] EndIf If $bDebug Then ConsoleWrite("InstallService("&$sServiceName &"): Installing service, please wait") _Service_Create($sServiceName, $sServiceName, $SERVICE_WIN32_OWN_PROCESS, $SERVICE_AUTO_START, $SERVICE_ERROR_SEVERE, '"' & @ScriptFullPath & '"');,"",False,"","NT AUTHORITY\NetworkService") If @error Then Msgbox("","","InstallService(): Problem installing service, Error number is " & @error & @CRLF & " message : " & _WinAPI_GetLastErrorMessage()) Else If $bDebug Then ConsoleWrite("InstallService(): Installation of service successful") EndIf Exit EndFunc Func RemoveService() _Service_Stop($sServiceName) _Service_Delete($sServiceName) If Not @error Then EndIf Exit EndFunc Func _exit() _Service_ReportStatus($SERVICE_STOPPED, $NO_ERROR, 0); EndFunc Func StopTimer() _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, $iServiceCounter) $iServiceCounter += -100 EndFunc Func _Stopping() _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 3000) EndFunc
سأحلل بمزيد من التفصيل لحظة بدء التطبيق. تبدأ بعد العملية $ bServiceRunning = True وتتحول إلى حلقة لا نهائية على ما يبدو. في الواقع ، ستتم مقاطعة هذه العملية بمجرد أن تتلقى الخدمة إشارة إكمال - سواء كانت تقوم بتسجيل الخروج أو تتوقف يدويًا.
نظرًا لأن برنامج النص البرمجي خارجي (powershell.exe) ، فبعد الخروج من الحلقة ، نحتاج إلى إنهاء عمله باستخدام ProcessClose .
للقيام بذلك ، يجب أن يتم ترجمة البرنامج النصي إلى. exe ، ثم تثبيت الخدمة عن طريق تشغيل exe مع -i التبديل .

يعمل!
بالطبع ، هذه الطريقة ليست الأكثر ملاءمة ، ويجب تنفيذ جميع الميزات الإضافية بشكل مستقل ، سواء أكان إعادة تشغيل التطبيق في حالة حدوث فشل أو دوران السجل. لكنه يعطي السيطرة الكاملة على ما يحدث. وفي النهاية ، يمكنك القيام بالمزيد - من إخطار Telegram حول فشل الخدمة إلى تفاعل IPC مع البرامج الأخرى. بالإضافة إلى ذلك - بلغة البرمجة النصية ، بدون تثبيت Visual Studio وتعلمه.
أخبرنا ، هل كان عليك تحويل البرامج النصية والتطبيقات إلى خدمات؟