HRESULT CSysProgressDlg::ShowModeless(HWND hWndParent, BOOL immediately) { EnsureValid(); m_hWndProgDlg = NULL; if (!IsValid()) return E_FAIL; m_hWndParent = hWndParent; HRESULT hr = m_pIDlg->StartProgressDialog(hWndParent, NULL, m_dwDlgFlags, NULL); if(FAILED(hr)) return hr; ATL::CComPtr<IOleWindow> pOleWindow; HRESULT hr2 = m_pIDlg.QueryInterface(&pOleWindow); if(SUCCEEDED(hr2)) { hr2 = pOleWindow->GetWindow(&m_hWndProgDlg); if(SUCCEEDED(hr2)) { // see comment in ShowModal() for why we subclass the window if (!m_isVisible) { m_OrigProc = (WNDPROC) SetWindowLongPtr(m_hWndProgDlg, GWLP_WNDPROC, (LONG_PTR) fnSubclass); SetProp(m_hWndProgDlg, L"ParentWindow", m_hWndParent); SetProp(m_hWndProgDlg, L"OrigProc", m_OrigProc); } if (immediately) ShowWindow(m_hWndProgDlg, SW_SHOW); } } m_isVisible = true; return hr; }
HRESULT CSysProgressDlg::ShowModeless(HWND hWndParent, BOOL immediately) { EnsureValid(); m_hWndProgDlg = nullptr; if (!IsValid()) return E_FAIL; m_hWndParent = hWndParent; auto winId = GetWindowThreadProcessId(m_hWndParent, 0); auto threadId = GetCurrentThreadId(); if (winId != threadId) AttachThreadInput(winId, threadId, TRUE); m_hWndFocus = GetFocus(); if (winId != threadId) AttachThreadInput(winId, threadId, FALSE); HRESULT hr = m_pIDlg->StartProgressDialog(hWndParent, nullptr, m_dwDlgFlags, nullptr); if(FAILED(hr)) return hr; ATL::CComPtr<IOleWindow> pOleWindow; HRESULT hr2 = m_pIDlg.QueryInterface(&pOleWindow); if(SUCCEEDED(hr2)) { hr2 = pOleWindow->GetWindow(&m_hWndProgDlg); if(SUCCEEDED(hr2)) { if (immediately) ShowWindow(m_hWndProgDlg, SW_SHOW); } } m_isVisible = true; return hr; }
HRESULT CSysProgressDlg::ShowModal(HWND hWndParent, BOOL immediately /* = true */) { EnsureValid(); m_hWndProgDlg = NULL; if (!IsValid()) return E_FAIL; m_hWndParent = hWndParent; HRESULT hr = m_pIDlg->StartProgressDialog(hWndParent, NULL, m_dwDlgFlags | PROGDLG_MODAL, NULL); if(FAILED(hr)) return hr; ATL::CComPtr<IOleWindow> pOleWindow; HRESULT hr2 = m_pIDlg.QueryInterface(&pOleWindow); if(SUCCEEDED(hr2)) { hr2 = pOleWindow->GetWindow(&m_hWndProgDlg); if(SUCCEEDED(hr2)) { // StartProgressDialog creates a new thread to host the progress window. // When the window receives WM_DESTROY message StopProgressDialog() wrongly // attempts to re-enable the parent in the calling thread (our thread), // after the progress window is destroyed and the progress thread has died. // When the progress window dies, the system tries to assign a new foreground window. // It cannot assign to hwndParent because StartProgressDialog (w/PROGDLG_MODAL) disabled the parent window. // So the system hands the foreground activation to the next process that wants it in the // system foreground queue. Thus we lose our right to recapture the foreground window. // The way to fix this bug is to insert a call to EnableWindow(hWndParent) in the WM_DESTROY // handler for the progress window in the progress thread. // To do that, we Subclass the progress dialog // Since the window and thread created by the progress dialog object live on a few // milliseconds after calling Stop() and Release(), we must not store anything // in member variables of this class but must only store everything in the window // itself: thus we use SetProp()/GetProp() to store the data. if (!m_isVisible) { m_OrigProc = (WNDPROC) SetWindowLongPtr(m_hWndProgDlg, GWLP_WNDPROC, (LONG_PTR) fnSubclass); SetProp(m_hWndProgDlg, L"ParentWindow", m_hWndParent); SetProp(m_hWndProgDlg, L"OrigProc", m_OrigProc); } if(immediately) ShowWindow(m_hWndProgDlg, SW_SHOW); } } m_isVisible = true; return hr; }