xref: /trunk/main/sal/osl/w32/procimpl.cxx (revision 22076bf1)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sal.hxx"
26 
27 #define UNICODE
28 #define _UNICODE
29 
30 #ifndef WIN32_LEAN_AND_MEAN
31 #   define WIN32_LEAN_AND_MEAN
32 # ifdef _MSC_VER
33 #   pragma warning(push,1) /* disable warnings within system headers */
34 # endif
35 #   include <windows.h>
36 # ifdef _MSC_VER
37 #   pragma warning(pop)
38 # endif
39 #   include <tchar.h>
40 #   undef WIN32_LEAN_AND_MEAN
41 #endif
42 #include "procimpl.h"
43 #include <rtl/ustring.hxx>
44 #include <rtl/ustrbuf.hxx>
45 #include "secimpl.h"
46 #include <osl/file.hxx>
47 
48 #include <list>
49 #include <vector>
50 #include <algorithm>
51 #include <string>
52 
53 //#################################################
54 extern "C" oslFileHandle SAL_CALL osl_createFileHandleFromOSHandle( HANDLE hFile, sal_uInt32 uFlags );
55 
56 //#################################################
57 const sal_Unicode NAME_VALUE_SEPARATOR = TEXT('=');
58 const sal_Char* SPACE = " ";
59 const rtl::OUString ENV_COMSPEC = rtl::OUString::createFromAscii("COMSPEC");
60 const rtl::OUString QUOTE = rtl::OUString::createFromAscii("\"");
61 
62 namespace /* private */
63 {
64     //#################################################
65     typedef std::list<rtl::OUString> string_container_t;
66     typedef string_container_t::iterator string_container_iterator_t;
67     typedef string_container_t::const_iterator string_container_const_iterator_t;
68     typedef std::pair<string_container_iterator_t, string_container_iterator_t> iterator_pair_t;
69     typedef std::vector<sal_Unicode > environment_container_t;
70 
71     //#################################################
72     /* Function object that compares two strings that are
73        expected to be environment variables in the form
74        "name=value". Only the 'name' part will be compared.
75        The comparison is in upper case and returns true
76        if the first of both strings is less than the
77        second one. */
78     struct less_environment_variable :
79         public std::binary_function<rtl::OUString, rtl::OUString, bool>
80     {
operator ()__anon216b588c0111::less_environment_variable81         bool operator() (const rtl::OUString& lhs, const rtl::OUString& rhs) const
82         {
83             OSL_ENSURE((lhs.indexOf(NAME_VALUE_SEPARATOR) > -1) && \
84                         (rhs.indexOf(NAME_VALUE_SEPARATOR) > -1), \
85                         "Malformed environment variable");
86 
87             // Windows compares environment variables uppercase
88             // so we do it, too
89             return (rtl_ustr_compare_WithLength(
90                 lhs.toAsciiUpperCase().pData->buffer,
91                 lhs.indexOf(NAME_VALUE_SEPARATOR),
92                 rhs.toAsciiUpperCase().pData->buffer,
93                 rhs.indexOf(NAME_VALUE_SEPARATOR)) < 0);
94         }
95     };
96 
97     //#################################################
98     /* Function object used by for_each algorithm to
99        calculate the sum of the length of all strings
100        in a string container. */
101     class sum_of_string_lengths
102     {
103     public:
104         //--------------------------------
sum_of_string_lengths()105         sum_of_string_lengths() : sum_(0) {}
106 
107         //--------------------------------
operator ()(const rtl::OUString & string)108         void operator() (const rtl::OUString& string)
109         {
110             OSL_ASSERT(string.getLength());
111 
112             // always include the terminating '\0'
113             if (string.getLength())
114                 sum_ += string.getLength() + 1;
115         }
116 
117         //--------------------------------
operator size_t() const118         operator size_t () const
119         {
120             return sum_;
121         }
122     private:
123         size_t sum_;
124     };
125 
126     //#################################################
calc_sum_of_string_lengths(const string_container_t & string_cont)127     inline size_t calc_sum_of_string_lengths(const string_container_t& string_cont)
128     {
129         return std::for_each(
130             string_cont.begin(), string_cont.end(), sum_of_string_lengths());
131     }
132 
133     //#################################################
read_environment(string_container_t * environment)134     void read_environment(/*out*/ string_container_t* environment)
135     {
136         // GetEnvironmentStrings returns a sorted list, Windows
137         // sorts environment variables upper case
138         LPTSTR env = reinterpret_cast<LPTSTR>(GetEnvironmentStrings());
139         LPTSTR p   = env;
140 
141         while (size_t l = _tcslen(p))
142         {
143             environment->push_back(reinterpret_cast<const sal_Unicode*>(p));
144             p += l + 1;
145         }
146         FreeEnvironmentStrings(env);
147     }
148 
149     //#################################################
150     /* the environment list must be sorted, new values
151     should either replace existing ones or should be
152     added to the list, environment variables will
153     be handled case-insensitive */
create_merged_environment(rtl_uString * env_vars[],sal_uInt32 env_vars_count,string_container_t * merged_env)154     bool create_merged_environment(
155         rtl_uString* env_vars[],
156         sal_uInt32 env_vars_count,
157         /*in|out*/ string_container_t* merged_env)
158     {
159         OSL_ASSERT(env_vars && env_vars_count > 0 && merged_env);
160 
161         read_environment(merged_env);
162 
163         for (sal_uInt32 i = 0; i < env_vars_count; i++)
164         {
165             rtl::OUString env_var = rtl::OUString(env_vars[i]);
166 
167             if (env_var.getLength() == 0)
168                 return false;
169 
170             iterator_pair_t iter_pair = std::equal_range(
171                 merged_env->begin(),
172                 merged_env->end(),
173                 env_var,
174                 less_environment_variable());
175 
176             if (env_var.indexOf(NAME_VALUE_SEPARATOR) == -1)
177             {
178                 merged_env->erase(iter_pair.first, iter_pair.second);
179             }
180             else
181             {
182                 if (iter_pair.first != iter_pair.second) // found
183                     *iter_pair.first = env_var;
184                 else // not found
185                     merged_env->insert(iter_pair.first, env_var);
186             }
187         }
188         return true;
189     }
190 
191     //#################################################
192     /* Create a merged environment */
setup_process_environment(rtl_uString * environment_vars[],sal_uInt32 n_environment_vars,environment_container_t & environment)193     bool setup_process_environment(
194         rtl_uString* environment_vars[],
195         sal_uInt32 n_environment_vars,
196         /*in|out*/ environment_container_t& environment)
197     {
198         string_container_t merged_env;
199 	    if (!create_merged_environment(environment_vars, n_environment_vars, &merged_env))
200 	        return false;
201 
202         // allocate enough space for the '\0'-separated environment strings and
203         // a final '\0'
204         environment.resize(calc_sum_of_string_lengths(merged_env) + 1);
205 
206         string_container_const_iterator_t iter = merged_env.begin();
207         string_container_const_iterator_t iter_end = merged_env.end();
208 
209         sal_uInt32 pos = 0;
210         for (/**/; iter != iter_end; ++iter)
211         {
212             rtl::OUString envv = *iter;
213 
214             OSL_ASSERT(envv.getLength());
215 
216             sal_uInt32 n = envv.getLength() + 1; // copy the final '\0', too
217             rtl_copyMemory(
218                 reinterpret_cast<void*>(&environment[pos]),
219                 reinterpret_cast<const void*>(envv.getStr()),
220                 n * sizeof(sal_Unicode));
221             pos += n;
222         }
223         environment[pos] = 0; // append a final '\0'
224 
225         return true;
226     }
227 
228     //##########################################################
229     /*  In contrast to the Win32 API function CreatePipe with
230         this function the caller is able to determine separately
231         which handle of the pipe is inheritable. */
create_pipe(PHANDLE p_read_pipe,bool b_read_pipe_inheritable,PHANDLE p_write_pipe,bool b_write_pipe_inheritable,LPVOID p_security_descriptor=NULL,DWORD pipe_size=0)232     bool create_pipe(
233         PHANDLE p_read_pipe,
234         bool    b_read_pipe_inheritable,
235         PHANDLE p_write_pipe,
236         bool    b_write_pipe_inheritable,
237         LPVOID  p_security_descriptor = NULL,
238         DWORD   pipe_size = 0)
239     {
240         SECURITY_ATTRIBUTES	sa;
241 	    sa.nLength              = sizeof(SECURITY_ATTRIBUTES);
242 	    sa.lpSecurityDescriptor = p_security_descriptor;
243 	    sa.bInheritHandle       = b_read_pipe_inheritable || b_write_pipe_inheritable;
244 
245 	    BOOL   bRet  = FALSE;
246 	    HANDLE hTemp = NULL;
247 
248 	    if (!b_read_pipe_inheritable && b_write_pipe_inheritable)
249 	    {
250 	        bRet = CreatePipe(&hTemp, p_write_pipe, &sa, pipe_size);
251 
252 	        if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
253 	                        GetCurrentProcess(), p_read_pipe, 0, FALSE,
254 	                        DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
255 	        {
256 	            CloseHandle(hTemp);
257 	            CloseHandle(*p_read_pipe);
258 	            return false;
259 	        }
260 	    }
261 	    else if (b_read_pipe_inheritable && !b_write_pipe_inheritable)
262 	    {
263 	        bRet = CreatePipe(p_read_pipe, &hTemp, &sa, pipe_size);
264 
265 	        if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
266 	                        GetCurrentProcess(), p_write_pipe, 0, FALSE,
267 	                        DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
268 	        {
269 	            CloseHandle(hTemp);
270 	            CloseHandle(*p_write_pipe);
271 	            return false;
272 	        }
273 	    }
274 	    else
275 	    {
276 	        bRet = CreatePipe(p_read_pipe, p_write_pipe, &sa, pipe_size);
277 	    }
278 	    return bRet;
279     }
280 
281     //#########################################################
282     // Add a quote sign to the start and the end of a string
283     // if not already present
quote_string(const rtl::OUString & string)284     rtl::OUString quote_string(const rtl::OUString& string)
285     {
286         rtl::OUStringBuffer quoted;
287         if (string.indexOf(QUOTE) != 0)
288             quoted.append(QUOTE);
289 
290         quoted.append(string);
291 
292         if (string.lastIndexOf(QUOTE) != (string.getLength() - 1))
293             quoted.append(QUOTE);
294 
295         return quoted.makeStringAndClear();
296     }
297 
298     //The parameter path must be a system path. If it is longer than 260 characters
299     //then it is shortened using the GetShortPathName function. This function only
300     //works if the path exists. Because "path" can be the path to an executable, it
301     //may not have the file extension ".exe". However, if the file on disk has the
302     //".exe" extension, then the function will fail. In this case a second attempt
303     //is started by adding the parameter "extension" to "path".
getShortPath(rtl::OUString const & path,rtl::OUString const & extension)304     rtl::OUString getShortPath(rtl::OUString const & path, rtl::OUString const & extension)
305     {
306         rtl::OUString ret(path);
307         if (path.getLength() > 260)
308         {
309             std::vector<sal_Unicode> vec(path.getLength() + 1);
310             //GetShortPathNameW only works if the file can be found!
311             const DWORD len = GetShortPathNameW(
312                 reinterpret_cast<LPCWSTR>(path.getStr()), reinterpret_cast<LPWSTR>(&vec[0]), path.getLength() + 1);
313 
314             if (!len && GetLastError() == ERROR_FILE_NOT_FOUND
315                 && extension.getLength())
316             {
317                 const rtl::OUString extPath(path + extension);
318                 std::vector<sal_Unicode > vec2( extPath.getLength() + 1);
319                 const DWORD len2 = GetShortPathNameW(
320                     reinterpret_cast<LPCWSTR>(extPath.getStr()), reinterpret_cast<LPWSTR>(&vec2[0]), extPath.getLength() + 1);
321                 ret = rtl::OUString(&vec2[0], len2);
322             }
323             else
324             {
325                 ret = rtl::OUString(&vec[0], len);
326             }
327         }
328         return ret;
329     }
330     //##########################################################
331     // Returns the system path of the executable which can either
332     // be provided via the strImageName parameter or as first
333     // element of the strArguments list.
334     // The returned path will be quoted if it contains spaces.
get_executable_path(rtl_uString * image_name,rtl_uString * cmdline_args[],sal_uInt32 n_cmdline_args,bool search_path)335     rtl::OUString get_executable_path(
336         rtl_uString* image_name,
337         rtl_uString* cmdline_args[],
338         sal_uInt32 n_cmdline_args,
339         bool search_path)
340     {
341         rtl::OUString exe_name;
342 
343         if (image_name)
344             exe_name = image_name;
345         else if (n_cmdline_args)
346             exe_name = rtl::OUString(cmdline_args[0]);
347 
348         rtl::OUString exe_url = exe_name;
349         if (search_path)
350             osl_searchFileURL(exe_name.pData, NULL, &exe_url.pData);
351 
352         rtl::OUString exe_path;
353         if (osl_File_E_None != osl::FileBase::getSystemPathFromFileURL(exe_url, exe_path))
354             return rtl::OUString();
355 
356         exe_path = getShortPath(exe_path, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".exe")));
357 
358         if (exe_path.indexOf(' ') != -1)
359             exe_path = quote_string(exe_path);
360 
361         return exe_path;
362     }
363 
364     //##########################################################
get_file_extension(const rtl::OUString & file_name)365     rtl::OUString get_file_extension(const rtl::OUString& file_name)
366     {
367         sal_Int32 index = file_name.lastIndexOf('.');
368         if ((index != -1) && ((index + 1) < file_name.getLength()))
369             return file_name.copy(index + 1);
370 
371         return rtl::OUString();
372     }
373 
374     //##########################################################
is_batch_file(const rtl::OUString & file_name)375     bool is_batch_file(const rtl::OUString& file_name)
376     {
377         rtl::OUString ext = get_file_extension(file_name);
378         return (ext.equalsIgnoreAsciiCaseAscii("bat") ||
379                 ext.equalsIgnoreAsciiCaseAscii("cmd") ||
380                 ext.equalsIgnoreAsciiCaseAscii("btm"));
381     }
382 
383     //##########################################################
get_batch_processor()384     rtl::OUString get_batch_processor()
385     {
386         rtl::OUString comspec;
387         osl_getEnvironment(ENV_COMSPEC.pData, &comspec.pData);
388 
389         OSL_ASSERT(comspec.getLength());
390 
391         /* check if comspec path contains blanks and quote it if any */
392         if (comspec.indexOf(' ') != -1)
393             comspec = quote_string(comspec);
394 
395         return comspec;
396     }
397 
398 } // namespace private
399 
400 
401 //#################################################
osl_executeProcess(rtl_uString * strImageName,rtl_uString * strArguments[],sal_uInt32 nArguments,oslProcessOption Options,oslSecurity Security,rtl_uString * strDirectory,rtl_uString * strEnvironmentVars[],sal_uInt32 nEnvironmentVars,oslProcess * pProcess)402 oslProcessError SAL_CALL osl_executeProcess(
403 	rtl_uString *strImageName,
404 	rtl_uString *strArguments[],
405 	sal_uInt32   nArguments,
406 	oslProcessOption Options,
407 	oslSecurity Security,
408 	rtl_uString *strDirectory,
409 	rtl_uString *strEnvironmentVars[],
410 	sal_uInt32   nEnvironmentVars,
411 	oslProcess *pProcess
412 )
413 {
414 	return osl_executeProcess_WithRedirectedIO(
415 		strImageName,
416 		strArguments,
417 		nArguments,
418 		Options,
419 		Security,
420 		strDirectory,
421 		strEnvironmentVars,
422 		nEnvironmentVars,
423 		pProcess,
424 		NULL, NULL, NULL );
425 }
426 
427 //#################################################
osl_executeProcess_WithRedirectedIO(rtl_uString * ustrImageName,rtl_uString * ustrArguments[],sal_uInt32 nArguments,oslProcessOption Options,oslSecurity Security,rtl_uString * ustrDirectory,rtl_uString * ustrEnvironmentVars[],sal_uInt32 nEnvironmentVars,oslProcess * pProcess,oslFileHandle * pProcessInputWrite,oslFileHandle * pProcessOutputRead,oslFileHandle * pProcessErrorRead)428 oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
429 	rtl_uString *ustrImageName,
430 	rtl_uString *ustrArguments[],
431 	sal_uInt32   nArguments,
432 	oslProcessOption Options,
433 	oslSecurity Security,
434 	rtl_uString *ustrDirectory,
435 	rtl_uString *ustrEnvironmentVars[],
436 	sal_uInt32 nEnvironmentVars,
437 	oslProcess *pProcess,
438 	oslFileHandle *pProcessInputWrite,
439 	oslFileHandle *pProcessOutputRead,
440 	oslFileHandle *pProcessErrorRead)
441 {
442     rtl::OUString exe_path = get_executable_path(
443         ustrImageName, ustrArguments, nArguments, (Options & osl_Process_SEARCHPATH));
444 
445     if (0 == exe_path.getLength())
446         return osl_Process_E_NotFound;
447 
448     if (pProcess == NULL)
449         return osl_Process_E_InvalidError;
450 
451     DWORD flags = NORMAL_PRIORITY_CLASS;
452     rtl::OUStringBuffer command_line;
453 
454     if (is_batch_file(exe_path))
455     {
456         rtl::OUString batch_processor = get_batch_processor();
457 
458         if (batch_processor.getLength())
459         {
460             /* cmd.exe does not work without a console window */
461             if (!(Options & osl_Process_WAIT) || (Options & osl_Process_DETACHED))
462                 flags |= CREATE_NEW_CONSOLE;
463 
464             command_line.append(batch_processor);
465             command_line.appendAscii(" /c ");
466         }
467         else
468             // should we return here in case of error?
469             return osl_Process_E_Unknown;
470     }
471 
472     command_line.append(exe_path);
473 
474     /* Add remaining arguments to command line. If ustrImageName is NULL
475        the first parameter is the name of the executable so we have to
476        start at 1 instead of 0 */
477     for (sal_uInt32 n = (NULL != ustrImageName) ? 0 : 1; n < nArguments; n++)
478     {
479         command_line.appendAscii(SPACE);
480 
481         /* Quote arguments containing blanks */
482         if (rtl::OUString(ustrArguments[n]).indexOf(' ') != -1)
483             command_line.append(quote_string(ustrArguments[n]));
484         else
485             command_line.append(ustrArguments[n]);
486     }
487 
488     environment_container_t environment;
489     LPVOID p_environment = NULL;
490 
491     if (nEnvironmentVars && ustrEnvironmentVars)
492     {
493         if (!setup_process_environment(
494                 ustrEnvironmentVars, nEnvironmentVars, environment))
495             return osl_Process_E_InvalidError;
496 
497         flags |= CREATE_UNICODE_ENVIRONMENT;
498         p_environment = &environment[0];
499     }
500 
501     rtl::OUString cwd;
502     if (ustrDirectory && ustrDirectory->length && (osl_File_E_None != osl::FileBase::getSystemPathFromFileURL(ustrDirectory, cwd)))
503    	    return osl_Process_E_InvalidError;
504 
505     LPCWSTR	p_cwd = (cwd.getLength()) ? reinterpret_cast<LPCWSTR>(cwd.getStr()) : NULL;
506 
507 	if ((Options & osl_Process_DETACHED) && !(flags & CREATE_NEW_CONSOLE))
508 		flags |= DETACHED_PROCESS;
509 
510     STARTUPINFO startup_info;
511 	memset(&startup_info, 0, sizeof(STARTUPINFO));
512 
513 	startup_info.cb        = sizeof(STARTUPINFO);
514 	startup_info.dwFlags   = STARTF_USESHOWWINDOW;
515 	startup_info.lpDesktop = L"";
516 
517 	/* Create pipes for redirected IO */
518     HANDLE hInputRead  = NULL;
519     HANDLE hInputWrite = NULL;
520 	if (pProcessInputWrite && create_pipe(&hInputRead, true, &hInputWrite, false))
521 	    startup_info.hStdInput = hInputRead;
522 
523 	HANDLE hOutputRead  = NULL;
524     HANDLE hOutputWrite = NULL;
525 	if (pProcessOutputRead && create_pipe(&hOutputRead, false, &hOutputWrite, true))
526 	    startup_info.hStdOutput = hOutputWrite;
527 
528 	HANDLE hErrorRead  = NULL;
529     HANDLE hErrorWrite = NULL;
530 	if (pProcessErrorRead && create_pipe(&hErrorRead, false, &hErrorWrite, true))
531 	    startup_info.hStdError = hErrorWrite;
532 
533 	bool b_inherit_handles = false;
534 	if (pProcessInputWrite || pProcessOutputRead || pProcessErrorRead)
535 	{
536 	    startup_info.dwFlags |= STARTF_USESTDHANDLES;
537 		b_inherit_handles      = true;
538 	}
539 
540 	switch(Options & (osl_Process_NORMAL | osl_Process_HIDDEN | osl_Process_MINIMIZED | osl_Process_MAXIMIZED | osl_Process_FULLSCREEN))
541 	{
542 		case osl_Process_HIDDEN:
543 			startup_info.wShowWindow = SW_HIDE;
544             flags |= CREATE_NO_WINDOW; // ignored for non-console
545                                        // applications; ignored on
546                                        // Win9x
547 			break;
548 
549 		case osl_Process_MINIMIZED:
550 			startup_info.wShowWindow = SW_MINIMIZE;
551 			break;
552 
553 		case osl_Process_MAXIMIZED:
554 		case osl_Process_FULLSCREEN:
555 			startup_info.wShowWindow = SW_MAXIMIZE;
556 			break;
557 
558 		default:
559 			startup_info.wShowWindow = SW_NORMAL;
560 	}
561 
562     rtl::OUString cmdline = command_line.makeStringAndClear();
563     PROCESS_INFORMATION process_info;
564     BOOL bRet = FALSE;
565 
566 	if ((Security != NULL) && (((oslSecurityImpl*)Security)->m_hToken != NULL))
567 	{
568 		bRet = CreateProcessAsUser(
569             ((oslSecurityImpl*)Security)->m_hToken,
570 			NULL, const_cast<LPTSTR>(reinterpret_cast<LPCTSTR>(cmdline.getStr())), NULL,  NULL,
571 		    b_inherit_handles, flags, p_environment, p_cwd,
572 			&startup_info, &process_info);
573 	}
574 	else
575 	{
576 		bRet = CreateProcess(
577             NULL, const_cast<LPTSTR>(reinterpret_cast<LPCTSTR>(cmdline.getStr())), NULL,  NULL,
578 			b_inherit_handles, flags, p_environment, p_cwd,
579 			&startup_info, &process_info);
580 	}
581 
582 	/* Now we can close the pipe ends that are used by the child process */
583 
584 	if (hInputRead)
585 		CloseHandle(hInputRead);
586 
587 	if (hOutputWrite)
588 		CloseHandle(hOutputWrite);
589 
590 	if (hErrorWrite)
591 		CloseHandle(hErrorWrite);
592 
593 	if (bRet)
594 	{
595 		CloseHandle(process_info.hThread);
596 
597 		oslProcessImpl* pProcImpl = reinterpret_cast<oslProcessImpl*>(
598 		    rtl_allocateMemory(sizeof(oslProcessImpl)));
599 
600 		if (pProcImpl != NULL)
601 		{
602 		    pProcImpl->m_hProcess  = process_info.hProcess;
603 		    pProcImpl->m_IdProcess = process_info.dwProcessId;
604 
605 		    *pProcess = (oslProcess)pProcImpl;
606 
607 		    if (Options & osl_Process_WAIT)
608 			    WaitForSingleObject(pProcImpl->m_hProcess, INFINITE);
609 
610 		    if (pProcessInputWrite)
611 			    *pProcessInputWrite = osl_createFileHandleFromOSHandle(hInputWrite, osl_File_OpenFlag_Write);
612 
613 		    if (pProcessOutputRead)
614 			    *pProcessOutputRead = osl_createFileHandleFromOSHandle(hOutputRead, osl_File_OpenFlag_Read);
615 
616 		    if (pProcessErrorRead)
617 			    *pProcessErrorRead = osl_createFileHandleFromOSHandle(hErrorRead, osl_File_OpenFlag_Read);
618 
619 		    return osl_Process_E_None;
620 	    }
621     }
622 
623 	/* if an error occurred we have to close the server side pipe ends too */
624 
625 	if (hInputWrite)
626 		CloseHandle(hInputWrite);
627 
628 	if (hOutputRead)
629 		CloseHandle(hOutputRead);
630 
631 	if (hErrorRead)
632 		CloseHandle(hErrorRead);
633 
634 	return osl_Process_E_Unknown;
635 }
636