1 /*
2  * PROJECT:         ReactOS Utility Manager Resources DLL (UManDlg.dll)
3  * LICENSE:         GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:         Process handling functions
5  * COPYRIGHT:       Copyright 2019-2020 George Bișoc (george.bisoc@reactos.org)
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include "umandlg.h"
11 
12 /* FUNCTIONS ******************************************************************/
13 
14 /**
15  * @GetProcessID
16  *
17  * Returns the process executable ID based on the given executable name.
18  *
19  * @param[in]   lpszProcessName
20  *     The name of the executable process.
21  *
22  * @return
23  *      Returns the ID number of the process, otherwise 0.
24  *
25  */
26 DWORD GetProcessID(IN LPCWSTR lpszProcessName)
27 {
28     PROCESSENTRY32W Process;
29 
30     /* Create a snapshot and check if the given process name matches with the one from the process entry structure */
31     HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
32 
33     if (hSnapshot == INVALID_HANDLE_VALUE)
34         return 0;
35 
36     /* Get the whole size of the structure */
37     Process.dwSize = sizeof(Process);
38 
39     /* Enumerate the processes */
40     if (Process32FirstW(hSnapshot, &Process))
41     {
42         do
43         {
44             if (_wcsicmp(Process.szExeFile, lpszProcessName) == 0)
45             {
46                 /* The names match, return the process ID we're interested */
47                 CloseHandle(hSnapshot);
48                 return Process.th32ProcessID;
49             }
50         }
51         while (Process32NextW(hSnapshot, &Process));
52     }
53 
54     CloseHandle(hSnapshot);
55     return 0;
56 }
57 
58 /**
59  * @IsProcessRunning
60  *
61  * Checks if a process is running.
62  *
63  * @param[in]   lpszProcessName
64  *     The name of the executable process.
65  *
66  * @return
67  *     Returns TRUE if the given process' name is running,
68  *     FALSE otherwise.
69  *
70  */
71 BOOL IsProcessRunning(IN LPCWSTR lpszProcessName)
72 {
73     DWORD dwReturn, dwProcessID;
74     HANDLE hProcess;
75 
76     /* Get the process ID */
77     dwProcessID = GetProcessID(lpszProcessName);
78     if (dwProcessID == 0)
79     {
80         return FALSE;
81     }
82 
83     /* Synchronize the process to get its signaling state */
84     hProcess = OpenProcess(SYNCHRONIZE, FALSE, dwProcessID);
85     if (!hProcess)
86     {
87         DPRINT("IsProcessRunning(): Failed to open the process! (Error: %lu)\n", GetLastError());
88         return FALSE;
89     }
90 
91     /* Wait for the process */
92     dwReturn = WaitForSingleObject(hProcess, 0);
93     if (dwReturn == WAIT_TIMEOUT)
94     {
95         /* The process is still running */
96         CloseHandle(hProcess);
97         return TRUE;
98     }
99 
100     CloseHandle(hProcess);
101     return FALSE;
102 }
103 
104 /**
105  * @LaunchProcess
106  *
107  * Executes a process.
108  *
109  * @param[in]   lpProcessName
110  *     The name of the executable process.
111  *
112  * @return
113  *     Returns TRUE if the process has been launched successfully,
114  *     FALSE otherwise.
115  *
116  */
117 BOOL LaunchProcess(IN LPCWSTR lpszProcessName)
118 {
119     STARTUPINFOW si;
120     PROCESS_INFORMATION pi;
121     HANDLE hUserToken, hProcessToken;
122     BOOL bSuccess;
123     WCHAR ExpandedCmdLine[MAX_PATH];
124 
125     /* Expand the process path string */
126     ExpandEnvironmentStringsW(lpszProcessName, ExpandedCmdLine, ARRAYSIZE(ExpandedCmdLine));
127 
128     ZeroMemory(&pi, sizeof(pi));
129     ZeroMemory(&si, sizeof(si));
130     si.cb = sizeof(si);
131     si.dwFlags = STARTF_USESHOWWINDOW;
132     si.wShowWindow = SW_SHOWNORMAL;
133 
134     /* Get the token of the parent (current) process of the application */
135     bSuccess = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &hUserToken);
136     if (!bSuccess)
137     {
138         DPRINT("OpenProcessToken() failed with error -> %lu\n", GetLastError());
139         return FALSE;
140     }
141 
142     /* Duplicate a new token so that we can use it to create our process */
143     bSuccess = DuplicateTokenEx(hUserToken, TOKEN_ALL_ACCESS, NULL, SecurityIdentification, TokenPrimary, &hProcessToken);
144     if (!bSuccess)
145     {
146         DPRINT("DuplicateTokenEx() failed with error -> %lu\n", GetLastError());
147         CloseHandle(hUserToken);
148         return FALSE;
149     }
150 
151     /* Finally create the process */
152     bSuccess = CreateProcessAsUserW(hProcessToken,
153                                     NULL,
154                                     ExpandedCmdLine,
155                                     NULL,
156                                     NULL,
157                                     FALSE,
158                                     0, // DETACHED_PROCESS, NORMAL_PRIORITY_CLASS
159                                     NULL,
160                                     NULL,
161                                     &si,
162                                     &pi);
163 
164     if (!bSuccess)
165     {
166         DPRINT("CreateProcessAsUserW() failed with error -> %lu\n", GetLastError());
167         CloseHandle(hUserToken);
168         CloseHandle(hProcessToken);
169         return FALSE;
170     }
171 
172     CloseHandle(pi.hProcess);
173     CloseHandle(pi.hThread);
174     CloseHandle(hUserToken);
175     CloseHandle(hProcessToken);
176     return TRUE;
177 }
178 
179 /**
180  * @CloseProcess
181  *
182  * Closes a process.
183  *
184  * @param[in]   lpszProcessName
185  *     The name of the executable process.
186  *
187  * @return
188  *     Returns TRUE if the process has been terminated successfully,
189  *     FALSE otherwise.
190  *
191  */
192 BOOL CloseProcess(IN LPCWSTR lpszProcessName)
193 {
194     HANDLE hProcess;
195     DWORD dwProcessID;
196 
197     /* Get the process ID */
198     dwProcessID = GetProcessID(lpszProcessName);
199     if (dwProcessID == 0)
200     {
201         return FALSE;
202     }
203 
204     /* Make sure that the given process ID is not ours, the parent process, so that we do not kill ourselves */
205     if (dwProcessID == GetCurrentProcessId())
206     {
207         return FALSE;
208     }
209 
210     /* Open the process so that we can terminate it */
211     hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessID);
212     if (!hProcess)
213     {
214         DPRINT("CloseProcess(): Failed to open the process for termination! (Error: %lu)\n", GetLastError());
215         return FALSE;
216     }
217 
218     /* Terminate it */
219     TerminateProcess(hProcess, 0);
220     CloseHandle(hProcess);
221     return TRUE;
222 }
223