sdancer75 Ответов: 2

Используя функцию waitforsingleobject ShellExecuteEx ПО и родительское окно теряет фокус, когда он вернется


Используя приведенный ниже код для запуска внешнего exe-файла, когда это приложение завершается, родительское окно теряет фокус. Я хочу отключить родительское окно, чтобы избежать каких-либо проблем с краской, и повторно активировать его, когда WaitForSingleObject будет завершен.

Есть ли какое-то решение для этого ? Что я делаю не так?

void CMainFrame::OnBtnUpdateApp() {

    SHELLEXECUTEINFO    lpExecInfo;
    DWORD               dwExitCode;
    HINSTANCE           hProcess = 0;
    BOOL                bResult;



    ZeroMemory(&lpExecInfo,sizeof(lpExecInfo));
    lpExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    lpExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    lpExecInfo.hwnd = GetSafeHwnd();
    lpExecInfo.lpVerb = _T("open");
    lpExecInfo.lpFile = _T("Update.exe");
    lpExecInfo.lpParameters = _T("");
    lpExecInfo.lpDirectory = _T("");
    lpExecInfo.nShow = SW_SHOWNORMAL;
    lpExecInfo.hInstApp = NULL;
    lpExecInfo.hProcess = hProcess;

    bResult = ShellExecuteEx(&lpExecInfo);

    if(bResult) { 

         EnableWindow(FALSE);
         WaitForSingleObject( lpExecInfo.hProcess, INFINITE );

         if (!GetExitCodeProcess(lpExecInfo.hProcess, &dwExitCode)) {
                //failed to terminate normally   
         }

         CloseHandle(lpExecInfo.hProcess);
         EnableWindow(TRUE);

    } else {

        //failed to execute the exe file
    }


}


Что я уже пробовал:

Я пробовал, BringWindowToTop(), SetActiveWindow(), SetFocus()

[no name]

Попробуйте вызвать Activate после ожидания или ... до ожидания - сохраните HWND из GetFocus и (после ожидания) вызовите SetFocus на сохраненном HWND.

sdancer75

С помощью кода : hfocus его hwnd = ::GetFocus();метода enablewindow(false) в;функцию waitforsingleobject( lpExecInfo.hProcess, бесконечные );::setfocus для(hfocus);метода enablewindow(true); в казалось, не работали. Есть еще идеи ?

Frankie-C

Вы не можете установить фокус на отключенном окне. Включите окно, а затем установите фокус.

sdancer75

Да, это правда, моя ошибка.... но я уже сделал это, и это не сработало.

Во всяком случае, у меня есть некоторые мысли о родительском окне, которое вызывает shellexectute.
Если мое приложение является родительским окном, а приложение shellexecute-дочерним, будет ли переключение с помощью клавиш Alt+Tab отображать как родительские, так и дочерние приложения ? Это то, что происходит прямо сейчас, но я думаю, что он будет показывать только родительское окно правильно ? Имеет ли это какое-либо отношение к управлению фокусом?

Frankie-C

есть Много хитрых вещей, связанных с состоянием окна, вызывающим потоком и т. д.
Вы пробовали использовать SetForegroundWindow()?

sdancer75

Да, я использовал старую статью из codeproject, которая используется SetForegroundWindow (), но у меня все еще есть проблема. Теперь я работаю с CreateProcess, чтобы проверить, будет ли это работать для меня...

В любом случае, есть ли у вас какое-либо представление о вопросе для родительского/дочернего окна, если они появляются как одно приложение при переключении приложений windows ?

Frankie-C

Прочтите раздел ремарка в https://msdn.microsoft.com/en-us/library/windows/desktop/ms646291(v=против 85).aspx. Все дочерние окна являются частью цепочки включения/выключения верхнего окна, но с большим количеством деталей, как обычно в Win.

sdancer75

Что бы я ни пытался, мне это не удавалось. Я загрузил простой файл MFC/SDI, который использует CreateProcess для всех, кто хочет посмотреть и дать решение.

http://www.filedropper.com/shellexecutecreateprocesstestapp

2 Ответов

Рейтинг:
5

sdancer75

void CMainFrame::OnBtnUpdateApp() {


	PROCESS_INFORMATION processInfo;
	DWORD				dwExitCode;
	HANDLE				hProcess = 0;
	BOOL				bResult;
	CString				strCmd;
	CUtils				util;
	CString				strPathAndApp;


	strCmd = _T("");



	MONITORINFO mi = { sizeof(mi) };
	::GetWindowPlacement(GetSafeHwnd(), &m_g_wpPrevFrameSize) && ::GetMonitorInfo(MonitorFromWindow(GetSafeHwnd(),MONITOR_DEFAULTTOPRIMARY), &mi);

	strPathAndApp = _T("\"") + util.GetApplicationPath() + _T("\\")+_T(UPDATE_EXE_FILE) + _T("\"") + strCmd;	
	//******************************************************************************************
	//Pass the File + Parameters to cmdLine because otherwise it wont run under Winsows XP
	//******************************************************************************************
	bResult = StartupApplicationWithProcess (NULL, strPathAndApp.GetBuffer(),  &processInfo); //CreateProcess goes here
	strPathAndApp.ReleaseBuffer();


	if(bResult) { //(UINT)result > HINSTANCE_ERROR)
	
		
		AMLOGINFO(_T("LiveUpdate dialog box opened successfully."));

		DWORD dwRet;

		do {
			dwRet = ::MsgWaitForMultipleObjects(1, &processInfo.hProcess, FALSE, INFINITE, QS_ALLINPUT);
			if (dwRet != WAIT_OBJECT_0) {

				PeekMessageLoop();
			}
		} while ((dwRet != WAIT_OBJECT_0) && (dwRet != WAIT_FAILED));

		   

		
		if (!GetExitCodeProcess(processInfo.hProcess, &dwExitCode)) {
			   	AMLOGINFO(_T("LiveUpdate is not terminated normally.")); 
		}

		CloseHandle( processInfo.hProcess );
        CloseHandle( processInfo.hThread );
		



		AMLOGINFO(_T("LiveUpdate is terminated without any errors.")); 
		
		 

	} else {

		CString ErrorDescr = FormatErrorMessage(GetLastError()); 

		AfxMessageBox("Error !");
		
		
	}

    SetForegroundWindow();
    SetActiveWindow();    
	BringWindowToTop();	
	UpdateWindow();


Рейтинг:
2

Michael Haephrati

Вы получите больше контроля над новым процессом, который вы создаете, если вы используете CreateProcess() вместо ShellExecuteEx() и запускаете Update.exe на заднем плане.

SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(sa);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	PROCESS_INFORMATION pi;
	STARTUPINFO si;
	BOOL ret = FALSE;
	DWORD flags = CREATE_NO_WINDOW;

	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags |= STARTF_USESTDHANDLES;
	si.hStdInput = NULL;
	si.hStdError = h;
	si.hStdOutput = h;

	CString cmd = _T("cmd /c ") + _T("Update.exe");
	BOOL inherits = FALSE;

	ret = CreateProcess(NULL,
		cmd.GetBuffer(),
		NULL,
		NULL,
		inherits,
		flags,
		NULL,
		NULL,
		&si,
		&pi);

	DWORD dwExitCode = 0;
	if (ret)
	{
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
		return TRUE;
	}


sdancer75

Привет, спасибо за ваше решение. Этим вопросам уже около 2 лет, но я уже протестировал createprocess и shellexecute. В некоторых случаях оба метода теряют свою направленность.

Я нашла обоснованным решение для разных случаев помощью функции shellexecute или CreateProcess. Они, кажется, прекрасно работают с некоторыми ограничениями.

Я скопировал ваш код, чтобы проверить, работает ли он лучше, чем уже реализованный код.

Michael Haephrati

Пожалуйста, напишите ответ на свой собственный вопрос. Это было бы полезно для других людей, сталкивающихся с подобной проблемой.