1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*=============================================================================
6**
7** Source: test11.c
8**
9** Purpose:
10**
11** Test to ensure proper operation of the DuplicateHandle API.
12** The test launches a trivial child process, then opens
13** a handle to it using OpenProcess. It then duplicates that
14** handle and uses it to wait for the child process to terminate,
15** and then checks the exit code of the child process in order to
16** verify that it was in fact a handle to the correct
17** process. The test tries to duplicate the handle again after
18** the process has been closed, to verify that failure ensues.
19**
20** Dependencies: PAL_Initialize
21** PAL_Terminate
22** Fail
23** ZeroMemory
24** GetCurrentDirectoryW
25** CreateProcessW
26** WaitForSingleObject
27** CreateMutexW
28** ReleaseMutex
29** CloseHandle
30** GetLastError
31** strlen
32** strncpy
33**
34**
35**===========================================================================*/
36#include <palsuite.h>
37#include "myexitcode.h"
38
39
40static const char* rgchPathDelim = "\\";
41
42
43int
44mkAbsoluteFilename( LPSTR dirName,
45 DWORD dwDirLength,
46 LPCSTR fileName,
47 DWORD dwFileLength,
48 LPSTR absPathName )
49{
50 DWORD sizeDN, sizeFN, sizeAPN;
51
52 sizeDN = strlen( dirName );
53 sizeFN = strlen( fileName );
54 sizeAPN = (sizeDN + 1 + sizeFN + 1);
55
56 /* ensure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
57 if( sizeAPN > _MAX_PATH )
58 {
59 return ( 0 );
60 }
61
62 strncpy( absPathName, dirName, dwDirLength +1 );
63 strncpy( absPathName, rgchPathDelim, 2 );
64 strncpy( absPathName, fileName, dwFileLength +1 );
65
66 return (sizeAPN);
67
68}
69
70
71int __cdecl main( int argc, char **argv )
72
73{
74 const char* rgchChildFile = "childprocess";
75
76 STARTUPINFO si;
77 PROCESS_INFORMATION pi;
78
79 DWORD dwError;
80 DWORD dwExitCode;
81 DWORD dwFileLength;
82 DWORD dwDirLength;
83 DWORD dwSize;
84 DWORD dwRet;
85
86 HANDLE hMutex;
87 HANDLE hChildProcess;
88 HANDLE hDupChildProcess;
89
90 char rgchDirName[_MAX_DIR];
91 char absPathBuf[_MAX_PATH];
92 char* rgchAbsPathName;
93
94 BOOL ret = FAIL;
95 BOOL bChildDone = FALSE;
96 WCHAR wszMutexName[] = { 'T','E','S','T','1','1','\0' };
97
98 /* initialize the PAL */
99 if( PAL_Initialize(argc, argv) != 0 )
100 {
101 return( FAIL );
102 }
103
104 /* create a mutex to synchronize with the child process */
105 hMutex = CreateMutexW( NULL, TRUE, wszMutexName );
106 if( hMutex == NULL )
107 {
108 Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() );
109 }
110
111 /* zero our process and startup info structures */
112 ZeroMemory( &si, sizeof(si) );
113 si.cb = sizeof( si );
114 ZeroMemory( &pi, sizeof(pi) );
115
116 /* build the absolute path to the child process */
117 rgchAbsPathName = &absPathBuf[0];
118 dwFileLength = strlen( rgchChildFile );
119
120 dwDirLength = GetCurrentDirectory( _MAX_PATH, rgchDirName );
121 if( dwDirLength == 0 )
122 {
123 dwError = GetLastError();
124 if( ReleaseMutex( hMutex ) == 0 )
125 {
126 Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
127 }
128 if( CloseHandle( hMutex ) == 0 )
129 {
130 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
131 }
132 Fail( "GetCurrentDirectory call failed with error code %d\n",
133 dwError );
134 }
135
136 dwSize = mkAbsoluteFilename( rgchDirName,
137 dwDirLength,
138 rgchChildFile,
139 dwFileLength,
140 rgchAbsPathName );
141 if( dwSize == 0 )
142 {
143 if( ReleaseMutex( hMutex ) == 0 )
144 {
145 Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
146 }
147 if( CloseHandle( hMutex ) == 0 )
148 {
149 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
150 }
151 Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ",
152 "not build absolute path name to file\n. Exiting.\n" );
153 }
154
155 /* launch the child process */
156 if( !CreateProcess( NULL, /* module name to execute */
157 rgchAbsPathName, /* command line */
158 NULL, /* process handle not */
159 /* inheritable */
160 NULL, /* thread handle not */
161 /*inheritable */
162 FALSE, /* handle inheritance */
163 CREATE_NEW_CONSOLE, /* dwCreationFlags */
164 NULL, /* use parent's environment */
165 NULL, /* use parent's starting */
166 /* directory */
167 &si, /* startup info struct */
168 &pi ) /* process info struct */
169 )
170 {
171 dwError = GetLastError();
172 if( ReleaseMutex( hMutex ) == 0 )
173 {
174 Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
175 }
176 if( CloseHandle( hMutex ) == 0 )
177 {
178 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
179 }
180 Fail( "CreateProcess call failed with error code %d\n",
181 dwError );
182 }
183
184 /* open another handle to the child process */
185 hChildProcess = OpenProcess( PROCESS_ALL_ACCESS, /* access */
186 FALSE, /* inheritable */
187 pi.dwProcessId /* process id */
188 );
189 if( hChildProcess == NULL )
190 {
191 dwError = GetLastError();
192 if( ReleaseMutex( hMutex ) == 0 )
193 {
194 Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
195 }
196 Trace( "ERROR:%lu:OpenProcess call failed\n", dwError );
197 goto cleanup3;
198 }
199
200 /* duplicate the child process handle */
201 if( ! DuplicateHandle( GetCurrentProcess(),
202 hChildProcess,
203 GetCurrentProcess(),
204 &hDupChildProcess,
205 GENERIC_READ|GENERIC_WRITE,
206 FALSE,
207 DUPLICATE_SAME_ACCESS) )
208 {
209 Trace( "ERROR:%lu:DuplicateHandle() call failed\n", GetLastError() );
210 if( ReleaseMutex( hMutex ) == 0 )
211 {
212 Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
213 }
214 goto cleanup2;
215 }
216
217 /* release the mutex so the child can proceed */
218 if( ReleaseMutex( hMutex ) == 0 )
219 {
220 Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
221 goto cleanup1;
222 }
223
224 /* wait for the child process to complete, using the new handle */
225 dwRet = WaitForSingleObject( hDupChildProcess, 10000 );
226 if( dwRet != WAIT_OBJECT_0 )
227 {
228 Trace( "ERROR:WaitForSingleObject call returned %lu, "
229 "expected WAIT_OBJECT_0",
230 dwRet );
231 goto cleanup1;
232 }
233
234 /* remember that we waited until the child was finished */
235 bChildDone = TRUE;
236
237 /* check the exit code from the process -- this is a bit of an */
238 /* extra verification that we opened the correct process handle */
239 if( ! GetExitCodeProcess( hDupChildProcess, &dwExitCode ) )
240 {
241 Trace( "ERROR:%lu:GetExitCodeProcess call failed\n", GetLastError() );
242 goto cleanup1;
243 }
244
245 /* verification */
246 if( (dwExitCode & 0xFF) != (TEST_EXIT_CODE & 0xFF) )
247 {
248 Trace( "GetExitCodeProcess returned an incorrect exit code %d, "
249 "expected value is %d\n",
250 (dwExitCode & 0xFF),
251 (TEST_EXIT_CODE & 0xFF));
252 goto cleanup1;
253 }
254
255 /* close the duplicate handle */
256 if( ! CloseHandle( hDupChildProcess ) )
257 {
258 Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
259 goto cleanup2;
260 }
261
262 /* close the child process handle */
263 if( ! CloseHandle ( hChildProcess ) )
264 {
265 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
266 goto cleanup3;
267 }
268
269 /* try to call duplicate handle on the closed child process handle */
270 if( DuplicateHandle( GetCurrentProcess(),
271 hChildProcess,
272 GetCurrentProcess(),
273 &hDupChildProcess,
274 GENERIC_READ|GENERIC_WRITE,
275 FALSE,
276 DUPLICATE_SAME_ACCESS) )
277 {
278 Trace( "ERROR:%lu:DuplicateHandle call succeeded on "
279 "a closed process handle, expected ERROR_INVALID_HANDLE\n" );
280 if( ! CloseHandle( hDupChildProcess ) )
281 {
282 Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
283 }
284 goto cleanup3;
285 }
286
287 /* verify that the last error was ERROR_INVALID_HANDLE */
288 dwRet = GetLastError();
289 if( dwRet != ERROR_INVALID_HANDLE )
290 {
291 Trace( "ERROR:DuplicateHandle returned %lu, "
292 "expected ERROR_INVALID_HANDLE\n",
293 dwRet );
294 goto cleanup3;
295 }
296
297
298 /* success if we get here */
299 ret = PASS;
300
301 /* skip the cleanup stuff that's already done */
302 goto cleanup3;
303
304
305cleanup1:
306 /* close our duplicate handle */
307 if( ! CloseHandle( hDupChildProcess ) )
308 {
309 Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
310 ret = FAIL;
311 }
312
313cleanup2:
314 /* wait on the child process to complete if necessary */
315 if( ! bChildDone )
316 {
317 dwRet = WaitForSingleObject( hChildProcess, 10000 );
318 if( dwRet != WAIT_OBJECT_0 )
319 {
320 Trace( "ERROR:WaitForSingleObject call returned %lu, "
321 "expected WAIT_OBJECT_0",
322 dwRet );
323 ret = FAIL;
324 }
325 }
326
327 /* close our child process handle */
328 if( CloseHandle ( hChildProcess ) == 0 )
329 {
330 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
331 ret = FAIL;
332 }
333
334cleanup3:
335 /* close all our other handles */
336 if( CloseHandle ( pi.hProcess ) == 0 )
337 {
338 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
339 ret = FAIL;
340 }
341 if( CloseHandle ( pi.hThread ) == 0 )
342 {
343 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
344 ret = FAIL;
345 }
346 if( CloseHandle( hMutex ) == 0 )
347 {
348 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
349 ret = FAIL;
350 }
351
352 if( ret == FAIL )
353 {
354 Fail( "test failed\n" );
355 }
356
357
358
359 /* terminate the PAL */
360 PAL_Terminate();
361
362 /* return success */
363 return PASS;
364}
365