lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / sal / osl / w32 / process.cxx
blobf1e223bd34cdd867085944a94998584af475b9ce
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "system.h"
21 #include <string.h>
23 #include <shellapi.h>
25 #include <cassert>
26 #include <memory>
28 #include <osl/nlsupport.h>
29 #include <o3tl/char16_t2wchar_t.hxx>
31 #include "filetime.hxx"
32 #include "nlsupport.hxx"
33 #include "procimpl.hxx"
34 #include "file_url.hxx"
35 #include "path_helper.hxx"
36 #include <rtl/alloc.h>
37 #include <sal/log.hxx>
39 oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
41 if (Process == nullptr)
42 return osl_Process_E_Unknown;
44 HANDLE hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess;
45 DWORD dwPID = GetProcessId(hProcess);
47 // cannot be System Process (0x00000000)
48 if (dwPID == 0x0)
49 return osl_Process_E_InvalidError;
51 // Test to see if we can create a thread in a process... adapted from:
52 // * https://support.microsoft.com/en-us/help/178893/how-to-terminate-an-application-cleanly-in-win32
53 // * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
55 // TODO: we really should firstly check to see if we have access to create threads and only
56 // duplicate the handle with elevated access if we don't have access... this can be done, but
57 // it's not exactly easy - an example can be found here:
58 // http://windowsitpro.com/site-files/windowsitpro.com/files/archive/windowsitpro.com/content/content/15989/listing_01.txt
60 HANDLE hDupProcess = nullptr;
63 // we need to make sure we can create a thread in the remote process, if the handle was created
64 // in something that doesn't give us appropriate levels of access then we will need to give it the
65 // desired level of access - if the process handle was grabbed from OpenProcess it's quite possible
66 // that the handle doesn't have the appropriate level of access...
68 // see https://msdn.microsoft.com/en-au/library/windows/desktop/ms684880(v=vs.85).aspx
69 DWORD const dwAccessFlags = (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION
70 | PROCESS_VM_WRITE | PROCESS_VM_READ);
72 BOOL bHaveDuplHdl = DuplicateHandle(GetCurrentProcess(), // handle to process that has handle
73 hProcess, // handle to be duplicated
74 GetCurrentProcess(), // process that will get the dup handle
75 &hDupProcess, // store duplicate process handle here
76 dwAccessFlags, // desired access
77 FALSE, // handle can't be inherited
78 0); // zero means no additional action needed
80 if (bHaveDuplHdl)
81 hProcess = hDupProcess; // so we were able to duplicate the handle, all good...
82 else
83 SAL_WARN("sal.osl", "Could not duplicate process handle, let's hope for the best...");
85 DWORD dwProcessStatus = 0;
86 HANDLE hRemoteThread = nullptr;
88 if (GetExitCodeProcess(hProcess, &dwProcessStatus) && (dwProcessStatus == STILL_ACTIVE))
90 // We need to get the address of the Win32 procedure ExitProcess, can't call it
91 // directly because we'll be calling the thunk and that will probably lead to an
92 // access violation. Once we have the address, then we need to create a new
93 // thread in the process (which we might need to run in the address space of
94 // another process) and then call on ExitProcess to try to cleanly terminate that
95 // process
97 DWORD dwTID = 0; // dummy variable as we don't need to track the thread ID
99 // Note: we want to call on ExitProcess() and not TerminateProcess() - this is
100 // because with ExitProcess() Windows notifies all attached dlls that the process
101 // is detaching from the dll, but TerminateProcess() terminates all threads
102 // immediately, doesn't call any termination handlers and doesn't notify any dlls
103 // that it is detaching from them
105 HINSTANCE hKernel = GetModuleHandleW(L"kernel32.dll");
106 FARPROC pfnExitProc = GetProcAddress(hKernel, "ExitProcess");
107 hRemoteThread = CreateRemoteThread(
108 hProcess, /* process handle */
109 nullptr, /* default security descriptor */
110 0, /* initial size of stack in bytes is default
111 size for executable */
112 reinterpret_cast<LPTHREAD_START_ROUTINE>(pfnExitProc), /* Win32 ExitProcess() */
113 reinterpret_cast<PVOID>(UINT(0)), /* ExitProcess(UINT uExitCode) argument */
114 0, /* value of 0 tells thread to run immediately
115 after creation */
116 &dwTID); /* new remote thread's identifier */
120 bool bHasExited = false;
122 if (hRemoteThread)
124 WaitForSingleObject(hProcess, INFINITE); // wait for process to terminate, never stop waiting...
125 CloseHandle(hRemoteThread); // close the thread handle to allow the process to exit
126 bHasExited = true;
129 // need to close this duplicated process handle...
130 if (bHaveDuplHdl)
131 CloseHandle(hProcess);
133 if (bHasExited)
134 return osl_Process_E_None;
136 // fallback - given that we wait for an infinite time on WaitForSingleObject, this should
137 // never occur... unless CreateRemoteThread failed
138 SAL_WARN("sal.osl", "TerminateProcess(hProcess, 0) called - we should never get here!");
139 return (TerminateProcess(hProcess, 0) == FALSE) ? osl_Process_E_Unknown : osl_Process_E_None;
142 oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
144 oslProcessImpl* pProcImpl;
145 HANDLE hProcess = OpenProcess(
146 STANDARD_RIGHTS_REQUIRED | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, static_cast<DWORD>(Ident));
148 if (hProcess)
150 pProcImpl = static_cast< oslProcessImpl*>( malloc(sizeof(oslProcessImpl)) );
151 pProcImpl->m_hProcess = hProcess;
152 pProcImpl->m_IdProcess = Ident;
154 else
155 pProcImpl = nullptr;
157 return pProcImpl;
160 void SAL_CALL osl_freeProcessHandle(oslProcess Process)
162 if (Process != nullptr)
164 CloseHandle(static_cast<oslProcessImpl*>(Process)->m_hProcess);
166 free(Process);
170 oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields,
171 oslProcessInfo* pInfo)
173 HANDLE hProcess;
174 DWORD IdProcess;
176 if (Process == nullptr)
178 hProcess = GetCurrentProcess();
179 IdProcess = GetCurrentProcessId();
181 else
183 hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess;
184 IdProcess = static_cast<oslProcessImpl*>(Process)->m_IdProcess;
187 if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
188 return osl_Process_E_Unknown;
190 pInfo->Fields = 0;
192 if (Fields & osl_Process_IDENTIFIER)
194 pInfo->Ident = IdProcess;
195 pInfo->Fields |= osl_Process_IDENTIFIER;
198 if (Fields & osl_Process_EXITCODE)
200 if (GetExitCodeProcess(hProcess, &(pInfo->Code)) && (pInfo->Code != STILL_ACTIVE))
201 pInfo->Fields |= osl_Process_EXITCODE;
204 if (Fields & osl_Process_HEAPUSAGE)
206 void* lpAddress=nullptr;
207 MEMORY_BASIC_INFORMATION Info;
209 pInfo->HeapUsage = 0;
213 if (VirtualQueryEx(hProcess, lpAddress, &Info, sizeof(Info)) == 0)
214 break;
216 if ((Info.State == MEM_COMMIT) && (Info.Type == MEM_PRIVATE))
217 pInfo->HeapUsage += Info.RegionSize;
219 lpAddress = static_cast<LPBYTE>(lpAddress) + Info.RegionSize;
221 while (reinterpret_cast<uintptr_t>(lpAddress) <= uintptr_t(0x7FFFFFFF)); // 2GB address space
223 pInfo->Fields |= osl_Process_HEAPUSAGE;
226 if (Fields & osl_Process_CPUTIMES)
228 FILETIME CreationTime, ExitTime, KernelTime, UserTime;
230 if (GetProcessTimes(hProcess, &CreationTime, &ExitTime,
231 &KernelTime, &UserTime))
233 __int64 Value;
235 Value = osl::detail::getFiletime(UserTime);
236 pInfo->UserTime.Seconds = static_cast<unsigned long>(Value / 10000000L);
237 pInfo->UserTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
239 Value = osl::detail::getFiletime(KernelTime);
240 pInfo->SystemTime.Seconds = static_cast<unsigned long>(Value / 10000000L);
241 pInfo->SystemTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
243 pInfo->Fields |= osl_Process_CPUTIMES;
247 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
250 oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
252 return osl_joinProcessWithTimeout(Process, nullptr);
255 oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
257 DWORD timeout = INFINITE;
258 oslProcessError osl_error = osl_Process_E_None;
259 DWORD ret;
261 if (nullptr == Process)
262 return osl_Process_E_Unknown;
264 if (pTimeout)
265 timeout = pTimeout->Seconds * 1000 + pTimeout->Nanosec / 1000000L;
267 ret = WaitForSingleObject(static_cast<oslProcessImpl*>(Process)->m_hProcess, timeout);
269 if (WAIT_FAILED == ret)
270 osl_error = osl_Process_E_Unknown;
271 else if (WAIT_TIMEOUT == ret)
272 osl_error = osl_Process_E_TimedOut;
274 return osl_error;
277 namespace {
279 oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
281 oslProcessError result = osl_Process_E_NotFound;
283 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
284 DWORD buflen = 0;
286 if ((buflen = GetModuleFileNameW (nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols())) > 0)
288 rtl_uString * pAbsPath = nullptr;
289 rtl_uString_newFromStr_WithLength (&pAbsPath, aBuffer, buflen);
290 if (pAbsPath)
292 /* Convert from path to url. */
293 if (osl_getFileURLFromSystemPath (pAbsPath, ppFileURL) == osl_File_E_None)
295 /* Success. */
296 result = osl_Process_E_None;
298 rtl_uString_release (pAbsPath);
302 return result;
307 struct CommandArgs_Impl
309 sal_uInt32 m_nCount;
310 rtl_uString ** m_ppArgs;
313 static struct CommandArgs_Impl g_command_args =
316 nullptr
319 static rtl_uString ** osl_createCommandArgs_Impl (int argc, char **)
321 rtl_uString ** ppArgs =
322 static_cast<rtl_uString**>(rtl_allocateZeroMemory (argc * sizeof(rtl_uString*)));
323 if (ppArgs != nullptr)
325 int i;
326 int nArgs;
327 LPWSTR *wargv = CommandLineToArgvW( GetCommandLineW(), &nArgs );
328 assert( nArgs == argc );
329 for (i = 0; i < nArgs; i++)
331 /* Convert to unicode */
332 rtl_uString_newFromStr( &(ppArgs[i]), o3tl::toU(wargv[i]) );
334 if (ppArgs[0] != nullptr)
336 /* Ensure absolute path */
337 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
338 DWORD dwResult = 0;
340 dwResult = SearchPathW (
341 nullptr, o3tl::toW(ppArgs[0]->buffer), L".exe", aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer), nullptr);
342 if ((0 < dwResult) && (dwResult < aBuffer.getBufSizeInSymbols()))
344 /* Replace argv[0] with its absolute path */
345 rtl_uString_newFromStr_WithLength(
346 &(ppArgs[0]), aBuffer, dwResult);
349 if (ppArgs[0] != nullptr)
351 /* Convert to FileURL, see @ osl_getExecutableFile() */
352 rtl_uString * pResult = nullptr;
353 osl_getFileURLFromSystemPath (ppArgs[0], &pResult);
354 if (pResult != nullptr)
356 rtl_uString_assign (&(ppArgs[0]), pResult);
357 rtl_uString_release (pResult);
361 return ppArgs;
365 oslProcessError SAL_CALL osl_getExecutableFile( rtl_uString **ppustrFile )
367 osl_acquireMutex (*osl_getGlobalMutex());
368 if (g_command_args.m_nCount == 0)
370 osl_releaseMutex (*osl_getGlobalMutex());
371 return bootstrap_getExecutableFile(ppustrFile);
374 /* CommandArgs set. Obtain arv[0]. */
375 rtl_uString_assign (ppustrFile, g_command_args.m_ppArgs[0]);
376 osl_releaseMutex (*osl_getGlobalMutex());
377 return osl_Process_E_None;
380 sal_uInt32 SAL_CALL osl_getCommandArgCount()
382 sal_uInt32 result = 0;
384 osl_acquireMutex (*osl_getGlobalMutex());
385 SAL_INFO_IF(
386 g_command_args.m_nCount == 0, "sal.osl",
387 "osl_getCommandArgCount w/o prior call to osl_setCommandArgs");
388 if (g_command_args.m_nCount > 0)
390 /* We're not counting argv[0] here. */
391 result = g_command_args.m_nCount - 1;
393 osl_releaseMutex (*osl_getGlobalMutex());
395 return result;
398 oslProcessError SAL_CALL osl_getCommandArg( sal_uInt32 nArg, rtl_uString **strCommandArg)
400 oslProcessError result = osl_Process_E_NotFound;
402 osl_acquireMutex (*osl_getGlobalMutex());
403 assert(g_command_args.m_nCount > 0);
404 if (g_command_args.m_nCount > (nArg + 1))
406 /* We're not counting argv[0] here. */
407 rtl_uString_assign (strCommandArg, g_command_args.m_ppArgs[nArg + 1]);
408 result = osl_Process_E_None;
410 osl_releaseMutex (*osl_getGlobalMutex());
412 return result;
415 void SAL_CALL osl_setCommandArgs (int argc, char ** argv)
417 assert(argc > 0);
418 osl_acquireMutex (*osl_getGlobalMutex());
419 SAL_WARN_IF(g_command_args.m_nCount != 0, "sal.osl", "args already set");
420 if (g_command_args.m_nCount == 0)
422 rtl_uString** ppArgs = osl_createCommandArgs_Impl (argc, argv);
423 if (ppArgs != nullptr)
425 g_command_args.m_nCount = argc;
426 g_command_args.m_ppArgs = ppArgs;
429 osl_releaseMutex (*osl_getGlobalMutex());
432 /* TODO because of an issue with GetEnvironmentVariableW we have to
433 allocate a buffer large enough to hold the requested environment
434 variable instead of testing for the required size. This wastes
435 some stack space, maybe we should revoke this work around if
436 this is no longer a problem */
437 #define ENV_BUFFER_SIZE (32*1024-1)
439 oslProcessError SAL_CALL osl_getEnvironment(rtl_uString *ustrVar, rtl_uString **ustrValue)
441 WCHAR buff[ENV_BUFFER_SIZE];
443 if (GetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), buff, ENV_BUFFER_SIZE) > 0)
445 rtl_uString_newFromStr(ustrValue, o3tl::toU(buff));
446 return osl_Process_E_None;
448 return osl_Process_E_Unknown;
451 oslProcessError SAL_CALL osl_setEnvironment(rtl_uString *ustrVar, rtl_uString *ustrValue)
453 // set Windows environment variable
454 LPCWSTR lpName = o3tl::toW(ustrVar->buffer);
455 LPCWSTR lpValue = o3tl::toW(ustrValue->buffer);
456 if (SetEnvironmentVariableW(lpName, lpValue))
458 auto buffer = std::unique_ptr<wchar_t[]>(
459 new wchar_t[wcslen(lpName) + 1 + wcslen(lpValue) + 1]);
460 wcscpy(buffer.get(), lpName);
461 wcscat(buffer.get(), L"=");
462 wcscat(buffer.get(), lpValue);
463 _wputenv(buffer.get());
464 return osl_Process_E_None;
466 return osl_Process_E_Unknown;
469 oslProcessError SAL_CALL osl_clearEnvironment(rtl_uString *ustrVar)
471 // delete the variable from the current process environment
472 // by setting SetEnvironmentVariable's second parameter to NULL
473 LPCWSTR lpName = o3tl::toW(ustrVar->buffer);
474 if (SetEnvironmentVariableW(lpName, nullptr))
476 auto buffer = std::unique_ptr<wchar_t[]>(
477 new wchar_t[wcslen(lpName) + 1 + 1]);
478 wcscpy(buffer.get(), lpName);
479 wcscat(buffer.get(), L"=");
480 _wputenv(buffer.get());
481 return osl_Process_E_None;
483 return osl_Process_E_Unknown;
486 oslProcessError SAL_CALL osl_getProcessWorkingDir( rtl_uString **pustrWorkingDir )
488 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
489 DWORD dwLen = 0;
491 osl_acquireMutex( g_CurrentDirectoryMutex );
492 dwLen = GetCurrentDirectoryW( aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer) );
493 osl_releaseMutex( g_CurrentDirectoryMutex );
495 if ( dwLen && dwLen < aBuffer.getBufSizeInSymbols() )
497 oslFileError eError;
498 rtl_uString *ustrTemp = nullptr;
500 rtl_uString_newFromStr_WithLength( &ustrTemp, aBuffer, dwLen );
501 eError = osl_getFileURLFromSystemPath( ustrTemp, pustrWorkingDir );
503 rtl_uString_release( ustrTemp );
505 if ( osl_File_E_None != eError )
506 return osl_Process_E_Unknown;
507 else
508 return osl_Process_E_None;
510 else
511 return osl_Process_E_Unknown;
514 static rtl_Locale * g_theProcessLocale = nullptr;
516 oslProcessError SAL_CALL osl_getProcessLocale( rtl_Locale ** ppLocale )
518 osl_acquireMutex( *osl_getGlobalMutex() );
520 /* determine the users default locale */
521 if( nullptr == g_theProcessLocale )
522 imp_getProcessLocale( &g_theProcessLocale );
524 /* or return the cached value */
525 *ppLocale = g_theProcessLocale;
527 osl_releaseMutex( *osl_getGlobalMutex() );
528 return osl_Process_E_None;
531 oslProcessError SAL_CALL osl_setProcessLocale( rtl_Locale * pLocale )
533 osl_acquireMutex( *osl_getGlobalMutex() );
535 /* check if locale is supported */
536 if( RTL_TEXTENCODING_DONTKNOW == osl_getTextEncodingFromLocale( pLocale ) )
537 return osl_Process_E_Unknown;
539 /* just remember the locale here */
540 g_theProcessLocale = pLocale;
542 osl_releaseMutex( *osl_getGlobalMutex() );
543 return osl_Process_E_None;
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */