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 ()__anon04b5a59d0111::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