2020-08-14 15:10:06 -07:00
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
using System.Drawing ;
using System.Windows.Forms ;
2022-12-14 13:37:23 +01:00
using Common.ComInterlop ;
2020-09-23 10:22:17 -07:00
using PreviewHandlerCommon.ComInterop ;
2020-08-14 15:10:06 -07:00
namespace Common
{
/// <summary>
/// Form based implementation of <see cref="IPreviewHandlerControl"/>.
/// </summary>
public abstract class FormHandlerControl : Form , IPreviewHandlerControl
{
/// <summary>
/// Needed to make the form a child window.
/// </summary>
private static int gwlStyle = - 16 ;
private static int wsChild = 0x40000000 ;
/// <summary>
/// Holds the parent window handle.
/// </summary>
private IntPtr parentHwnd ;
/// <summary>
/// Initializes a new instance of the <see cref="FormHandlerControl"/> class.
/// </summary>
public FormHandlerControl ( )
{
// Gets the handle of the control to create the control on the VI thread. Invoking the Control.Handle get accessor forces the creation of the underlying window for the control.
// This is important, because the thread that instantiates the preview handler component and calls its constructor is a single-threaded apartment (STA) thread, but the thread that calls into the interface members later on is a multithreaded apartment (MTA) thread. Windows Forms controls are meant to run on STA threads.
2022-09-28 18:18:55 +02:00
// More details: https://learn.microsoft.com/archive/msdn-magazine/2007/january/windows-vista-and-office-writing-your-own-preview-handlers.
2020-08-14 15:10:06 -07:00
var forceCreation = this . Handle ;
2020-09-23 10:22:17 -07:00
2020-08-14 15:10:06 -07:00
this . FormBorderStyle = FormBorderStyle . None ;
this . Visible = false ;
}
/// <inheritdoc />
2020-09-23 10:22:17 -07:00
public IntPtr GetWindowHandle ( )
2020-08-14 15:10:06 -07:00
{
return this . Handle ;
}
/// <inheritdoc />
public void QueryFocus ( out IntPtr result )
{
var getResult = IntPtr . Zero ;
2022-12-14 13:37:23 +01:00
getResult = NativeMethods . GetFocus ( ) ;
2020-08-14 15:10:06 -07:00
result = getResult ;
}
/// <inheritdoc />
public void SetBackgroundColor ( Color argbColor )
{
2022-12-14 13:37:23 +01:00
this . BackColor = argbColor ;
2020-08-14 15:10:06 -07:00
}
/// <inheritdoc />
public void SetFocus ( )
{
2022-12-14 13:37:23 +01:00
this . Focus ( ) ;
2020-08-14 15:10:06 -07:00
}
/// <inheritdoc />
public void SetFont ( Font font )
{
2022-12-14 13:37:23 +01:00
this . Font = font ;
2020-08-14 15:10:06 -07:00
}
/// <inheritdoc />
2023-03-16 15:51:31 +01:00
public void SetRect ( Rectangle windowBounds )
2020-08-14 15:10:06 -07:00
{
2024-03-20 15:28:43 +01:00
this . UpdateWindowBounds ( parentHwnd , windowBounds ) ;
2020-08-14 15:10:06 -07:00
}
/// <inheritdoc />
public void SetTextColor ( Color color )
{
2022-12-14 13:37:23 +01:00
this . ForeColor = color ;
2020-08-14 15:10:06 -07:00
}
/// <inheritdoc />
public void SetWindow ( IntPtr hwnd , Rectangle rect )
{
this . parentHwnd = hwnd ;
2024-03-20 15:28:43 +01:00
this . UpdateWindowBounds ( hwnd , rect ) ;
2020-08-14 15:10:06 -07:00
}
/// <inheritdoc />
public virtual void Unload ( )
{
2022-12-14 13:37:23 +01:00
this . Visible = false ;
foreach ( Control c in this . Controls )
2020-08-14 15:10:06 -07:00
{
2022-12-14 13:37:23 +01:00
c . Dispose ( ) ;
}
2020-08-14 15:10:06 -07:00
2022-12-14 13:37:23 +01:00
this . Controls . Clear ( ) ;
2020-08-14 15:10:06 -07:00
2022-04-14 17:27:22 +02:00
// Call garbage collection at the time of unloading of Preview.
2020-08-14 15:10:06 -07:00
// Which is preventing prevhost.exe to exit at the time of closing File explorer.
// Preview Handlers run in a separate process from PowerToys. This will not affect the performance of other modules.
2022-07-01 10:09:41 -04:00
// Mitigate the following GitHub issue: https://github.com/microsoft/PowerToys/issues/1468
2020-08-14 15:10:06 -07:00
GC . Collect ( ) ;
}
/// <inheritdoc />
public virtual void DoPreview < T > ( T dataSource )
{
this . Visible = true ;
}
/// <summary>
/// Update the Form Control window with the passed rectangle.
/// </summary>
2024-03-20 15:28:43 +01:00
public void UpdateWindowBounds ( IntPtr hwnd , Rectangle newBounds )
2020-08-14 15:10:06 -07:00
{
2024-06-16 17:32:33 +02:00
if ( this . Disposing | | this . IsDisposed )
{
// For unclear reasons, this can be called when handling an error and the form has already been disposed.
return ;
}
2022-12-14 13:37:23 +01:00
// We must set the WS_CHILD style to change the form to a control within the Explorer preview pane
int windowStyle = NativeMethods . GetWindowLong ( Handle , gwlStyle ) ;
if ( ( windowStyle & wsChild ) = = 0 )
{
_ = NativeMethods . SetWindowLong ( Handle , gwlStyle , windowStyle | wsChild ) ;
}
NativeMethods . SetParent ( Handle , hwnd ) ;
2024-03-20 15:28:43 +01:00
if ( newBounds . IsEmpty )
{
RECT s = default ( RECT ) ;
NativeMethods . GetClientRect ( hwnd , ref s ) ;
newBounds = new Rectangle ( s . Left , s . Top , s . Right - s . Left , s . Bottom - s . Top ) ;
}
2022-12-14 13:37:23 +01:00
2024-03-20 15:28:43 +01:00
if ( Bounds . Right ! = newBounds . Right | | Bounds . Bottom ! = newBounds . Bottom | | Bounds . Left ! = newBounds . Left | | Bounds . Top ! = newBounds . Top )
2020-08-14 15:10:06 -07:00
{
2024-03-20 15:28:43 +01:00
Bounds = newBounds ;
2022-12-14 13:37:23 +01:00
}
2020-08-14 15:10:06 -07:00
}
}
}