tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sal / osl / w32 / process.cxx
blobd18513be6dd5891f7fcedfa407cf8d0ce5303cd0
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>
27 #include <osl/mutex.hxx>
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 <rtl/ustring.hxx>
38 #include <sal/log.hxx>
40 oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
42 if (Process == nullptr)
43 return osl_Process_E_Unknown;
45 HANDLE hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess;
46 DWORD dwPID = GetProcessId(hProcess);
48 // cannot be System Process (0x00000000)
49 if (dwPID == 0x0)
50 return osl_Process_E_InvalidError;
52 // Test to see if we can create a thread in a process... adapted from:
53 // * https://support.microsoft.com/en-us/help/178893/how-to-terminate-an-application-cleanly-in-win32
54 // * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
56 // TODO: we really should firstly check to see if we have access to create threads and only
57 // duplicate the handle with elevated access if we don't have access... this can be done, but
58 // it's not exactly easy - an example can be found here:
59 // http://windowsitpro.com/site-files/windowsitpro.com/files/archive/windowsitpro.com/content/content/15989/listing_01.txt
61 HANDLE hDupProcess = nullptr;
64 // we need to make sure we can create a thread in the remote process, if the handle was created
65 // in something that doesn't give us appropriate levels of access then we will need to give it the
66 // desired level of access - if the process handle was grabbed from OpenProcess it's quite possible
67 // that the handle doesn't have the appropriate level of access...
69 // see https://msdn.microsoft.com/en-au/library/windows/desktop/ms684880(v=vs.85).aspx
70 DWORD const dwAccessFlags = (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION
71 | PROCESS_VM_WRITE | PROCESS_VM_READ);
73 bool bHaveDuplHdl = DuplicateHandle(GetCurrentProcess(), // handle to process that has handle
74 hProcess, // handle to be duplicated
75 GetCurrentProcess(), // process that will get the dup handle
76 &hDupProcess, // store duplicate process handle here
77 dwAccessFlags, // desired access
78 FALSE, // handle can't be inherited
79 0); // zero means no additional action needed
81 if (bHaveDuplHdl)
82 hProcess = hDupProcess; // so we were able to duplicate the handle, all good...
83 else
84 SAL_WARN("sal.osl", "Could not duplicate process handle, let's hope for the best...");
86 DWORD dwProcessStatus = 0;
87 HANDLE hRemoteThread = nullptr;
89 if (GetExitCodeProcess(hProcess, &dwProcessStatus) && (dwProcessStatus == STILL_ACTIVE))
91 // We need to get the address of the Win32 procedure ExitProcess, can't call it
92 // directly because we'll be calling the thunk and that will probably lead to an
93 // access violation. Once we have the address, then we need to create a new
94 // thread in the process (which we might need to run in the address space of
95 // another process) and then call on ExitProcess to try to cleanly terminate that
96 // process
98 DWORD dwTID = 0; // dummy variable as we don't need to track the thread ID
100 // Note: we want to call on ExitProcess() and not TerminateProcess() - this is
101 // because with ExitProcess() Windows notifies all attached dlls that the process
102 // is detaching from the dll, but TerminateProcess() terminates all threads
103 // immediately, doesn't call any termination handlers and doesn't notify any dlls
104 // that it is detaching from them
106 HINSTANCE hKernel = GetModuleHandleW(L"kernel32.dll");
107 FARPROC pfnExitProc = GetProcAddress(hKernel, "ExitProcess");
108 hRemoteThread = CreateRemoteThread(
109 hProcess, /* process handle */
110 nullptr, /* default security descriptor */
111 0, /* initial size of stack in bytes is default
112 size for executable */
113 reinterpret_cast<LPTHREAD_START_ROUTINE>(pfnExitProc), /* Win32 ExitProcess() */
114 reinterpret_cast<PVOID>(UINT(0)), /* ExitProcess(UINT uExitCode) argument */
115 0, /* value of 0 tells thread to run immediately
116 after creation */
117 &dwTID); /* new remote thread's identifier */
121 bool bHasExited = false;
123 if (hRemoteThread)
125 WaitForSingleObject(hProcess, INFINITE); // wait for process to terminate, never stop waiting...
126 CloseHandle(hRemoteThread); // close the thread handle to allow the process to exit
127 bHasExited = true;
130 // need to close this duplicated process handle...
131 if (bHaveDuplHdl)
132 CloseHandle(hProcess);
134 if (bHasExited)
135 return osl_Process_E_None;
137 // fallback - given that we wait for an infinite time on WaitForSingleObject, this should
138 // never occur... unless CreateRemoteThread failed
139 SAL_WARN("sal.osl", "TerminateProcess(hProcess, 0) called - we should never get here!");
140 return (TerminateProcess(hProcess, 0) == FALSE) ? osl_Process_E_Unknown : osl_Process_E_None;
143 oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
145 oslProcessImpl* pProcImpl;
146 HANDLE hProcess = OpenProcess(
147 STANDARD_RIGHTS_REQUIRED | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, static_cast<DWORD>(Ident));
149 if (hProcess)
151 pProcImpl = static_cast< oslProcessImpl*>( malloc(sizeof(oslProcessImpl)) );
152 assert(pProcImpl && "Don't handle OOM conditions");
153 pProcImpl->m_hProcess = hProcess;
154 pProcImpl->m_IdProcess = Ident;
156 else
157 pProcImpl = nullptr;
159 return pProcImpl;
162 void SAL_CALL osl_freeProcessHandle(oslProcess Process)
164 if (Process != nullptr)
166 CloseHandle(static_cast<oslProcessImpl*>(Process)->m_hProcess);
168 free(Process);
172 oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields,
173 oslProcessInfo* pInfo)
175 HANDLE hProcess;
176 DWORD IdProcess;
178 if (Process == nullptr)
180 hProcess = GetCurrentProcess();
181 IdProcess = GetCurrentProcessId();
183 else
185 hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess;
186 IdProcess = static_cast<oslProcessImpl*>(Process)->m_IdProcess;
189 if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
190 return osl_Process_E_Unknown;
192 pInfo->Fields = 0;
194 if (Fields & osl_Process_IDENTIFIER)
196 pInfo->Ident = IdProcess;
197 pInfo->Fields |= osl_Process_IDENTIFIER;
200 if (Fields & osl_Process_EXITCODE)
202 if (GetExitCodeProcess(hProcess, &(pInfo->Code)) && (pInfo->Code != STILL_ACTIVE))
203 pInfo->Fields |= osl_Process_EXITCODE;
206 if (Fields & osl_Process_HEAPUSAGE)
208 void* lpAddress=nullptr;
209 MEMORY_BASIC_INFORMATION Info;
211 pInfo->HeapUsage = 0;
215 if (VirtualQueryEx(hProcess, lpAddress, &Info, sizeof(Info)) == 0)
216 break;
218 if ((Info.State == MEM_COMMIT) && (Info.Type == MEM_PRIVATE))
219 pInfo->HeapUsage += Info.RegionSize;
221 lpAddress = static_cast<LPBYTE>(lpAddress) + Info.RegionSize;
223 while (reinterpret_cast<uintptr_t>(lpAddress) <= 0x7FFFFFFFU); // 2GB address space
225 pInfo->Fields |= osl_Process_HEAPUSAGE;
228 if (Fields & osl_Process_CPUTIMES)
230 FILETIME CreationTime, ExitTime, KernelTime, UserTime;
232 if (GetProcessTimes(hProcess, &CreationTime, &ExitTime,
233 &KernelTime, &UserTime))
235 __int64 Value;
237 Value = osl::detail::getFiletime(UserTime);
238 pInfo->UserTime.Seconds = static_cast<unsigned long>(Value / 10000000L);
239 pInfo->UserTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
241 Value = osl::detail::getFiletime(KernelTime);
242 pInfo->SystemTime.Seconds = static_cast<unsigned long>(Value / 10000000L);
243 pInfo->SystemTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
245 pInfo->Fields |= osl_Process_CPUTIMES;
249 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
252 oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
254 return osl_joinProcessWithTimeout(Process, nullptr);
257 oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
259 DWORD timeout = INFINITE;
260 oslProcessError osl_error = osl_Process_E_None;
261 DWORD ret;
263 if (nullptr == Process)
264 return osl_Process_E_Unknown;
266 if (pTimeout)
267 timeout = pTimeout->Seconds * 1000 + pTimeout->Nanosec / 1000000L;
269 ret = WaitForSingleObject(static_cast<oslProcessImpl*>(Process)->m_hProcess, timeout);
271 if (WAIT_FAILED == ret)
272 osl_error = osl_Process_E_Unknown;
273 else if (WAIT_TIMEOUT == ret)
274 osl_error = osl_Process_E_TimedOut;
276 return osl_error;
279 namespace {
281 oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
283 oslProcessError result = osl_Process_E_NotFound;
285 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
286 DWORD buflen = 0;
288 if ((buflen = GetModuleFileNameW (nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols())) > 0)
290 rtl_uString * pAbsPath = nullptr;
291 rtl_uString_newFromStr_WithLength (&pAbsPath, aBuffer, buflen);
292 if (pAbsPath)
294 /* Convert from path to url. */
295 if (osl_getFileURLFromSystemPath (pAbsPath, ppFileURL) == osl_File_E_None)
297 /* Success. */
298 result = osl_Process_E_None;
300 rtl_uString_release (pAbsPath);
304 return result;
307 struct CommandArgs_Impl
309 sal_uInt32 m_nCount;
310 rtl_uString ** m_ppArgs;
315 static struct CommandArgs_Impl g_command_args =
318 nullptr
321 static rtl_uString ** osl_createCommandArgs_Impl (int argc, char **)
323 rtl_uString ** ppArgs =
324 static_cast<rtl_uString**>(rtl_allocateZeroMemory (argc * sizeof(rtl_uString*)));
325 if (ppArgs != nullptr)
327 int i;
328 int nArgs;
329 LPWSTR *wargv = CommandLineToArgvW( GetCommandLineW(), &nArgs );
330 assert( nArgs == argc );
331 for (i = 0; i < nArgs; i++)
333 /* Convert to unicode */
334 rtl_uString_newFromStr( &(ppArgs[i]), o3tl::toU(wargv[i]) );
336 if (ppArgs[0] != nullptr)
338 /* Ensure absolute path */
339 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
340 DWORD dwResult
341 = GetModuleFileNameW(nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols());
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 )
368 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
369 if (g_command_args.m_nCount > 0)
371 /* CommandArgs set. Obtain arv[0]. */
372 rtl_uString_assign(ppustrFile, g_command_args.m_ppArgs[0]);
373 return osl_Process_E_None;
376 return bootstrap_getExecutableFile(ppustrFile);
379 sal_uInt32 SAL_CALL osl_getCommandArgCount()
381 sal_uInt32 result = 0;
383 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
384 SAL_INFO_IF(
385 g_command_args.m_nCount == 0, "sal.osl",
386 "osl_getCommandArgCount w/o prior call to osl_setCommandArgs");
387 if (g_command_args.m_nCount > 0)
389 /* We're not counting argv[0] here. */
390 result = g_command_args.m_nCount - 1;
393 return result;
396 oslProcessError SAL_CALL osl_getCommandArg( sal_uInt32 nArg, rtl_uString **strCommandArg)
398 oslProcessError result = osl_Process_E_NotFound;
400 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
401 assert(g_command_args.m_nCount > 0);
402 if (g_command_args.m_nCount > (nArg + 1))
404 /* We're not counting argv[0] here. */
405 rtl_uString_assign (strCommandArg, g_command_args.m_ppArgs[nArg + 1]);
406 result = osl_Process_E_None;
409 return result;
412 void SAL_CALL osl_setCommandArgs (int argc, char ** argv)
414 assert(argc > 0);
415 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
416 SAL_WARN_IF(g_command_args.m_nCount != 0, "sal.osl", "args already set");
417 if (g_command_args.m_nCount == 0)
419 rtl_uString** ppArgs = osl_createCommandArgs_Impl (argc, argv);
420 if (ppArgs != nullptr)
422 g_command_args.m_nCount = argc;
423 g_command_args.m_ppArgs = ppArgs;
428 /* TODO because of an issue with GetEnvironmentVariableW we have to
429 allocate a buffer large enough to hold the requested environment
430 variable instead of testing for the required size. This wastes
431 some stack space, maybe we should revoke this work around if
432 this is no longer a problem */
433 #define ENV_BUFFER_SIZE (32*1024-1)
435 oslProcessError SAL_CALL osl_getEnvironment(rtl_uString *ustrVar, rtl_uString **ustrValue)
437 WCHAR buff[ENV_BUFFER_SIZE];
439 if (GetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), buff, ENV_BUFFER_SIZE) > 0)
441 rtl_uString_newFromStr(ustrValue, o3tl::toU(buff));
442 return osl_Process_E_None;
444 return osl_Process_E_Unknown;
447 oslProcessError SAL_CALL osl_setEnvironment(rtl_uString *ustrVar, rtl_uString *ustrValue)
449 // set Windows environment variable
450 if (SetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), o3tl::toW(ustrValue->buffer)))
452 OUString sAssign = OUString::unacquired(&ustrVar) + "=" + OUString::unacquired(&ustrValue);
453 _wputenv(o3tl::toW(sAssign.getStr()));
454 return osl_Process_E_None;
456 return osl_Process_E_Unknown;
459 oslProcessError SAL_CALL osl_clearEnvironment(rtl_uString *ustrVar)
461 // delete the variable from the current process environment
462 // by setting SetEnvironmentVariable's second parameter to NULL
463 if (SetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), nullptr))
465 OUString sAssign = OUString::unacquired(&ustrVar) + "=";
466 _wputenv(o3tl::toW(sAssign.getStr()));
467 return osl_Process_E_None;
469 return osl_Process_E_Unknown;
472 oslProcessError SAL_CALL osl_getProcessWorkingDir( rtl_uString **pustrWorkingDir )
474 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
475 DWORD dwLen = GetCurrentDirectoryW(aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer));
477 if ( dwLen && dwLen < aBuffer.getBufSizeInSymbols() )
479 oslFileError eError;
480 rtl_uString *ustrTemp = nullptr;
482 rtl_uString_newFromStr_WithLength( &ustrTemp, aBuffer, dwLen );
483 eError = osl_getFileURLFromSystemPath( ustrTemp, pustrWorkingDir );
485 rtl_uString_release( ustrTemp );
487 if ( osl_File_E_None != eError )
488 return osl_Process_E_Unknown;
489 else
490 return osl_Process_E_None;
492 else
493 return osl_Process_E_Unknown;
496 static rtl_Locale * g_theProcessLocale = nullptr;
498 oslProcessError SAL_CALL osl_getProcessLocale( rtl_Locale ** ppLocale )
500 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
502 /* determine the users default locale */
503 if( nullptr == g_theProcessLocale )
504 imp_getProcessLocale( &g_theProcessLocale );
506 /* or return the cached value */
507 *ppLocale = g_theProcessLocale;
509 return osl_Process_E_None;
512 oslProcessError SAL_CALL osl_setProcessLocale( rtl_Locale * pLocale )
514 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
516 /* check if locale is supported */
517 if( RTL_TEXTENCODING_DONTKNOW == osl_getTextEncodingFromLocale( pLocale ) )
518 return osl_Process_E_Unknown;
520 /* just remember the locale here */
521 g_theProcessLocale = pLocale;
523 return osl_Process_E_None;
526 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */