Process Creation Functions Part 2
مقدمة
السلام عليكم ورحمة الله وبركاته
المقالة هذه استكمال للسلسلة السابقة
في هذا الجزء سنُلقي نظرة تفصيلية على الكود الخاص بدالة 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
والمقصود منه أن الدالة ستستقبل هذا المتغيّر وستقوم بارجاعه او التعديل على قيمته في حالة الـ pointersoptional
: كما ترمز الكلمة، تمرير هذا الـ parameter للدالة ليس ضروري
- ننتقل الآن للـ parameters
lpApplicationName
: يشير الى البرنامج الذي سيتم تنفيذهlpCommandLine
: بدل من تمرير اسم البرنامج في الـ parameter السابق، بامكاننا تمريرcommands
يتم تنفيذها في العملية، على سبيل المثال لو أردنا فتح ملفtest.txt
عبر الـNotepad.exe
نمرر القيمةnotepad.exe test.txt
لهذا الـ parameterlpProcessAttributes
: عبارة عن pointer يشير لـ structure يسمىSECURITY_ATTRIBUTES
، للمزيد اقرأ عن هذا الـ structurelpThreadAttributes
: يمثل 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
structure18
: قمنا بتهيئة الذاكرة للمتغيّرpi
21
: قمنا بنداء دالةCreateProcess
ونلاحظ أن القيمة العائدة من الدالة سيتم تخزينها في المتغيّرresult
22
: قمنا بتمرير القيمةC:\\Windows\\System32\\notepad.exe
والتي تمثل اسم البرنامج الذي سيتم تنفيذه (lpApplicationName
)23
: قمنا بتمرير القيمةNULL
كوننا عرفنا اسم البرنامج في الـ parameter السابق24
: يمثل الـlpProcessAttributes
وقمنا بتمرير القيمةNULL
مما يعني أنه سيتم استخدام الاعدادات الافراضية للـSecurity Descriptor
الخاص بعملية الـnotepad.exe
25
: نفس السطر السابق لكن على مستوى الـ Thread26
: قمنا بتمرير القيمةFALSE
والتي تعني هنا أنه لن يتم توريث الـ handles الخاصة بالـcalling process
الى الـnotepad.exe
process27
: لم نقم بتحديد أيCreation Flags
، بالتالي سيتم تنفيذ الـ Thread الخاص بالعملية مباشرة28
: لم نقم بتحديد قيمة للـenvironment block
، بالتالي سيتم استخدام الـenvironment block
الخاص بالـcalling process
29
: لم نقم بتحديد قيمة للمسار، سيتم استخدام اعدادات الـcalling process
30
: قمنا بتمرير الـ pointer الخاص بالـSTARTUPINFO
structure31
: قمنا بتمرير الـ pointer الخاص بالـPROCESS_INFORMATION
structure33
-37
: في حال فشل تنفيذ الدالةCreateProcess
سيتم طباعة الخطأ باستخدام دالةGetLastError
39
-40
: في هذه السطرين نسمح لعملية الـnotepad.exe
أن تعمل حتى يتم انهاءها بشكل يدوي من المستخدم ، يتم تنفيذ هذا عن طريق الدالةWaitForSingleObject
43
-44
: قمنا بقراءة الـexit code
الخاص بالعملية باستخدام دالةGetExitCodeProcess
47
-48
: قمنا باغلاق الـ handles الخاصة بالعملية وبالـ Thread الخاص بها
بعد تنفيذ الكود، سنرى أنه استطعنا بدء عملية الـ notepade.exe
ℹ️ [ملاحظة] هذه المقالة تمّت كتابتها خلال دراسة هذه المواضيع، فكل ما تم ذكره هنا قد يحتمل الخطأ، لكن بالإمكان العودة إلى المراجع التي إستندت عليها هذه المقالة