مقدمة
السلام عليكم ورحمة الله وبركاته
المقالة هذه استكمال للسلسلة السابقة
في هذا الجزء سنُلقي نظرة تفصيلية على الكود الخاص بدالة CreateProcess ، كيفية نداءها ، وماهي المتغيرات التي نحتاج لتعريفها ومن ثم تمريرها للدالة
كما ذكرنا في المقالة السابقة، سنقوم باستخدام دالة CreateProcess لانشاء عملية Notepad.exe
وهذا هو الكود الذي سنقوم بمناقشته
1 // CreateProcess.cpp : This file contains the 'main' function. Program execution begins and ends there.
2 //
3
4 #include <iostream>
5 #include <windows.h>
6 #include <stdio.h>
7 int main()
8 {
9 // Declare variables
10 STARTUPINFO si;
11 PROCESS_INFORMATION pi;
12 BOOL result;
13 DWORD exitCode;
14
15 // Initialize variables
16 ZeroMemory(&si, sizeof(si));
17 si.cb = sizeof(si);
18 ZeroMemory(&pi, sizeof(pi));
19
20 // Create a process using the command line argument
21 result = CreateProcess(
22 L"C:\\Windows\\System32\\notepad.exe", // Module name
23 NULL, // Command line
24 NULL, // Process handle not inheritable
25 NULL, // Thread handle not inheritable
26 FALSE, // Set handle inheritance to FALSE
27 0, // No creation flags
28 NULL, // Use parent's environment block
29 NULL, // Use parent's starting directory
30 &si, // Pointer to STARTUPINFO structure
31 &pi); // Pointer to PROCESS_INFORMATION structure
32
33 if (!result)
34 {
35 printf("CreateProcess failed (%d).\n", GetLastError());
36 return -1;
37 }
38
39 // Wait until child process exits
40 WaitForSingleObject(pi.hProcess, INFINITE);
41
42 // Get the exit code of the child process
43 GetExitCodeProcess(pi.hProcess, &exitCode);
44 printf("Child process exited with code %d.\n", exitCode);
45
46 // Close process and thread handles
47 CloseHandle(pi.hProcess);
48 CloseHandle(pi.hThread);
49
50 return 0;
51 }
قبل الخوض في تفاصيل الكود، من الجيد أن نعرف الـ Signature / Prototype الخاص بالدالة حتى يسهل علينا تتبّع الكود
CreateProcess Prototype
بحسب موقع مايكروسوفت فهذا الـ Syntax الخاص بالدالة
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
لنبدأ التفصيل في هذا الـSyntax :
- نلاحظ أن الدالة تُرجع قيمة من نوع BOOL ، ستكون القيمة TRUE في حال تم تنفيذ الدالة بشكل سليم ، و FALSE في حال فشلت الدالة في بدء العملية
بالنسبة للـ parameters الخاصة بالدالة نلاحظ وجود بعض العبارات مثل in و out و optional
- تستخدم هذه العبارات بين المطورين كوسيلة لشرح الدالة وماتفعله بالـ parameters
- in : اختصار لـ input والمقصود بها أن الـ parameter هذا سيتم استخدامه داخل الدالة
- out : اختصار لـ output والمقصود منه أن الدالة ستستقبل هذا المتغيّر وستقوم بارجاعه او التعديل على قيمته في حالة الـ pointers
- optional : كما ترمز الكلمة، تمرير هذا الـ parameter للدالة ليس ضروري
ننتقل الآن للـ parameters
- lpApplicationName : يشير الى البرنامج الذي سيتم تنفيذه
- lpCommandLine : بدل من تمرير اسم البرنامج في الـ parameter السابق، بامكاننا تمرير commands يتم تنفيذها في العملية، على سبيل المثال لو أردنا فتح ملف test.txt عبر الـ Notepad.exe نمرر القيمة notepad.exe test.txt لهذا الـ parameter
- lpProcessAttributes : عبارة عن pointer يشير لـ structure يسمى SECURITY_ATTRIBUTES ، للمزيد اقرأ عن هذا الـ structure
- lpThreadAttributes : يمثل pointer لنفس الـ structure السابق
- bInheritHandles : يمثل قيمة Boolean وتعبر عن هل بالامكان وراثة الـ handles الخاصة بالـ calling process
- dwCreationFlags : يستخدم هذا الـ parameter للتحكم في مرحلة انشاء العملية، بالامكان الاطلاع على جميع الـ flags من هنا
- lpEnvironment : عبارة عن pointer للـ environment variables الخاصة بالعملية
- lpCurrentDirectory : يمثل المسار الكامل الخاص بالعملية
- lpStartupInfo : عبارة عن pointer يشير لـ structure يسمى STARTUPINFO ، يمثل هذا الـ structure الاعدادات الخاصة بالعملية عند بدءها مثل حجم وشكل الـ window , العنوان وخلافه ، بالامكان القراءة عن هذا الـ structure هنا
- lpProcessInformation : عبارة عن pointer يشير الى structure يسمى PROCESS_INFORMATION هذا الـ structure يحمل العديد من المعلومات عن العملية التي تم انشاءها مثل : الـ handle الخاص بالعملية ، الـ thread الأساسي في العملية وغيره ، بالامكان القراءة عن هذا الـ structure هنا
نعود الآن للكود
انشاء عملية notepad.exe باستخدام دالة CreateProcess
1 // CreateProcess.cpp : This file contains the 'main' function. Program execution begins and ends there.
2 //
3
4 #include <iostream>
5 #include <windows.h>
6 #include <stdio.h>
7 int main()
8 {
9 // Declare variables
10 STARTUPINFO si;
11 PROCESS_INFORMATION pi;
12 BOOL result;
13 DWORD exitCode;
14
15 // Initialize variables
16 ZeroMemory(&si, sizeof(si));
17 si.cb = sizeof(si);
18 ZeroMemory(&pi, sizeof(pi));
19
20 // Create a process using the command line argument
21 result = CreateProcess(
22 L"C:\\Windows\\System32\\notepad.exe", // Module name
23 NULL, // Command line
24 NULL, // Process handle not inheritable
25 NULL, // Thread handle not inheritable
26 FALSE, // Set handle inheritance to FALSE
27 0, // No creation flags
28 NULL, // Use parent's environment block
29 NULL, // Use parent's starting directory
30 &si, // Pointer to STARTUPINFO structure
31 &pi); // Pointer to PROCESS_INFORMATION structure
32
33 if (!result)
34 {
35 printf("CreateProcess failed (%d).\n", GetLastError());
36 return -1;
37 }
38
39 // Wait until child process exits
40 WaitForSingleObject(pi.hProcess, INFINITE);
41
42 // Get the exit code of the child process
43 GetExitCodeProcess(pi.hProcess, &exitCode);
44 printf("Child process exited with code %d.\n", exitCode);
45
46 // Close process and thread handles
47 CloseHandle(pi.hProcess);
48 CloseHandle(pi.hThread);
49
50 return 0;
51 }
لنبدأ بالتفصيل :
- 10 : نلاحظ أننا قمنا بتعريف متغيّر باسم si من نوع STARTUPINFO وهو يمثّل الـ structure الذي قمنا بشرحه سابقًا، إن كنت تتساءل كيف استطعنا الوصول لهذا النوع ( STARTUPINFO ) فلأنه قمنا بادراج المكتبة windows.h في السطر رقم 5
- 11 : قمنا بتعريف متغيّر باسم pi والذي يشير كذلك للـ structure الذي سيحمل المعلومات الخاصة بالعملية التي سيتم انشاءها
- 12 : قمنا بتعريف المتغيّر result الذي سيحمل نتيجة تنفيذ الدالة
- 13 : الغرض من المتغيّر exitCode هو لمعرفة الـ exit code الخاص بعملية الـ notepad.exe بعد انتهاءها
- 16 : قمنا بعمل initialization للمتغيّر si عبر الدالة ZeroMemory ، الخطوة هذه لتهيئة الذاكرة وحذف أي قيم سابقة في الذاكرة قبل استخدامها
- 17 : قمنا بتعريف قيمة المتغيّر si.cb والذي يمثّل الـ size الخاص بالـ STARTUPINFO structure
- 18 : قمنا بتهيئة الذاكرة للمتغيّر pi
- 21 : قمنا بنداء دالة CreateProcess ونلاحظ أن القيمة العائدة من الدالة سيتم تخزينها في المتغيّر result
- 22 : قمنا بتمرير القيمة C:\\Windows\\System32\\notepad.exe والتي تمثل اسم البرنامج الذي سيتم تنفيذه ( lpApplicationName )
- 23 : قمنا بتمرير القيمة NULL كوننا عرفنا اسم البرنامج في الـ parameter السابق
- 24 : يمثل الـ lpProcessAttributes وقمنا بتمرير القيمة NULL مما يعني أنه سيتم استخدام الاعدادات الافراضية للـ Security Descriptor الخاص بعملية الـ notepad.exe
- 25 : نفس السطر السابق لكن على مستوى الـ Thread
- 26 : قمنا بتمرير القيمة FALSE والتي تعني هنا أنه لن يتم توريث الـ handles الخاصة بالـ calling process الى الـ notepad.exe process
- 27 : لم نقم بتحديد أي Creation Flags ، بالتالي سيتم تنفيذ الـ Thread الخاص بالعملية مباشرة
- 28 : لم نقم بتحديد قيمة للـ environment block ، بالتالي سيتم استخدام الـ environment block الخاص بالـ calling process
- 29 : لم نقم بتحديد قيمة للمسار، سيتم استخدام اعدادات الـ calling process
- 30 : قمنا بتمرير الـ pointer الخاص بالـ STARTUPINFO structure
- 31 : قمنا بتمرير الـ pointer الخاص بالـ PROCESS_INFORMATION structure
- 33 - 37 : في حال فشل تنفيذ الدالة CreateProcess سيتم طباعة الخطأ باستخدام دالة GetLastError
- 39 - 40 : في هذه السطرين نسمح لعملية الـ notepad.exe أن تعمل حتى يتم انهاءها بشكل يدوي من المستخدم ، يتم تنفيذ هذا عن طريق الدالة WaitForSingleObject
- 43 - 44 : قمنا بقراءة الـ exit code الخاص بالعملية باستخدام دالة GetExitCodeProcess
- 47 - 48 : قمنا باغلاق الـ handles الخاصة بالعملية وبالـ Thread الخاص بها
بعد تنفيذ الكود، سنرى أنه استطعنا بدء عملية الـ notepade.exe

[ملاحظة] هذه المقالة تمّت كتابتها خلال دراسة هذه المواضيع، فكل ما تم ذكره هنا قد يحتمل الخطأ، لكن بالإمكان العودة إلى المراجع التي إستندت عليها هذه المقالة