لماذا لا تُنفَّذ مهامّ Task Scheduler أو تنتهي بالرمز 0x1 ── تشخيص الأسباب وتصميم تشغيل آمن
· آخر تحديث: · غو كومورا · Task Scheduler, Windows, PowerShell, أتمتة الأعمال, معالجة الدُفعات, التشغيل, استكشاف الأخطاء وإصلاحها, الاستشارات التقنية
سجل التعديلات (2 تحديثات، آخر تحديث 2 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621591)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
غو كومورا (2026). لماذا لا تُنفَّذ مهامّ Task Scheduler أو تنتهي بالرمز 0x1 ── تشخيص الأسباب وتصميم تشغيل آمن. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621591 https://comcomponent.com/ar/blog/windows-task-scheduler-reliable-scheduled-tasks/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621591
- DOI (هذه النسخة)
- 10.5281/zenodo.22240981
«أريد تشغيل سكربت التجميع الذي أنشأته بـ PowerShell كلّ صباح عند الساعة 6»، «يعمل عند التشغيل يدويّاً، لكنّه لا يعمل عند تحميله على Task Scheduler». عند تلقّي استشارات حول أتمتة الأعمال، ينتهي الأمر تقريباً دائماً بهذا الحديث.
في هذه المدوّنة أيضاً، تناولنا مواضيع الأتمتة تباعاً: أتمتة تنظيم السجلّات، واختبار السكربتات عبر Pester، وتنفيذ PowerShell من C#، وأتمتة الأعمال عبر Power Automate. جميع هذه المقالات تفترض في النهاية «التشغيل الدوريّ عبر Task Scheduler». لكن Task Scheduler نفسه آليّة ذات طبائع غريبة أكثر ممّا يُتوقَّع، وحوادث من نوع «يعمل يدويّاً لكن يفشل في التشغيل الدوريّ» أو «توقّف دون أن يلاحظ أحد» لا تنقطع.
في هذا المقال، نرتّب الأجزاء من آليّة Task Scheduler التي ترتبط مباشرةً بحوادث التشغيل ── حساب التشغيل ونوع تسجيل الدخول، وتشخيص حالة «عدم التنفيذ»، والسبب النمطيّ لقيمة الإرجاع 0x1، وطريقة حفظ السجلّات، والتحكّم في التشغيل المتعدّد ── بالترتيب الذي يكثر التعثّر فيه في العمل الفعليّ.
مقدّمات هذا المقال
| البند | المحتوى |
|---|---|
| نظام التشغيل المستهدَف | نفترض Task Scheduler (سلسلة Task Scheduler 2.0) على Windows 10 / 11 وWindows Server 2016 فما بعده. إن بقيت مهامّ أقدم موروثة من أمر at، فابدأ بجردها أولاً |
| القرّاء المفترضون | مسؤولو أنظمة داخليّة ومطوّرون يكتبون PowerShell أو دُفعات، لكنّهم يتعثّرون عند تحميلها على التشغيل الدوريّ |
| العمليّات | نعالج الواجهة الرسوميّة (taskschd.msc) ووحدة PowerShell ScheduledTasks معاً. لا نورد لقطات شاشة. بدلاً من ذلك نكتب أسماء علامات التبويب والبنود والأزرار بصيغتها الفعليّة، فاقرأ والمجدول مفتوح أمامك |
| افتراض بيئة النطاق | gMSA (القسم 3.3) حديث مقصور على بيئة النطاق. في بيئة مجموعة عمل (workgroup) تجاوزه |
1. الخلاصة أوّلاً
- معظم مشاكل Task Scheduler لا تنشأ من السكربت نفسه، بل من عدم فهم «كمن، وفي أيّ جلسة يُنفَّذ». بمجرّد اختيار «التشغيل بغضّ النظر عن تسجيل دخول المستخدم»، صمِّم على افتراض أنّه يعمل في عالم مختلف تماماً عن الجلسة التفاعليّة وبيئة تسجيل الدخول.1
- تحقيق حالة «عدم التنفيذ» ينطلق من علامة تبويب السجلّ (History) وسجلّ الأحداث (Event Log). لكن سجلّ المهمّة معطَّل افتراضيّاً، لذا يجب تفعيل «تفعيل سجلّ جميع المهامّ» قبل التشغيل الفعليّ.2
- الرمز
0x1الذي يظهر في «نتيجة التشغيل الأخيرة» ليس خطأً من Task Scheduler، بل يعني أنّ البرنامج الذي تمّ تشغيله أعاد رمز خروج قيمته 1. السبب في جانب السكربت، لذا صمِّم رمز الخروج وابنِ آليّة لحفظ السجلّ بنفسك أوّلاً.3 - الصيغة الأساسيّة لاستدعاء سكربت PowerShell هي
-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "المسار الكامل"، مع بناء المسارات داخل السكربت على$PSScriptRoot. هناك أيضاً فخّ كلاسيكيّ وهو عدم جواز وضع علامات اقتباس في حقل «البدء (اختياريّ)». - لتفادي حادثة توقّف المهمّة صامتة بسبب تغيير كلمة المرور، لا بدّ من تصميم حساب التشغيل (جرد حسابات الخدمة، والنظر في gMSA لبيئة النطاق).4
- التحكّم في التشغيل المتعدّد (الافتراضيّ هو «عدم بدء نسخة جديدة»)، وحدّ مدّة التنفيذ (الافتراضيّ 3 أيّام)، وشرط الطاقة (الافتراضيّ AC فقط)، كلّها إعدادات نموذجيّة يستمرّ التشغيل بها على قيمها الافتراضيّة دون أن يُلاحَظ. حدِّدها صراحةً عند التسجيل دائماً.5
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 19، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
2. بنية Task Scheduler ── المشغِّل والعمليّة والشروط والإعدادات
تتكوّن مهمّة Task Scheduler من 4 عناصر رئيسيّة.
| العنصر | المحتوى | نقطة قد تسبّب حادثاً |
|---|---|---|
| المشغِّل (Trigger) | متى يبدأ التشغيل (وقت، عند تسجيل الدخول، عند وقوع حدث، وما إلى ذلك) | كيفيّة التعامل مع فوات مشغِّل الوقت (StartWhenAvailable المذكور لاحقاً) |
| العمليّة (Action) | ما الذي يُنفَّذ (البرنامج، المعطيات، مجلّد البدء) | أخطاء في وضع علامات الاقتباس على المعطيات، أو في تحديد مجلّد البدء |
| الشروط (Conditions) | هل الوضع مناسب للتنفيذ (الطاقة، الشبكة، الخمول) | تفعيل «AC فقط» افتراضيّاً |
| الإعدادات (Settings) | السلوك أثناء التنفيذ (التشغيل المتعدّد، حدّ الوقت، إعادة المحاولة) | بدء التشغيل الفعليّ دون التحقّق من القيم الافتراضيّة |
يمكن تصدير المهمّة المُنشَأة عبر الواجهة الرسوميّة (taskschd.msc) كملفّ XML. إن أردتَ إدارة تعريف المهمّة عبر Git، أو توزيع نفس المهمّة على عدّة أجهزة، يُنصَح بجعلها سكربتاً عبر تصدير XML مع schtasks /Create /XML، أو عبر وحدة PowerShell ScheduledTasks (New-ScheduledTaskAction / New-ScheduledTaskTrigger / New-ScheduledTaskSettingsSet / Register-ScheduledTask).5
$action = New-ScheduledTaskAction -Execute 'pwsh.exe' `
-Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Jobs\Cleanup-Logs.ps1"' `
-WorkingDirectory 'C:\Jobs'
$trigger = New-ScheduledTaskTrigger -Daily -At '06:00'
# القيمة الافتراضيّة لشرط الطاقة هي «البدء فقط عند التشغيل بالطاقة الكهربائيّة (AC) والتوقّف عند التحوّل إلى البطاريّة».
# إذا كانت المهمّة ستعمل أيضاً على حاسوب محمول أو جهاز ميدانيّ، اسمح بذلك صراحةً هنا
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable `
-MultipleInstances IgnoreNew `
-ExecutionTimeLimit (New-TimeSpan -Hours 2) `
-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
# استقبل بيانات الاعتماد عبر Get-Credential حتى لا تظهر كلمة المرور على الشاشة
$cred = Get-Credential -UserName 'DOMAIN\svc-batch' -Message 'بيانات اعتماد حساب التنفيذ'
Register-ScheduledTask -TaskName 'KS-Cleanup-Logs' `
-Action $action -Trigger $trigger -Settings $settings `
-User $cred.UserName -Password $cred.GetNetworkCredential().Password
إذا أصبح تعريف المهمّة شيفرة، يمكن نقل ما جُرِّب على جهاز التحقّق إلى الإنتاج كما هو، وتتجنّب حالة «لا نعرف بأيّ إعداد يعمل».
لنشر على عدّة أجهزة، تصدير مهمّة أُنجزت في الواجهة الرسوميّة إلى XML وتوزيعها بـ schtasks أسلوب له سوابق أيضاً.
rem تصدير المهمّة التي صُنعت على جهاز التحقّق
schtasks /Query /TN "KS-Cleanup-Logs" /XML > KS-Cleanup-Logs.xml
rem الاستيراد على كلّ جهاز (حدِّد حساب التشغيل وكلمة المرور عند التسجيل)
schtasks /Create /TN "KS-Cleanup-Logs" /XML KS-Cleanup-Logs.xml /RU DOMAIN\svc-batch /RP *
يتضمّن XML المشغِّلات والشروط والإعدادات كلّها، فإيداعه في المستودع يتيح مراجعة تعريف المهمّة وإدارة الفروقات. بالمقابل، تسجيل المهمّة نفسها يدوياً عبر الواجهة على 10 أجهزة يولّد حتماً «مهمّة شاردة» بإعداد مختلف على جهاز واحد. أمّن الأمر بتحويله إلى شيفرة قبل أن يصبح العدد منزلتين.
3. حساب التشغيل ونوع تسجيل الدخول ── أكبر نقطة حوادث
خيارا خصائص المهمّة «التشغيل فقط عند تسجيل دخول المستخدم» و«التشغيل بغضّ النظر عن تسجيل دخول المستخدم» هما داخليّاً اختيار نوع تسجيل الدخول (LogonType). إن انحرف الفهم هنا، تضلّ عن تفسير معظم «يعمل يدوياً ولا يعمل دوريّاً».1
3.1 الفرق بين الأوضاع الثلاثة
| الاختيار | الآليّة الداخليّة | السمات والقيود |
|---|---|---|
| التشغيل فقط عند تسجيل دخول المستخدم | رمز تفاعليّ (InteractiveToken) | تظهر النوافذ على شاشة من سجّل الدخول. لا يبدأ أصلاً أثناء تسجيل الخروج |
| التشغيل بغضّ النظر عن تسجيل الدخول | حفظ كلمة المرور (Password) | تُحفظ كلمة المرور عند التسجيل. لا تظهر شاشة (غير تفاعليّ). بعد تغيير كلمة المرور يفشل البدء |
| السابق + «عدم حفظ كلمة المرور» | S4U | بدلاً من حفظ كلمة المرور، لا يمكن الوصول إلى موارد الشبكة ولا إلى الملفّات المشفَّرة (EFS)1 |
الحوادث النمطيّة في العمل كالتالي.
- سُجِّل سكربت يصل إلى مجلّد مشترك بـ «عدم حفظ كلمة المرور» (S4U). عمل في الاختبار المحلّي، وفي الإنتاج فشل الوصول إلى المجلّد المشترك وحده. ← لأنّ S4U لا يملك بيانات اعتماد للشبكة.
- بعد أشهر من التسجيل بـ «التشغيل بغضّ النظر عن تسجيل الدخول» حلّ أجل كلمة مرور النطاق فغُيِّرت. بعد ذلك توقّفت المهمّة بفشل تسجيل الدخول (
0x8007052E) دون أن يلاحظ أحد. - سُجِّلت مهمّة تشغّل تطبيقاً بواجهة رسوميّة بـ «بغضّ النظر». التطبيق يعمل لكن الشاشة لا تظهر في أيّ مكان، فيُظنّ أنّه «لا يعمل». ← لأنّه يعمل في جلسة غير تفاعليّة. المعالجة التي تحتاج شاشة تفاعليّة لا تعمل بهذا التكوين من حيث المبدأ.
كذلك، الحساب الذي يعمل بـ Password / S4U يحتاج حقّ «تسجيل الدخول كمهمة دُفعيّة» (SeBatchLogonRight). يُمنح لـ Administrators افتراضيّاً، لكن عند جعل مستخدم عاديّ مخصّص حساب خدمة راجع أيضاً إعداد سياسة الأمان المحلّيّة.6
3.2 بأيّ حساب نُشغِّل؟
- SYSTEM: لا يحتاج إدارة كلمة مرور وهو قويّ، لكنّ الصلاحيّات أقوى من اللازم. مفيد لمعالجة صيانة محلّيّة مكتملة، لكن تجنّب جعل SYSTEM لكلّ مهمّة تلمس بيانات أعمال. فكرة متى تلزم فعلاً صلاحيات المسؤول مرتَّبة في مقال منفصل: «متى يصبح Windows admin privilege ضرورياً - UAC والمناطق المحميّة وكيفيّة التمييز على مستوى التصميم».
- حساب خدمة مخصّص (مستخدم عاديّ): يمكن تضييق الصلاحيّات إلى الحدّ الأدنى، مقابل حاجة إلى تحديث المهمّة عند كلّ تغيير لكلمة المرور. أدِر أجل كلمة المرور وجرد المهامّ معاً.
- gMSA (حساب خدمة مُدار جماعيّاً): الخيار الأوّل في بيئة النطاق. يدير المتحكّم بالنطاق كلمة المرور تلقائيّاً، فتختفي مشكلة «المهمّة تموت عند تغيير كلمة المرور» أصلاً. يدعم Task Scheduler التشغيل بـ gMSA.4
مربّع «التشغيل بأعلى الامتيازات» يعني التشغيل برمز المسؤول (المُرقَّى) من الرمزين اللذين يقسمهما UAC. لا تضعْه لمهمّة لا تحتاج صلاحيات مسؤول.
3.3 الحدّ الأدنى لتسجيل مهمّة بـ gMSA
ما دمنا سمّينا gMSA «الخيار الأوّل»، نبيّن أيضاً كيف يُسجَّل فعلاً. مهامّ Task Scheduler مذكورة رسميّاً كأحد التكوينات التي تدعم gMSA.4
الشروط المسبقة كالتالي.4
- مستوى وظائف النطاق والغابة Windows Server 2012 فما بعده
- إنشاء مفتاح جذر KDS في النطاق (يمكن تأكيد الإنشاء بحدث المعرِّف 4004 في سجلّ Operational لـ
KdsSvc) - إنشاء gMSA وإدارته لأعضاء Domain Admins أو Enterprise Admins
- اسم gMSA فريد على مستوى الغابة لا النطاق. وجود الاسم نفسه في نطاق آخر يفشل الإنشاء
الخطوات ثلاث. ينفّذ 1 و2 مسؤول النطاق، و3 على كلّ جهاز ستعمل عليه المهمّة.
# --- 1. جانب النطاق: إنشاء gMSA. حدِّد بالمجموعة الأمنيّة المضيفات المسموح لها بجلب كلمة المرور ---
# ضع في <SecurityGroup> مجموعة تضمّ حسابات أجهزة الخوادم التي ستشغّل المهمّة
New-ADServiceAccount -Name 'svc-batch' -DNSHostName 'svc-batch.contoso.local' `
-PrincipalsAllowedToRetrieveManagedPassword 'GG-BatchHosts'
# --- 2. على كلّ جهاز تشغّل عليه المهمّة: ثبِّت gMSA وتحقّق من إمكان جلب كلمة المرور ---
Install-ADServiceAccount -Identity 'svc-batch'
Test-ADServiceAccount -Identity 'svc-batch' # إن عاد True فهو جاهز
الخطوة الثالثة هي تسجيل المهمّة. النقطتان هما التاليتان.
# --- 3. على الجهاز الذي تشغّل عليه المهمّة: سجِّل المهمّة وgMSA هو الـ principal ---
$action = New-ScheduledTaskAction -Execute 'pwsh.exe' `
-Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Jobs\Cleanup-Logs.ps1"' `
-WorkingDirectory 'C:\Jobs'
$trigger = New-ScheduledTaskTrigger -Daily -At '06:00'
# النقطة 1: ألحق $ في نهاية اسم الحساب (صيغة اسم gMSA)
# النقطة 2: LogonType هو Password. لكن لا تمرِّر -Password
# (يدير المتحكّم بالنطاق كلمة مرور gMSA ويجلبها المضيف)
$principal = New-ScheduledTaskPrincipal -UserId 'CONTOSO\svc-batch$' `
-LogonType Password -RunLevel Limited
Register-ScheduledTask -TaskName 'KS-Cleanup-Logs' `
-Action $action -Trigger $trigger -Principal $principal
القيم التي يمكن تحديدها في -LogonType لـ New-ScheduledTaskPrincipal هي None / Password / S4U / Interactive / Group / ServiceAccount / InteractiveOrPassword.7 كما في جدول القسم 3.1، لا يصل S4U إلى موارد الشبكة، فلا تختره بسهولة لمهمّة تلمس مجلّداً مشتركاً.
تكميلان. أولاً، هذا gMSA يحتاج أيضاً حقّ «تسجيل الدخول كمهمة دُفعيّة» (الكلام نفسه في نهاية القسم 3.1؛ gMSA لا يُعفى).6 ثانياً، schtasks.exe أمر يفترض تمرير كلمة مرور حساب التشغيل بـ /RP، وإجراء التسجيل بـ gMSA غير موجَّه رسميّاً. عند استخدام gMSA اجمع تسجيل المهمّة على جانب PowerShell Register-ScheduledTask. إن جمعته مع أسلوب الفصل 2 «تصدير XML والتوزيع بـ schtasks»، يصبح التكوين استبدال جزء الـ principal فقط عبر PowerShell.
4. خطوات تشخيص «لا تُنفَّذ»
4.1 فعِّل السجلّ ثم اشبه
«تفعيل سجلّ جميع المهامّ» في الجزء الأيمن من Task Scheduler معطَّل افتراضيّاً. إن بقي السجلّ معطَّلاً لا يبقى حتى سجلّ بوقوع الفشل. فعِّله حتماً قبل التشغيل الفعليّ. الخطوات كالتالي.
- شغِّل Task Scheduler (
taskschd.msc، أو ابحث في قائمة ابدأ عن «Task Scheduler»). شغِّله بصلاحيات مسؤول. - في شجرة الجزء الأيسر اختر الأعلى «Task Scheduler (Local)». لن يظهر هذا البند إن كانت مهمّة فرديّة محدَّدة.
- انقر «Enable All Tasks History» في قائمة «Actions» في الجزء الأيمن. إن كان مفعَّلاً أصلاً، يتغيّر العرض إلى «Disable All Tasks History».
كيان السجلّ هو سجلّ Applications and Services Logs > Microsoft > Windows > TaskScheduler > Operational في عارض الأحداث.2 علامة تبويب «History» لكلّ مهمّة تعرض هذا السجلّ مصفّى بالمهمّة المستهدَفة، لذا لتتبّع التسلسل الزمنيّ عبر عدّة مهامّ افتح جانب عارض الأحداث.
خطوات التشخيص الأساسيّة تعمل كما هي وفق دليل استكشاف الأخطاء لدى Microsoft.2
- اختبر السكربت منفرداً ── قبل التحميل على المهمّة، تأكّد أنّ السكربت نفسه يكتمل بشروط حساب التشغيل نفسها (إن أمكن بـ
runasأو جهاز تحقّق). - انظر عمود الحالة وعلامة تبويب السجلّ ── ميّز هل أُطلق المشغِّل أصلاً، أم بدأ ثم فشل. إن لم يُطلق، اشبه إعداد المشغِّل والشروط (الطاقة والشبكة)، وتحقّق هل تعمل العمليّة نفسها بالتشغيل اليدويّ (نقر أيمن ← Run).
- جرِّب التحويل المؤقّت إلى «التشغيل فقط عند تسجيل الدخول» ── إن عمل بذلك، ينحصر السبب في الجلسة غير التفاعليّة أو بيانات الاعتماد (الفصل السابق).
4.2 ماذا تشبه إذا ظهر ماذا في علامة تبويب السجلّ ── جدول سريع لمعرِّفات الأحداث
كلّ سطر في علامة تبويب السجلّ (وسجلّ Operational) يحمل معرِّف حدث. بهذا المعرِّف تعرف فوراً «إلى أين وصل». المهمّة الطبيعيّة الواحدة تتراتب تقريباً: «بدء (100) ← تشغيل العمليّة (129) ← بدء العمليّة (200) ← اكتمال العمليّة (201) ← اكتمال (102)».
| معرِّف الحدث | معنى الرسالة | ما تشبهه عند ظهوره |
|---|---|---|
| 106 | سجّل مستخدم المهمّة8 | سجلّ التسجيل. نقطة انطلاق لـ«متى غيّره من» |
| 140 / 141 | حدّث مستخدم المهمّة / حذفها8 | البحث عن الجاني في «كانت تعمل حتى أمس» يبدأ هنا |
| 113 | سُجِّلت المهمّة لكن بعض المشغِّلات لا تبدأ المهمّة8 | نقص في تعريف المشغِّل. حُذِّر منه عند التسجيل |
| 116 | حُفظ تكوين المهمّة لكن تعذّر حفظ بيانات الاعتماد المستخدمة للتشغيل8 | تحديد حساب التشغيل وكلمة المرور. إن ظهر هذا فلن تعمل طبعاً |
| 100 | بدأت المهمّة9 | إن غاب، فإمّا أنّ المشغِّل لم يُطلق أصلاً، أو فشل البدء بـ 101 |
| 101 | تعذّر بدء المهمّة. مع قيمة خطأ9 | أُطلق المشغِّل لكن فشل التشغيل. اشبه بيانات اعتماد حساب التشغيل (0x8007052E وغيرها) والصلاحيّات. لا يظهر 100 |
| 129 | شُغِّلت المهمّة مع معرِّف عمليّة9 | وُلدت العمليّة. ما بعده حديث جانب السكربت |
| 200 / 201 | بدأت العمليّة (action) / اكتملت العمليّة9 | 201 يعني «انتهى البرنامج الذي شُغِّل»، ولا يعبّر عن نجاح المحتوى. في Windows الحالي يحمل 201 قيمة إرجاع (ResultCode) في النصّ وبيانات الحدث، فانظر هناك (القسم 4.3) |
| 202 | تعذّر على Task Scheduler إكمال العمليّة. مع قيمة خطأ9 | فشل جانب Task Scheduler. لا يلزم أن يظهر هنا عندما ينتهي البرنامج بـ 0x1 |
| 203 | فشل تشغيل العمليّة نفسها. مع قيمة خطأ9 | خطأ مسار الملفّ التنفيذيّ، أو تحديد غير صحيح لـ «البدء (اختياريّ)»، أو نقص صلاحيات |
| 102 | انتهت المهمّة بشكل طبيعيّ9 | نهاية المسار الطبيعيّ |
| 111 | أُنهيَت المهمّة لتجاوز حدّ مدّة التنفيذ9 | بلوغ «الوقت حتى الإيقاف» (افتراضيّ 3 أيّام). إلى الفصل 7 |
| 322 | لم تبدأ لأنّ نسخة أخرى من المهمّة نفسها قيد التنفيذ10 | يعمل التحكّم في التشغيل المتعدّد. السابقة لم تنتهِ. إلى الفصل 7 |
| 323 | أُوقفت النسخة قيد التنفيذ لبدء نسخة جديدة9 | MultipleInstances مضبوط على «إيقاف النسخة القائمة» |
| 327 | أُوقفت النسخة لأنّ الطاقة تحوّلت إلى البطاريّة9 | شرط الطاقة (القسم 4.4). شائع على الحواسيب المحمولة والأجهزة الميدانيّة |
| 328 | أُوقفت النسخة لأنّ الحاسوب لم يعد خاملاً9 | شرط الخمول مفعَّل |
| 329 | أُوقفت النسخة لانتهاء مهلة المهمّة9 | راجع تصميم مدّة التنفيذ كما في 111 |
| 330 | أُوقفت النسخة بطلب من المستخدم9 | أحدهم يوقفها يدوياً |
أبرز ثلاث قراءات هي التالية.
- إن غاب 100 فانظر أولاً هل ظهر 101 (تعذّر بدء المهمّة). إن ظهر، أُطلق المشغِّل وفشل التشغيل، فاشبه قيمة الخطأ وحساب التشغيل (الفصل 3). إن غاب 101 أيضاً فالسبب قبل المهمّة (المشغِّل، والشروط، وتعطيل المهمّة)، وإن ظهر 322 فسبب ذلك أنّ النسخة السابقة لم تنتهِ.
- إن وُجد 100 وغاب 102 فقد بدأ ولم ينتهِ. 111 / 329 انتهاء وقت، 203 فشل التشغيل نفسه، و327 / 328 تعني أنّ النسخة قيد التنفيذ أُوقفت بشرط الطاقة أو الخمول (327 / 328 سجلّ «أوقفت» لا «لم تبدأ»، فيظهر بعد 100).
- إن وُجد 100 و102 والنتيجة غريبة فقد أكمل Task Scheduler نطاق مسؤوليّته. بعد ذلك لا يُتتبَّع إلا بسجلّ جانب السكربت (الفصل 6).
flowchart TD
S["لا تُنفَّذ أو النتيجة غريبة"] --> Q1{"هل يوجد الحدث 100 (بدء)؟"}
Q1 -->|"لا"| Q1b{"هل يوجد الحدث 101؟"}
Q1b -->|"نعم"| A0["أُطلق المشغِّل لكن فشل التشغيل. انظر قيمة خطأ 101 وحساب التشغيل (الفصل 3)"]
Q1b -->|"لا"| A1["السبب قبل المهمّة: المشغِّل والشروط والتعطيل (القسم 4.4). إن كان 322 فالسابقة لم تنتهِ"]
Q1 -->|"نعم"| Q2{"هل يوجد الحدث 102 (إنهاء طبيعيّ)؟"}
Q2 -->|"لا"| B["بدأ ولم ينتهِ. 203 = فشل التشغيل نفسه. 111 و329 = انتهاء وقت. 327 و328 = إيقاف بشرط الطاقة أو الخمول. 202 = فشل جانب Task Scheduler"]
Q2 -->|"نعم"| C["جانب Task Scheduler اكتمل. إن كان 0x1 فالسكربت أعاد رمز خروج 1. بعد ذلك تتبّع بسجلّ السكربت (الفصل 6)"]
الشكل 1: وجود الحدثين 100 و102 أو غيابهما يفصل جهة البحث بين إعداد المهمّة وجانب السكربت.
هنا موضع يسهل الخطأ فيه. حتى لو انتهى البرنامج بقيمة غير صفريّة مثل 0x1، يرى Task Scheduler أنّه «بدأ وانتهى» فيظهر 201 (ACTION_SUCCESS). 202 حدث «تعذّر على Task Scheduler إكمال العمليّة»، وليس وعاءً لانتهاء غير صفريّ للبرنامج.9 لذلك قد لا تجد 202 وأنت تتتبّع 0x1. ما ينبغي النظر إليه هو قيمة إرجاع 201 وعمود «نتيجة التشغيل الأخيرة» للمهمّة (القسم 4.3). ResultCode في 201 و«نتيجة التشغيل الأخيرة» لا يتطابقان بالضرورة، فالأوثق تسجيل رمز خروجك بنفسك في جانب السكربت كما في الفصل 6.
4.3 قراءة «نتيجة التشغيل الأخيرة»
| العرض | المعنى |
|---|---|
0x0 |
إنهاء طبيعيّ (أعاد البرنامج الذي شُغِّل رمز خروج 0) |
0x1 |
أعاد البرنامج الذي شُغِّل رمز خروج 1 (ليس خطأ Task Scheduler نفسه) |
0x41300 |
بانتظار التشغيل المجدوَل التالي (SCHED_S_TASK_READY) |
0x41301 |
قيد التنفيذ الآن (SCHED_S_TASK_RUNNING) |
0x41303 |
لم تُنفَّذ ولا مرّة بعد (SCHED_S_TASK_HAS_NOT_RUN) |
0x8007010B |
تحديد مجلّد البدء («البدء (اختياريّ)») غير صحيح. العَرَض النمطيّ لوضع علامات اقتباس |
0x8007052E |
فشل تسجيل الدخول. كلمة المرور المحفوظة قديمة، أو لا يوجد الحقّ، وما شابه |
سلسلة 0x413xx رموز حالة Task Scheduler، و0x8007xxxx رموز أخطاء Windows، والقيم الصغيرة مثل 0x1 و0x2 هي رمز خروج البرنامج الذي شُغِّل نفسه.3 بهذا التمييز لا تخطئ من البداية جهة البحث (إعداد المهمّة أم السكربت).
flowchart TB
R["القيمة الظاهرة في «نتيجة التشغيل الأخيرة»"]
R -->|"قيمة صغيرة مثل 0x0 / 0x1 / 0x2"| P["رمز خروج البرنامج الذي شُغِّل نفسه. جهة البحث هي السكربت"]
R -->|"0x413xx"| ST["رمز حالة Task Scheduler (انتظار، تنفيذ، لم تُنفَّذ). لا يعبّر عن فشل أصلاً"]
R -->|"0x8007xxxx"| Q{"هل شُغِّلت العمليّة؟ (هل يوجد 201، وهل غاب 203؟)"}
Q -->|"لم تُشغَّل"| W["رمز خطأ Windows (تحديد مجلّد البدء غير صحيح، فشل تسجيل الدخول، وغيرها). جهة البحث إعداد المهمّة"]
Q -->|"شُغِّلت"| W2["رمز خروج أعادته العمليّة الابنة. قد يعيده التطبيق بصيغة HRESULT. جهة البحث هي السكربت"]
الشكل 2: تختلط في العمود نفسه ثلاث سلاسل قيم. غير أنّ 0x8007xxxx لا يُحسَم بالبادئة وحدها ── إن شُغِّلت العمليّة فهي القيمة التي أعادتها العمليّة الابنة، فاحكم المصدر بمقابلة الحدثين 201 و203.
4.4 انتبه للقيم الافتراضيّة للشروط والإعدادات
- شرط الطاقة: افتراضيّاً مفعَّل «بدء المهمّة فقط عند استخدام الحاسوب بطاقة AC». إن جعلت حاسوباً محمولاً جهاز تحقّق، يصبح «عطل لا يتكرّر» يعمل فقط أثناء البطاريّة. علاوة على ذلك، من Windows 10 فصاعداً تتأخّر مشغِّلات كثير من المهامّ أثناء تفعيل ميزة توفير البطاريّة.11
- عند فوات وقت البدء: إن كان الحاسوب مطفأً وتجاوز وقت البدء، لا تُنفَّذ افتراضيّاً حتى الجدول التالي. فعِّل صراحة «إذا تعذّر بدء المهمّة في الوقت المجدوَل، شغِّلها فوراً» (
-StartWhenAvailable)، أو قرِّر كتصميم أنّ الفوات مقبول.5 - إيقاظ من النوم: لمهمّة ليليّة على حاسوب ينام، قرِّر أيضاً لزوم «إيقاظ الحاسوب لتشغيل المهمّة» (
-WakeToRun).
نجمع هنا، حسب علامة التبويب، أين تقع الإعدادات التي ظهرت حتى الآن في الواجهة الرسوميّة. هذا تكوين مربع الحوار الذي يُفتح بنقر أيمن على المهمّة ← «Properties».
| علامة التبويب | ما يُقرَّر هنا | الموضع في هذا المقال |
|---|---|---|
| General | اسم المهمّة، حساب التشغيل («Change User or Group»)، «Run only when user is logged on» / «Run whether user is logged on or not»، «Do not store password»، «Run with highest privileges» | الفصل 3 |
| Triggers | متى يبدأ التشغيل. أضف وقتاً أو عند تسجيل الدخول أو عند حدث من «New» | القسم 4.5 |
| Actions | الحقول الثلاثة «Program/script» و«Add arguments (optional)» و«Start in (optional)». عدم وضع علامات اقتباس في «Start in (optional)» هنا | الفصل 5 |
| Conditions | شرط الطاقة (AC فقط / التوقّف عند التحوّل إلى البطاريّة)، وشرط الخمول، وشرط الشبكة | النقاط أعلاه |
| Settings | «Run task as soon as possible after a scheduled start is missed»، و«If the task is already running, then the following rule applies»، و«Stop the task if it runs longer than» | النقاط أعلاه والفصل 7 |
| History | قائمة أحداث تلك المهمّة. معطَّلة افتراضيّاً وتبقى فارغة حتى تفعيلها بإجراء القسم 4.1 | القسمان 4.1 و4.2 |
4.5 انتبه لتصميم المشغِّل نفسه
قد تظنّ «لا تُنفَّذ» بينما تصميم المشغِّل نفسه منحرف عن القصد.
- «كلّ شهر في اليوم 31» لا يُنفَّذ في الأشهر التي لا تحتوي 31. لمعالجة نهاية الشهر أميل إلى تصميم يقصد «آخر يوم من الشهر» (معالجة حصّة الشهر السابق في أوّله، أو الحكم بالتاريخ داخل السكربت).
- الوقت هو الوقت المحلّي للجهاز الذي سُجِّلت عليه المهمّة. توزيع XML نفسه على أجهزة فروع خارجيّة أو خوادم تُشغَّل أحياناً بتوقيت UTC يزيح وقت التنفيذ حسب الفرع. قرِّر كمواصفة هل المقصود «السادسة صباحاً بتوقيت اليابان في كلّ الفروع» أم «السادسة صباحاً في كلّ فرع».
- إن بدأت التكرار بفاصل قصير (كلّ 5 دقائق مثلاً) عبر Task Scheduler فانتبه. هو أداة ممتازة لـ«دُفعة مرّة في اليوم»، لكن إن لزم استقصاء بالدقيقة أو مراقبة دائمة فذلك مجال عمليّة مقيمة (الفصل 8 لاحقاً).
- مشغِّل الحدث قويّ، لكن تحقّق أولاً أنّ الحدث المستهدَف يُسجَّل بثبات. تكوين يشغِّل بمعرِّف حدث معيّن في سجلّ التطبيق يصمت عن العمل إذا تغيّر أسلوب إصدار الأحداث بعد تحديث التطبيق. مشغِّل وقت + حكم شرط داخل السكربت كثيراً ما يكون أسهل تتبّعاً في النهاية.
5. الأنماط النمطيّة للانتهاء بـ 0x1 والاستدعاء الصحيح لـ PowerShell
0x1 نتيجة فشل السكربت فقط، فالسبب في فرق بيئة تنفيذ السكربت. ما يختلف بين التشغيل اليدويّ والدوريّ هو أساساً التالي.
- دليل العمل الحاليّ مختلف: إن لم تحدِّد «البدء (اختياريّ)» يعمل في
C:\Windows\System32مثلاً. السكربت المكتوب بمسارات نسبيّة ينكسر هنا. ابنِ المسارات في السكربت على$PSScriptRoot، وحدِّد مجلّد العمل في «البدء (اختياريّ)» على جانب المهمّة. عندئذ لا تضع علامات اقتباس في حقل «البدء (اختياريّ)». اكتب حتى المسار الذي يتضمّن مسافات بلا اقتباس (وضعها يفشل بـ0x8007010B). - متغيّرات البيئة والملفّ الشخصيّ مختلفة: عدّ متغيّرات البيئة التي يضبطها سكربت تسجيل الدخول أو ملفّ المستخدم الشخصيّ، ومحرّكات الشبكة المرتبطة (مثل X:)، غير موجودة في الجلسة غير التفاعليّة. استخدم مسار UNC (
\\server\share\...) مباشرة، وأزل فرق الملفّ الشخصيّ بـ-NoProfile. - سياسة التنفيذ مختلفة: قد تكون
RemoteSignedمضبوطة للمستخدم وغير مضبوطة لحساب الخدمة. صرِّح بـ-ExecutionPolicy Bypassفي معطيات المهمّة. - اتفاقيّة رمز خروج الأداة خاصّة: مثلاً يعيد
robocopyالقيمة 1 عندما «نُسخت كلّ الملفّات بنجاح». الغلاف الذي يعيد رمز الخروج كما هو يظهر0x1رغم الحالة الطبيعيّة، أو العكس. راجع دائماً اتفاقيّة رمز الخروج للأمر الخارجيّ الذي تستخدمه.
في حالة robocopy، جدول رموز الخروج الرسميّ يعدّ 0 إلى 7 تركيبات «بلا فشل»، و8 فما فوق تعني «وقع فشل واحد على الأقلّ أثناء النسخ».12 أي أنّ الصحيح ليس الإعادة كما هي، بل غلاف يُطبِّع إلى 0/1.
# استقبل المعطيات بنفسك. لا تعتمد ضمنيّاً على متغيّرات جهة الاستدعاء
param(
[Parameter(Mandatory)][string]$Source,
[Parameter(Mandatory)][string]$Destination
)
# يعيد robocopy رمز الخروج في $LASTEXITCODE.
# حتى مع $ErrorActionPreference = 'Stop'، الانتهاء غير الصفريّ للأمر الأصليّ
# لا يصبح استثناء، لذا يلزم الحكم بنفسك
robocopy $Source $Destination /E /R:2 /W:5 /NP
$rc = $LASTEXITCODE
if ($rc -ge 8) {
Write-Error "فشل robocopy. رمز الخروج: $rc"
exit 1
}
# 0 إلى 7 بلا فشل. اترك ما حدث في السجلّ، وأعد النجاح إلى Task Scheduler
Write-Host "اكتمل robocopy بنجاح. رمز الخروج: $rc"
exit 0
سطر -ge 8 هو الجوهر. إن كتبت if ($rc -ne 0) تُعدّ المرّة التي نُسخ فيها بنجاح (1) فشلاً أيضاً. هذا هو جوهر الاستشارة الكلاسيكيّة «يصل إشعار فشل النسخ الاحتياطيّ كلّ صباح، لكن الملفّات منسوخة فعلاً».
الصيغة الأساسيّة لاستدعاء PowerShell كالتالي.
البرنامج/السكربت: pwsh.exe (powershell.exe إن كان Windows PowerShell)
إضافة المعطيات: -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Jobs\Cleanup-Logs.ps1"
البدء (اختياريّ): C:\Jobs (بلا علامات اقتباس)
سبب استخدام -File لا -Command، فضلاً عن أنّ تهريب المعطيات يصبح مباشراً، أنّ exit n في السكربت يصبح رمز خروج العمليّة كما هو، فيمكن تمييز النجاح من الفشل من «نتيجة التشغيل الأخيرة» في Task Scheduler. صمِّم أيضاً في جانب السكربت إعادة 0 صراحة عند النجاح وغير 0 عند الفشل.
# اضبط Stop حتى تسقط «أخطاء غير منتهية» للأوامر في catch.
# بدونها قد تمرّ إخفاقات مثل Copy-Item وينتهي الأمر بـ exit 0
$ErrorActionPreference = 'Stop'
try {
Main
exit 0
}
catch {
Write-Error $_
exit 1
}
فكرة أين تُلتقط الاستثناءات وكيف تُسجَّل مفصَّلة أيضاً في مقال منفصل: «أين يجب التقاط الاستثناءات وتسجيلها ومعالجة الأخطاء - دليل عمليّ للحدود والمسؤوليّات في تسلسل الاستدعاء».
6. اترك السجلّ بنفسك
سجلّ Task Scheduler لا يخبرك إلا «هل بدأ، وما رمز الخروج». ما الذي فعله السكربت وإلى أين وصل يلزم أن يتركه السكربت نفسه كسجلّ.
في الحدّ الأدنى، لا تُعد التوجيه في معطيات المهمّة (صيغة التوجيه في حقل «المعطيات» في Task Scheduler لا تعمل لأنّها ليست عبر صدفة)، بل خذ transcript داخل السكربت فهو أيسر.
$logDir = 'C:\Jobs\logs'
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
Start-Transcript -Path (Join-Path $logDir ("cleanup-{0:yyyyMMdd}.log" -f (Get-Date))) -Append
try {
# المعالجة الأساسيّة
}
finally {
Stop-Transcript
}
معالجة مشكلة تراكم السجلّات نفسها (إدارة الأجيال والأرشفة) هي تماماً محتوى «تطبيقات PowerShell المتقدّمة ── أتمتة آمنة لتحقيق السجلّات وأرشفتها وإصدار التقارير عنها».
خطوة أبعد: انظر أيضاً في الكتابة إلى سجلّ أحداث Windows. بخلاف سجلّ الملفّ، تصل النتيجة إلى موضع يراه جانب التشغيل أصلاً (عارض الأحداث، وأدوات المراقبة القائمة).
# --- عند الإعداد مرّة واحدة، بصلاحيات مسؤول (المثبِّت أو سكربت الإعداد الأوّليّ) ---
if (-not [System.Diagnostics.EventLog]::SourceExists('KS-Jobs')) {
[System.Diagnostics.EventLog]::CreateEventSource('KS-Jobs', 'Application')
}
# --- جسم المهمّة (يعمل بحساب أدنى صلاحيات) يقوم بالكتابة فقط.
# قد يطلب SourceExists صلاحيات مسؤول لاستكشاف كلّ السجلّات، فلا تستدعه وقت التنفيذ.
# التالي مفترض داخل معالج فشل Main (catch) ---
catch {
$err = $_
try {
[System.Diagnostics.EventLog]::WriteEntry('KS-Jobs',
"Cleanup-Logs failed: $($err.Exception.Message)",
[System.Diagnostics.EventLogEntryType]::Error, 1001)
}
catch {
# لا تجعل تعذّر الكتابة في السجلّ سبباً لابتلاع الفشل الأصليّ
Write-Warning "فشل الكتابة إلى سجلّ الأحداث: $_"
}
exit 1
}
تكميلان. أولاً، تسجيل مصدر الحدث (CreateEventSource) يحتاج صلاحيات مسؤول. خلط التسجيل في جسم المهمّة يؤدّي عند أوّل فشل في الإنتاج بحساب خدمة أدنى صلاحيات إلى فشل مزدوج: «محاولة التسجيل ترمى استثناء ← الحدث الجوهريّ لا يُكتب». افصل التسجيل إلى جانب الإعداد كما أعلاه، واجعل وقت التنفيذ كتابة فقط. ثانياً، إذا كان محرّك التنفيذ pwsh.exe كما في أمثلة تسجيل المهمّة في هذا المقال، لا تعمل أوامر New-EventLog / Write-EventLog من عصر Windows PowerShell 5.1 (لا يُوجد الأمر فيفشل السكربت كلّه). استدعاء صنف .NET مباشرة كما أعلاه يعمل على 5.1 و7 كليهما.
بعد ذلك، ضع آليّة «تصل إلى شخص عند الفشل» ── إشعاراً بالبريد أو Teams / Slack ── فتتجنّب حادثة «اكتشفنا في الجرد أنّها توقّفت أشهراً». لا تحتاج بنية إشعار معقّدة؛ بضعة أسطر POST إلى Webhook عند الفشل تكفي. بالمقابل «إشعار عند كلّ نجاح» سرعان ما يتوقّف عن القراءة، فاكبح النجاح إلى ملخّص أسبوعيّ تقريباً، واجعل هدف الرصد الفشل و«لم تُنفَّذ» (وقت التشغيل السابق قديم). معيار ما يُكتب في السجلّ مرتَّب أيضاً في «أين يجب التقاط الاستثناءات وتسجيلها ومعالجة الأخطاء».
7. التحكّم في التشغيل المتعدّد والتنفيذ الطويل
ماذا يحدث إن جاء وقت الجدول التالي بينما السابقة ما زالت تطول؟ يُحسَم ذلك بقاعدة «If the task is already running, then the following rule applies» في علامة تبويب الإعدادات، ويقابل في PowerShell -MultipleInstances.5
| القيمة | السلوك | الاستخدام المناسب |
|---|---|---|
| IgnoreNew (افتراضيّ الواجهة: عدم بدء نسخة جديدة) | إن كانت قيد التنفيذ تُتخطّى النسخة الجديدة | الدُفعات الدوريّة ذات القدرة على التكرار (idempotent) عموماً. ابدأ بهذا |
| Queue | إن كانت قيد التنفيذ تُنفَّذ بالترتيب بعد انتهائها | تجميع لا يُسمح بتفويته |
| Parallel | تبدأ بالتوازي | تجنّبها من حيث المبدأ. فقط إن ضُمنت سلامة التوازي |
اضبط أيضاً «الوقت حتى الإيقاف» (-ExecutionTimeLimit، افتراضيّ 3 أيّام) على قيمة واقعيّة (نحو 2 إلى 3 أضعاف مدّة التنفيذ المتوقَّعة)، فتتجنّب حادثة عمليّة معلَّقة تجرّ مهمّة الغد معها.5
انتبه أنّ ما يحميه IgnoreNew أو Queue هو داخل تعريف المهمّة نفسها فقط. لا يمنع تصادم مهمّة أخرى تستدعي السكربت نفسه، أو تشغيلاً يدويّاً عند معالجة عطل. إن وُجدت مسارات عدّة تلمس المورد نفسه (ملفّ، قاعدة بيانات، نظام خارجيّ)، ضع حصراً أيضاً في جانب السكربت. الأسلوب الثابت هو mutex مسمّى.
$mutex = New-Object System.Threading.Mutex($false, 'Global\KS-Cleanup-Logs')
$acquired = $false
try {
try {
$acquired = $mutex.WaitOne(0)
}
catch [System.Threading.AbandonedMutexException] {
# انتهت المهمّة السابقة قسراً وهي ما زالت تملك الـ mutex
# (تجاوز «الوقت حتى الإيقاف»، وقتل العمليّة، وانقطاع الطاقة، وغيرها).
# عندئذ لا يعيد WaitOne القيمة false بل يرمي استثناء،
# وملكيّة الـ mutex انتقلت إلينا. إن سقطت دون التقاطه،
# تتوقّف كلّ تنفيذات لاحقة هنا ولا تعمل المهمّة بعد ذلك أبداً
$acquired = $true
Write-Warning 'انتهى التنفيذ السابق بشكل غير طبيعي. تحقّق من تنظيف العمل المقطوع.'
# افحص هنا هل بقي ملفّ كُتب إلى المنتصف أو سجلّ ناقص،
# ثم انتقل إلى المعالجة الأساسيّة
}
if (-not $acquired) {
Write-Warning 'ينتهي لأن مثيلاً آخر قيد التشغيل.'
exit 0 # إن لم تعدّ «لم تُنفَّذ» فشلاً فـ 0، وإن عدّتها فشلاً فغير 0
}
# المعالجة الأساسيّة
}
finally {
if ($acquired) { $mutex.ReleaseMutex() }
$mutex.Dispose()
}
AbandonedMutexException استثناء يُعلم أنّ «المالك السابق اختفى دون تحرير»، وعند الرمي تكون الملكيّة قد انتقلت إلينا. لذلك إن أنهيت بـ exit هنا يبقى دون تحرير ويظهر الاستثناء نفسه في المرّة التالية، فتتوقّف المهمّة إلى الأبد. الصحيح التقاطه، والتحقّق من حالة العمل المقطوع، ثم المتابعة.
إلحاق Global\ في بداية الاسم يجعل الحصر يعمل أيضاً بين جلسات مختلفة (مهمّة مستخدم آخر وتشغيل يدويّ مثلاً). قرِّر وفق طبيعة المهمّة هل تنتظر الحصول على القفل (تمرير مهلة إلى WaitOne) أم تتنازل فوراً. وانتبه أنّ الكائن المسمّى بـ Global\ يراه أيّ شخص على الجهاز. على خادم مشترك يسجّل عليه عدّة مستخدمين، إن أُمسك mutex بالاسم نفسه عمداً أو خطأ، تُتخطّى المهمّة إلى الأبد (وتبدو طبيعيّة إن كان exit 0). في تلك البيئة اضبط ACL (MutexSecurity) على الـ mutex لتضييق الحسابات التي يمكنها الحصول عليه، أو على الأقل أركب «تخطّيت لأنّي لم أحصل» على إشعار الفصل السابق وسجلّ الأحداث، حتى ترصد المراقبة تتابع التخطّي. الحصر في التكامل عبر الملفّات مفصَّل في «أساسيات التحكّم الحصري في تكامل الملفّات - أفضل الممارسات لـ file lock والـ claim الذرّي».
8. متى تتوقّف عن Task Scheduler ── الفصل بينه وبين الخدمة المقيمة
Task Scheduler ليس لكلّ شيء. عندما تنمو المتطلّبات، توجد حدود يكون فيها تبديل الآليّة أفضل من مواصلة الاستخدام بالقوّة.
| المتطلّب | الآليّة المناسبة |
|---|---|
| دُفعة موقوتة حتى عدّة مرّات في اليوم | Task Scheduler |
| محفّز البدء خليط من شخص وحدث ووقت، وتريد إظهار التدفّق كلّه | Power Automate (مقال منفصل) |
| استقصاء بالدقيقة، ومراقبة دائمة، ومعالجة طابور | خدمة Windows / عمليّة مقيمة |
| تريد الاحتفاظ بحالة بين المعالجات، والتحكّم الدقيق في إعادة المحاولة والتراجع (backoff) | خدمة Windows / عمليّة مقيمة |
إن بدأت الاستقصاء بـ«مهمّة كلّ 5 دقائق»، تتراكم تكلفة توليد العمليّة وتحميل الوحدات عند كلّ بدء، ويلزم آليّة لإيداع حالة المرّة السابقة في ملفّ وما شابه، فتعيد عمليّاً تنفيذ عمليّة مقيمة مقطَّعة. عند هذه المرحلة، الإقامة بـ Generic Host في .NET وBackgroundService هي المسار المباشر. نمط التنفيذ مشروح في «لماذا نستخدم Generic Host و BackgroundService في تطبيق سطح المكتب».
بالمقابل، تحويل دُفعة شهريّة أو يوميّة إلى خدمة وإدارة مؤقِّت بنفسك مبالغة أيضاً. الخطّ «فاصل التنفيذ بالساعة فما فوق، والمعالجة مستقلّة، وبلا حالة ← Task Scheduler؛ وإن بدأ الخروج عن ذلك فانظر في الإقامة» لا يخطئ كثيراً.
9. قائمة تحقّق قبل التشغيل الفعليّ
يُنصَح بمراجعة البنود التالية واحداً واحداً قبل التسجيل.
- هل تقرّر حساب التشغيل؟ (ألم تختر SYSTEM بالكسل. إن كان نطاقاً هل نظرت في gMSA؟)
- هل فهمت قيود نوع تسجيل الدخول؟ (S4U بلا وصول للشبكة. Password: هل قرّرت التشغيل عند تغيير كلمة المرور؟)
- هل اختبرت السكربت منفرداً بشروط تعادل حساب التشغيل؟
- هل تستدعيه بصيغة
-NoProfile -NonInteractive -ExecutionPolicy Bypass -File؟ - هل مسارات داخل السكربت على أساس
$PSScriptRoot/ UNC؟ (ألا تعتمد على محرّك مرتبط أو مسار نسبيّ؟) - ألا توجد علامات اقتباس في «البدء (اختياريّ)»؟
- هل صُمِّم رمز الخروج؟ (نجاح 0 / فشل غير 0. هل راجعت اتفاقيّة رمز خروج الأمر الخارجيّ؟)
- هل فُعِّل سجلّ المهمّة؟ هل يوجد سجلّ السكربت نفسه وإشعار فشل؟
- هل ضُبط شرط الطاقة وStartWhenAvailable والتشغيل المتعدّد وحدّ مدّة التنفيذ صراحة؟
- هل حُفظ تعريف المهمّة في المستودع كـ XML أو سكربت PowerShell؟
10. الخلاصة
Task Scheduler ليس «اكتب السكربت وانتهِ»، بل يستقرّ التشغيل فقط بعد تصميم ثلاث مقدّمات: حساب التشغيل، والجلسة، والقيم الافتراضيّة. بالمقابل، إن ثبّت عند التسجيل النقاط الواردة هنا ── اختيار نوع تسجيل الدخول، وتفعيل السجلّ، وتصميم رمز الخروج والسجلّ، والتصريح بالتشغيل المتعدّد وحدّ الوقت ── يقلّ العمل بعد ذلك على نحو مدهش.
«يعمل يدوياً ولا يعمل دوريّاً» سببه شبه المؤكَّد فرق الجلسة والبيئة. قبل العبث بالإعدادات بلا هدف، تحقّق من أين وصل في علامة تبويب السجلّ، وجرِّب خطوات التشخيص في هذا المقال من الأعلى.
مقالات ذات صلة
- تطبيقات PowerShell المتقدّمة ── أتمتة آمنة لتحقيق السجلّات وأرشفتها وإصدار التقارير عنها
- تهيئة اختبارات PowerShell عبر Pester ── الأسلوب العمليّ لجعل سكربتات التشغيل أقلّ عرضةً للكسر
- أتمتة الأعمال باستخدام Power Automate ── الفصل بين التدفّق السحابي وتدفّق سطح المكتب وتصميم معالجة الأخطاء
- متى يصبح Windows admin privilege ضرورياً - UAC والمناطق المحميّة وكيفيّة التمييز على مستوى التصميم
مجالات الاستشارة ذات الصلة
تتعامل شركة كومورا سوفت ذ.م.م. مع مراجعة تصميم أتمتة الأعمال عبر PowerShell وTask Scheduler، ومع استشارات إعادة بناء المهامّ الدوريّة التي «تعمل لكن لا يستطيع أحد إصلاحها».
روابط مرجعية
-
Microsoft Learn, logonType Simple Type (Task Scheduler). تعريف نوع تسجيل الدخول. حول أنّ S4U لا يحفظ كلمة المرور، وبالمقابل لا يمكن الوصول إلى الشبكة ولا إلى الملفّات المشفَّرة. ↩ ↩2 ↩3
-
Microsoft Learn, Troubleshoot issues with scheduled tasks not running. حول خطوات التشخيص: اختبار السكربت منفرداً ← تأكيد الحالة والسجلّ ← تغيير خيار الأمان، وموضع سجلّ أحداث TaskScheduler Operational. ↩ ↩2 ↩3
-
Microsoft Learn, Task Scheduler error and success constants. حول تعريف رموز الحالة والخطأ مثل SCHED_S_TASK_READY (0x41300) وSCHED_S_TASK_RUNNING (0x41301) وSCHED_S_TASK_HAS_NOT_RUN (0x41303). ↩ ↩2
-
Microsoft Learn, Manage group Managed Service Accounts. حول إدارة المتحكّم بالنطاق لكلمة مرور gMSA وجلب المضيف لها، ودعم مهامّ Task Scheduler لـ gMSA، وكون مستوى وظائف النطاق والغابة Windows Server 2012 فما بعده، ولزوم مفتاح جذر KDS (يُؤكَّد بمعرِّف الحدث 4004 في سجلّ KdsSvc Operational)، وكون اسم gMSA فريداً على مستوى الغابة، و
-PrincipalsAllowedToRetrieveManagedPasswordفيNew-ADServiceAccount، وInstall-ADServiceAccountوTest-ADServiceAccountعلى كلّ مضيف. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, New-ScheduledTaskSettingsSet. حول معاملات كائن إعداد المهمّة مثل MultipleInstances (Parallel / Queue / IgnoreNew) وStartWhenAvailable وExecutionTimeLimit (افتراضيّ 3 أيّام). ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Security Contexts for Tasks. حول سياق أمان المهمّة، وأنّ المهمّة المسجَّلة بـ Password / S4U تحتاج حقّ «تسجيل الدخول كمهمة دُفعيّة». ↩ ↩2
-
Microsoft Learn, New-ScheduledTaskPrincipal. حول تحديد حساب التشغيل بـ
-UserIdوطريقة تسجيل الدخول بـ-LogonType(None/Password/S4U/Interactive/Group/ServiceAccount/InteractiveOrPassword)، وأنّ-RunLevelيأخذLimitedوHighest. ↩ -
Microsoft Learn (أرشيف), General Task Registration. حول تعريف رسائل أحداث Microsoft-Windows-TaskScheduler 106 (تسجيل المهمّة) و113 (سُجِّلت لكن بعض المشغِّلات لا تبدأ) و116 (حُفظ التكوين لكن تعذّر حفظ بيانات الاعتماد) و140 (تحديث) و141 (حذف). ↩ ↩2 ↩3 ↩4
-
Microsoft Learn (أرشيف), Task Monitoring and Control. أحداث Microsoft-Windows-TaskScheduler 100 (بدء المهمّة) و102 (إنهاء طبيعيّ) و111 (إنهاء لتجاوز مدّة التنفيذ) و129 (تشغيل بمعرِّف عمليّة) و200/201 (بدء العمليّة واكتمالها) و202/203 (فشل إكمال العمليّة / فشل تشغيلها) و323 (إيقاف لبدء نسخة جديدة). خصوصاً أنّ اسم الرمز لـ 201 هو
ACTION_SUCCESSونصّه «Task Scheduler successfully completed task … and action …»، و202 «Task Scheduler failed to complete the … instance of the … task with action … The error value is: …»، وأنّ 202 يدلّ على تعذّر إكمال العمليّة من جانب Task Scheduler. وأنّ 201 الذي يصدره Windows الحالي إصدار 2، فيصبح النصّ «… with return code N» ويحملResultCodeفي بيانات الحدث (بما في ذلك أنّ هذه القيمة قد لا تطابق «نتيجة التشغيل الأخيرة» للمهمّة) وتعريفات رسائل 327 (إيقاف لتحوّل البطاريّة) و328 (إيقاف لزوال الخمول) و329 (انتهاء المهلة) و330 (إيقاف بطلب المستخدم). ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 -
Microsoft Learn (أرشيف), Event ID 322 — Task Properties. حول أنّ الحدث 322 (اسم الرمز
NEW_INSTANCE_IGNORED) يدلّ على «لم تبدأ لأنّ نسخة أخرى من المهمّة نفسها قيد التنفيذ أصلاً»، وإجراءات مراجعة الشروط والإعدادات. ↩ -
Microsoft Learn, What’s New in Task Scheduler. حول تأخّر مشغِّلات المهامّ غير التفاعليّة أثناء تفعيل ميزة توفير البطاريّة من Windows 10 فصاعداً. ↩
-
Microsoft Learn, robocopy. حول جدول رموز الخروج (0 لا يوجد ما يُنسخ، 1 نُسخت كلّ الملفّات بنجاح، 2 فما فوق تركيبات ملفّات إضافيّة وعدم تطابق)، وأنّ 8 فما فوق تدلّ على وقوع فشل واحد على الأقلّ أثناء النسخ. ↩
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
معالجة الأخطاء وتصميم إعادة التنفيذ في PowerShell ── من فخّ عدم نجاعة try/catch حتى القاعدة الثابتة لـ exit code وإعادة المحاولة
نرتّب من منظور عمليّ الفرق بين الخطأ المنهي وغير المنهي في PowerShell، وفخّ عدم نجاعة try/catch والقاعدة الثابتة لـ -ErrorAction Stop، وا...
دليل عملي لـ Process Monitor (ProcMon) ── تحديد «الإعدادات لا تُقرأ» وACCESS DENIED خلال 10 دقائق
أعطال مثل «عدّلت الإعدادات لكنها لا تنعكس» تُحدَّد أسبابها عبر Process Monitor (ProcMon) من واقع الوصول إلى الملفات وRegistry. نشرح استخد...
التاريخ والوقت والمناطق الزمنية في تطبيقات الأعمال ── من فخاخ DateTime إلى مبدأ التخزين بـ UTC وتصميم الاختبارات
ينزاح الوقت 9 ساعات بعد نقل الخادم. نرتّب أسباب أعطال التاريخ والوقت انطلاقاً من Kind في DateTime والتحويل الضمني. نشرح التمييز مع DateTi...
استخدام SQLite في تطبيقات C# للأعمال ── وضع WAL، والتحكّم الحصريّ، والوقاية من التلف، والتمييز عن EF Core
نرتّب هنا المعرفة العمليّة اللازمة لدمج SQLite في تطبيقات الأعمال عبر Microsoft.Data.Sqlite. نشرح وضع WAL، وSQLITE_BUSY وتوحيد مسار الكتا...
كيفيّة إنشاء خدمات Windows وتشغيلها ── من التمييز عن جدولة المهام إلى تحويل BackgroundService إلى خدمة
هل ينبغي تحويل المعالجة المقيمة إلى خدمة Windows، أم تكفي جدولة المهام؟ نرتّب من منظور عمليّ جدول القرار، وكيفيّة إنشاء الخدمة عبر .NET W...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- ما الذي يعنيه الرمز 0x1 الذي يظهر في «نتيجة التشغيل الأخيرة» في Task Scheduler؟
- 0x1 ليس خطأً من Task Scheduler نفسه، بل يعني أنّ البرنامج الذي تمّ تشغيله أعاد رمز خروج (exit code) قيمته 1. السبب يكمن في جانب السكربت. الفرق النمطيّ بين التشغيل اليدويّ والتشغيل الدوريّ هو دليل العمل الحاليّ (current directory)، ومتغيّرات البيئة والملفّ الشخصيّ (profile)، وسياسة التنفيذ (execution policy). كما يجب الانتباه إلى اتفاقيّات رمز الخروج الخاصّة ببعض الأوامر الخارجيّة، مثل robocopy الذي يُعيد القيمة 1 حتّى في الحالة الطبيعيّة. معرفة الفرق بين رموز 0x413xx التي هي رموز حالة Task Scheduler نفسه، ورموز 0x8007xxxx التي هي رموز أخطاء Windows، تمنعك من البحث في المكان الخطأ.
- لماذا يعمل الأمر يدويّاً لكنّه لا يعمل عبر Task Scheduler؟
- السبب شبه مؤكَّد أنّه الفرق في حساب التشغيل والجلسة والبيئة. عند اختيار «التشغيل بغضّ النظر عن تسجيل دخول المستخدم»، تعمل المهمّة في جلسة غير تفاعليّة، فلا تكون محرّكات الأقراص الشبكيّة المرتبطة (mapped drives) أو متغيّرات بيئة ملفّ المستخدم الشخصيّ موجودة. علاوة على ذلك، لا يمكن الوصول إلى الموارد على الشبكة عند اختيار «عدم حفظ كلمة المرور» (S4U). خطوات التشخيص الفعّالة هي: التحقّق من علامة تبويب السجلّ (History)، واختبار السكربت منفرداً، والتحويل المؤقّت إلى «التشغيل فقط عند تسجيل الدخول». وبما أنّ سجلّ المهمّة معطَّل افتراضيّاً، يجب تفعيله قبل بدء التشغيل الفعليّ.
- ما الطريقة الصحيحة لاستدعاء سكربت PowerShell من Task Scheduler؟
- حدِّد pwsh.exe (أو powershell.exe) في حقل البرنامج، والصيغة الأساسيّة للمعطيات هي -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "المسار الكامل". استخدام -File بدلاً من -Command يجعل exit n داخل السكربت هو رمز خروج العمليّة مباشرةً، ما يتيح تمييز النجاح من الفشل. اجعل المسارات داخل السكربت مبنيّة على $PSScriptRoot، ولا تضع علامات اقتباس في حقل «البدء (اختياريّ)» (وضعها يؤدّي إلى الفشل بالرمز 0x8007010B). صمِّم السكربت نفسه بحيث يُعيد صراحةً 0 عند النجاح وقيمة غير صفريّة عند الفشل.
- كيف نتجنّب توقّف المهمّة بسبب تغيير كلمة المرور؟
- عند التسجيل بخيار «التشغيل بغضّ النظر عن تسجيل الدخول»، تُحفَظ كلمة المرور، فتستمرّ المهمّة بالتوقّف بفشل تسجيل الدخول (0x8007052E) بعد تغيير كلمة المرور. في بيئة النطاق (domain)، يكون الخيار الأوّل هو gMSA (حساب خدمة مُدار جماعيّاً) الذي يدير فيه المتحكّم بالنطاق كلمة المرور تلقائيّاً، وعندها تختفي هذه المشكلة أصلاً. أمّا عند استخدام حساب خدمة مخصَّص، فأدِر مدّة صلاحيّة كلمة المرور وجرد المهامّ معاً. وأضِف كذلك آليّة تُخطِر عبر البريد الإلكترونيّ أو Teams عند الفشل، لتفادي حادثة عدم ملاحظة توقّف المهمّة لعدّة أشهر.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.