xref: /AOO41X/main/sal/osl/w32/procimpl.cxx (revision 79e556ee827beb4823bf162e2fd4108e56b8fd4e)
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 ()__anonb02070bc0111::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 occured 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