diff --git a/src/modules/previewpane/GcodePreviewHandler/Program.cs b/src/modules/previewpane/GcodePreviewHandler/Program.cs index 0b0696fd01..cddf09cee4 100644 --- a/src/modules/previewpane/GcodePreviewHandler/Program.cs +++ b/src/modules/previewpane/GcodePreviewHandler/Program.cs @@ -36,15 +36,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode Rectangle s = new Rectangle(left, top, right - left, bottom - top); _previewHandlerControl = new GcodePreviewHandlerControl(); - _previewHandlerControl.SetWindow(hwnd, s); + + if (!_previewHandlerControl.SetWindow(hwnd, s)) + { + return; + } + _previewHandlerControl.DoPreview(filePath); NativeEventWaiter.WaitForEventLoop( Constants.GcodePreviewResizeEvent(), () => { - Rectangle s = default(Rectangle); - _previewHandlerControl.SetRect(s); + Rectangle s = default; + if (!_previewHandlerControl.SetRect(s)) + { + // When the parent HWND became invalid, the application won't respond to Application.Exit(). + Environment.Exit(0); + } }, Dispatcher.CurrentDispatcher, _tokenSource.Token); diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.cpp b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.cpp index 41a4fc202f..3ad3711dd0 100644 --- a/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.cpp +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.cpp @@ -159,9 +159,9 @@ IFACEMETHODIMP GcodePreviewHandler::DoPreview() { try { - if (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0) + if (m_hwndParent == NULL || (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0)) { - // Postponing Start GcodePreviewHandler.exe, position not yet initialized. preview will be done after initialisation + // Postponing Start GcodePreviewHandler.exe, parent and position not yet initialized. Preview will be done after initialisation. return S_OK; } Logger::info(L"Starting GcodePreviewHandler.exe"); @@ -189,6 +189,13 @@ IFACEMETHODIMP GcodePreviewHandler::DoPreview() sei.lpParameters = cmdLine.c_str(); sei.nShow = SW_SHOWDEFAULT; ShellExecuteEx(&sei); + + // Prevent to leak processes: preview is called multiple times when minimizing and restoring Explorer window + if (m_process) + { + TerminateProcess(m_process, 0); + } + m_process = sei.hProcess; } catch (std::exception& e) diff --git a/src/modules/previewpane/MarkdownPreviewHandler/Program.cs b/src/modules/previewpane/MarkdownPreviewHandler/Program.cs index 8b6b66f974..3d5fe7c207 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/Program.cs +++ b/src/modules/previewpane/MarkdownPreviewHandler/Program.cs @@ -36,15 +36,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown Rectangle s = new Rectangle(left, top, right - left, bottom - top); _previewHandlerControl = new MarkdownPreviewHandlerControl(); - _previewHandlerControl.SetWindow(hwnd, s); + + if (!_previewHandlerControl.SetWindow(hwnd, s)) + { + return; + } + _previewHandlerControl.DoPreview(filePath); NativeEventWaiter.WaitForEventLoop( Constants.MarkdownPreviewResizeEvent(), () => { - Rectangle s = default(Rectangle); - _previewHandlerControl.SetRect(s); + Rectangle s = default; + if (!_previewHandlerControl.SetRect(s)) + { + // When the parent HWND became invalid, the application won't respond to Application.Exit(). + Environment.Exit(0); + } }, Dispatcher.CurrentDispatcher, _tokenSource.Token); diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.cpp b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.cpp index 66970506f3..2837880a42 100644 --- a/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.cpp +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.cpp @@ -160,9 +160,9 @@ IFACEMETHODIMP MarkdownPreviewHandler::DoPreview() { try { - if (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0) + if (m_hwndParent == NULL || (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0)) { - // Postponing Start MarkdownPreviewHandler.exe, position not yet initialized. preview will be done after initialisation + // Postponing Start MarkdownPreviewHandler.exe, parent and position not yet initialized. Preview will be done after initialisation. return S_OK; } Logger::info(L"Starting MarkdownPreviewHandler.exe"); @@ -190,6 +190,13 @@ IFACEMETHODIMP MarkdownPreviewHandler::DoPreview() sei.lpParameters = cmdLine.c_str(); sei.nShow = SW_SHOWDEFAULT; ShellExecuteEx(&sei); + + // Prevent to leak processes: preview is called multiple times when minimizing and restoring Explorer window + if (m_process) + { + TerminateProcess(m_process, 0); + } + m_process = sei.hProcess; } catch (std::exception& e) diff --git a/src/modules/previewpane/MonacoPreviewHandler/Program.cs b/src/modules/previewpane/MonacoPreviewHandler/Program.cs index 7821d25658..fabb0ca710 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Program.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/Program.cs @@ -39,15 +39,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco Rectangle s = new Rectangle(left, top, right - left, bottom - top); _previewHandlerControl = new MonacoPreviewHandlerControl(); - _previewHandlerControl.SetWindow(hwnd, s); + + if (!_previewHandlerControl.SetWindow(hwnd, s)) + { + return; + } + _previewHandlerControl.DoPreview(filePath); NativeEventWaiter.WaitForEventLoop( Constants.DevFilesPreviewResizeEvent(), () => { - Rectangle s = default(Rectangle); - _previewHandlerControl.SetRect(s); + Rectangle s = default; + if (!_previewHandlerControl.SetRect(s)) + { + // When the parent HWND became invalid, the application won't respond to Application.Exit(). + Environment.Exit(0); + } }, Dispatcher.CurrentDispatcher, _tokenSource.Token); diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.cpp b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.cpp index 724e140574..004427de69 100644 --- a/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.cpp +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.cpp @@ -159,9 +159,9 @@ IFACEMETHODIMP MonacoPreviewHandler::DoPreview() { try { - if (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0) + if (m_hwndParent == NULL || (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0)) { - // Postponing Start MonacoPreviewHandler.exe, position not yet initialized. preview will be done after initialisation + // Postponing Start MonacoPreviewHandler.exe, parent and position not yet initialized. Preview will be done after initialisation. return S_OK; } @@ -189,6 +189,13 @@ IFACEMETHODIMP MonacoPreviewHandler::DoPreview() sei.lpParameters = cmdLine.c_str(); sei.nShow = SW_SHOWDEFAULT; ShellExecuteEx(&sei); + + // Prevent to leak processes: preview is called multiple times when minimizing and restoring Explorer window + if (m_process) + { + TerminateProcess(m_process, 0); + } + m_process = sei.hProcess; } catch (std::exception& e) diff --git a/src/modules/previewpane/PdfPreviewHandler/Program.cs b/src/modules/previewpane/PdfPreviewHandler/Program.cs index d9427dc215..7c883b7989 100644 --- a/src/modules/previewpane/PdfPreviewHandler/Program.cs +++ b/src/modules/previewpane/PdfPreviewHandler/Program.cs @@ -35,15 +35,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf Rectangle s = new Rectangle(left, top, right - left, bottom - top); _previewHandlerControl = new PdfPreviewHandlerControl(); - _previewHandlerControl.SetWindow(hwnd, s); + + if (!_previewHandlerControl.SetWindow(hwnd, s)) + { + return; + } + _previewHandlerControl.DoPreview(filePath); NativeEventWaiter.WaitForEventLoop( Constants.PdfPreviewResizeEvent(), () => { - Rectangle s = default(Rectangle); - _previewHandlerControl.SetRect(s); + Rectangle s = default; + if (!_previewHandlerControl.SetRect(s)) + { + // When the parent HWND became invalid, the application won't respond to Application.Exit(). + Environment.Exit(0); + } }, Dispatcher.CurrentDispatcher, _tokenSource.Token); diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.cpp b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.cpp index 7ad100bbc4..aadc5ce709 100644 --- a/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.cpp +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.cpp @@ -147,6 +147,7 @@ IFACEMETHODIMP PdfPreviewHandler::SetRect(const RECT* prc) } } } + m_rcParent = *prc; hr = S_OK; } return hr; @@ -156,9 +157,9 @@ IFACEMETHODIMP PdfPreviewHandler::DoPreview() { try { - if (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0) + if (m_hwndParent == NULL || (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0)) { - // Postponing Start PdfPreviewHandler.exe, position not yet initialized. preview will be done after initialisation + // Postponing Start PdfPreviewHandler.exe, parent and position not yet initialized. Preview will be done after initialisation. return S_OK; } Logger::info(L"Starting PdfPreviewHandler.exe"); @@ -186,6 +187,13 @@ IFACEMETHODIMP PdfPreviewHandler::DoPreview() sei.lpParameters = cmdLine.c_str(); sei.nShow = SW_SHOWDEFAULT; ShellExecuteEx(&sei); + + // Prevent to leak processes: preview is called multiple times when minimizing and restoring Explorer window + if (m_process) + { + TerminateProcess(m_process, 0); + } + m_process = sei.hProcess; } catch (std::exception& e) diff --git a/src/modules/previewpane/QoiPreviewHandler/Program.cs b/src/modules/previewpane/QoiPreviewHandler/Program.cs index 5a9f5bfa2b..469d0c3b25 100644 --- a/src/modules/previewpane/QoiPreviewHandler/Program.cs +++ b/src/modules/previewpane/QoiPreviewHandler/Program.cs @@ -36,15 +36,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Qoi Rectangle s = new Rectangle(left, top, right - left, bottom - top); _previewHandlerControl = new QoiPreviewHandlerControl(); - _previewHandlerControl.SetWindow(hwnd, s); + + if (!_previewHandlerControl.SetWindow(hwnd, s)) + { + return; + } + _previewHandlerControl.DoPreview(filePath); NativeEventWaiter.WaitForEventLoop( Constants.QoiPreviewResizeEvent(), () => { - Rectangle s = default(Rectangle); - _previewHandlerControl.SetRect(s); + Rectangle s = default; + if (!_previewHandlerControl.SetRect(s)) + { + // When the parent HWND became invalid, the application won't respond to Application.Exit(). + Environment.Exit(0); + } }, Dispatcher.CurrentDispatcher, _tokenSource.Token); diff --git a/src/modules/previewpane/QoiPreviewHandlerCpp/QoiPreviewHandler.cpp b/src/modules/previewpane/QoiPreviewHandlerCpp/QoiPreviewHandler.cpp index 9d1ce0a813..7cf8873bba 100644 --- a/src/modules/previewpane/QoiPreviewHandlerCpp/QoiPreviewHandler.cpp +++ b/src/modules/previewpane/QoiPreviewHandlerCpp/QoiPreviewHandler.cpp @@ -159,9 +159,9 @@ IFACEMETHODIMP QoiPreviewHandler::DoPreview() { try { - if (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0) + if (m_hwndParent == NULL || (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0)) { - // Postponing Start QoiPreviewHandler.exe, position not yet initialized. preview will be done after initialisation + // Postponing Start QoiPreviewHandler.exe, parent and position not yet initialized. Preview will be done after initialisation. return S_OK; } Logger::info(L"Starting QoiPreviewHandler.exe"); @@ -189,6 +189,13 @@ IFACEMETHODIMP QoiPreviewHandler::DoPreview() sei.lpParameters = cmdLine.c_str(); sei.nShow = SW_SHOWDEFAULT; ShellExecuteEx(&sei); + + // Prevent to leak processes: preview is called multiple times when minimizing and restoring Explorer window + if (m_process) + { + TerminateProcess(m_process, 0); + } + m_process = sei.hProcess; } catch (std::exception& e) diff --git a/src/modules/previewpane/SvgPreviewHandler/Program.cs b/src/modules/previewpane/SvgPreviewHandler/Program.cs index 4b506ded1a..e46e9f784c 100644 --- a/src/modules/previewpane/SvgPreviewHandler/Program.cs +++ b/src/modules/previewpane/SvgPreviewHandler/Program.cs @@ -36,15 +36,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg Rectangle s = new Rectangle(left, top, right - left, bottom - top); _previewHandlerControl = new SvgPreviewControl(); - _previewHandlerControl.SetWindow(hwnd, s); + + if (!_previewHandlerControl.SetWindow(hwnd, s)) + { + return; + } + _previewHandlerControl.DoPreview(filePath); NativeEventWaiter.WaitForEventLoop( Constants.SvgPreviewResizeEvent(), () => { - Rectangle s = default(Rectangle); - _previewHandlerControl.SetRect(s); + Rectangle s = default; + if (!_previewHandlerControl.SetRect(s)) + { + // When the parent HWND became invalid, the application won't respond to Application.Exit(). + Environment.Exit(0); + } }, Dispatcher.CurrentDispatcher, _tokenSource.Token); diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.cpp b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.cpp index 174163f77a..e0c257db49 100644 --- a/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.cpp +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.cpp @@ -149,6 +149,7 @@ IFACEMETHODIMP SvgPreviewHandler::SetRect(const RECT* prc) } } } + m_rcParent = *prc; hr = S_OK; } return hr; @@ -158,9 +159,9 @@ IFACEMETHODIMP SvgPreviewHandler::DoPreview() { try { - if (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0) + if (m_hwndParent == NULL || (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0)) { - // Postponing Start SvgPreviewHandler.exe, position not yet initialized. preview will be done after initialisation + // Postponing Start SvgPreviewHandler.exe, parent and position not yet initialized. Preview will be done after initialisation. return S_OK; } Logger::info(L"Starting SvgPreviewHandler.exe"); @@ -188,6 +189,13 @@ IFACEMETHODIMP SvgPreviewHandler::DoPreview() sei.lpParameters = cmdLine.c_str(); sei.nShow = SW_SHOWDEFAULT; ShellExecuteEx(&sei); + + // Prevent to leak processes: preview is called multiple times when minimizing and restoring Explorer window + if (m_process) + { + TerminateProcess(m_process, 0); + } + m_process = sei.hProcess; } catch (std::exception& e) diff --git a/src/modules/previewpane/common/cominterop/NativeMethods.cs b/src/modules/previewpane/common/cominterop/NativeMethods.cs index 611136b1a3..58979d0ff8 100644 --- a/src/modules/previewpane/common/cominterop/NativeMethods.cs +++ b/src/modules/previewpane/common/cominterop/NativeMethods.cs @@ -37,5 +37,8 @@ namespace PreviewHandlerCommon.ComInterop [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetClientRect(IntPtr hWnd, ref Common.ComInterlop.RECT rect); + + [DllImport("user32.dll")] + public static extern bool IsWindow(IntPtr hWnd); } } diff --git a/src/modules/previewpane/common/controls/FormHandlerControl.cs b/src/modules/previewpane/common/controls/FormHandlerControl.cs index ef798ed424..dd3cd9f897 100644 --- a/src/modules/previewpane/common/controls/FormHandlerControl.cs +++ b/src/modules/previewpane/common/controls/FormHandlerControl.cs @@ -73,9 +73,9 @@ namespace Common } /// - public void SetRect(Rectangle windowBounds) + public bool SetRect(Rectangle windowBounds) { - this.UpdateWindowBounds(parentHwnd, windowBounds); + return this.UpdateWindowBounds(parentHwnd, windowBounds); } /// @@ -85,10 +85,10 @@ namespace Common } /// - public void SetWindow(IntPtr hwnd, Rectangle rect) + public bool SetWindow(IntPtr hwnd, Rectangle rect) { this.parentHwnd = hwnd; - this.UpdateWindowBounds(hwnd, rect); + return this.UpdateWindowBounds(hwnd, rect); } /// @@ -118,12 +118,18 @@ namespace Common /// /// Update the Form Control window with the passed rectangle. /// - public void UpdateWindowBounds(IntPtr hwnd, Rectangle newBounds) + public bool UpdateWindowBounds(IntPtr hwnd, Rectangle newBounds) { + if (hwnd == IntPtr.Zero || !NativeMethods.IsWindow(hwnd)) + { + // If the HWND is IntPtr.Zero the desktop window will be used as parent. + return false; + } + if (this.Disposing || this.IsDisposed) { // For unclear reasons, this can be called when handling an error and the form has already been disposed. - return; + return false; } // We must set the WS_CHILD style to change the form to a control within the Explorer preview pane @@ -133,7 +139,10 @@ namespace Common _ = NativeMethods.SetWindowLong(Handle, gwlStyle, windowStyle | wsChild); } - NativeMethods.SetParent(Handle, hwnd); + if (NativeMethods.SetParent(Handle, hwnd) == IntPtr.Zero) + { + return false; + } if (newBounds.IsEmpty) { @@ -146,6 +155,8 @@ namespace Common { Bounds = newBounds; } + + return true; } } } diff --git a/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs b/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs index a5e074b8db..c0b7bc291f 100644 --- a/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs +++ b/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs @@ -58,14 +58,16 @@ namespace Common /// Directs the control to change the area within the parent hwnd that it draws into. /// /// Instance of Rectangle defining the area. - void SetRect(Rectangle windowBounds); + /// if the operation was successful; otherwise, . + bool SetRect(Rectangle windowBounds); /// - /// Sets the parent window of the previewer window, as well as the area within the parent to be used for the previewer window.. + /// Sets the parent window of the previewer window, as well as the area within the parent to be used for the previewer window. /// /// Pointer to the parent window handle. /// Instance of Rectangle defining the area. - void SetWindow(IntPtr hwnd, Rectangle rect); + /// if the operation was successful; otherwise, . + bool SetWindow(IntPtr hwnd, Rectangle rect); /// /// Called by Preview Handler to start the preview.