From 2a7a01b02bdf0695da8c0d4c2250ad6289da9026 Mon Sep 17 00:00:00 2001 From: Mike Kaganski Date: Sun, 28 Oct 2018 19:14:28 +0300 Subject: tdf#120703 PVS: V547 Fix activation of launched process' window V547 Expression 'procHandle != nullptr' is always false. The code was nonsensical overall. First, the launched process handle was never returned by ShellExecuteExW, because SEE_MASK_NOCLOSEPROCESS wasn't used, so GetProcessId couldn't succeed. Then, nullptr window handle was passed to GetWindowThreadProcessId, thus never returning a meaningful result. This reimplements this to find the launched process' main window by first waiting for process idle (up to 1-second delay is possible), then enumerating all the top-level windows and checking their process. Change-Id: I5fb4c04147b3f9414e27650a023f7844523c18bd Reviewed-on: https://gerrit.libreoffice.org/62478 Tested-by: Jenkins Reviewed-by: Mike Kaganski --- shell/source/win32/SysShExec.cxx | 49 ++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/shell/source/win32/SysShExec.cxx b/shell/source/win32/SysShExec.cxx index 6cb31942ba02..5edb22a1a821 100644 --- a/shell/source/win32/SysShExec.cxx +++ b/shell/source/win32/SysShExec.cxx @@ -243,6 +243,34 @@ CSysShExec::CSysShExec( const Reference< css::uno::XComponentContext >& xContext CoInitialize( nullptr ); } +namespace +{ +// This callback checks if the found window is the specified process's top-level window, +// and activates the first found such window. +BOOL CALLBACK FindAndActivateProcWnd(HWND hwnd, LPARAM lParam) +{ + if (!IsWindowVisible(hwnd)) + return TRUE; // continue enumeration + if (GetWindow(hwnd, GW_OWNER)) // not a top-level window + return TRUE; // continue enumeration + const DWORD nParamProcId = static_cast(lParam); + assert(nParamProcId != 0); + DWORD nWndProcId = 0; + (void)GetWindowThreadProcessId(hwnd, &nWndProcId); + if (nWndProcId != nParamProcId) + return TRUE; // continue enumeration + + // Found it! Bring it to front + if (IsIconic(hwnd)) + { + ShowWindow(hwnd, SW_RESTORE); + } + SetForegroundWindow(hwnd); + SetActiveWindow(hwnd); + return FALSE; // stop enumeration +} +} + void SAL_CALL CSysShExec::execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags ) throw (IllegalArgumentException, SystemShellExecuteException, RuntimeException) { @@ -300,9 +328,10 @@ void SAL_CALL CSysShExec::execute( const OUString& aCommand, const OUString& aPa sei.lpFile = reinterpret_cast(preprocessed_command.getStr()); sei.lpParameters = reinterpret_cast(aParameter.getStr()); sei.nShow = SW_SHOWNORMAL; + sei.fMask = SEE_MASK_NOCLOSEPROCESS; // we need sei.hProcess if (NO_SYSTEM_ERROR_MESSAGE & nFlags) - sei.fMask = SEE_MASK_FLAG_NO_UI; + sei.fMask |= SEE_MASK_FLAG_NO_UI; SetLastError( 0 ); @@ -326,20 +355,12 @@ void SAL_CALL CSysShExec::execute( const OUString& aCommand, const OUString& aPa else { // Get Permission make changes to the Window of the created Process - HWND procHandle = 0; - DWORD procId = GetProcessId(sei.hProcess); - AllowSetForegroundWindow(procId); - - // Get the handle of the created Window - DWORD check = 0; - GetWindowThreadProcessId(procHandle, &check); - SAL_WARN_IF(check != procId, "shell", "Could not get handle of process called by shell."); - - // Move created Window into the foreground - if(procHandle != 0) + const DWORD procId = GetProcessId(sei.hProcess); + if (procId != 0) { - SetForegroundWindow(procHandle); - SetActiveWindow(procHandle); + AllowSetForegroundWindow(procId); + WaitForInputIdle(sei.hProcess, 1000); // so that main window is created; imperfect + EnumWindows(FindAndActivateProcWnd, static_cast(procId)); } } -- cgit