diff --git a/src/Files.App.CsWin32/NativeMethods.txt b/src/Files.App.CsWin32/NativeMethods.txt
index 9d7c337d7c4d..2cd8c6234c8c 100644
--- a/src/Files.App.CsWin32/NativeMethods.txt
+++ b/src/Files.App.CsWin32/NativeMethods.txt
@@ -133,3 +133,25 @@ IFileOperation
IShellItem2
PSGetPropertyKeyFromName
ShellExecuteEx
+E_FAIL
+S_OK
+S_FALSE
+MSG
+E_NOTIMPL
+LOGFONTW
+AssocCreate
+IQueryAssociations
+UnregisterClass
+SetWindowLong
+GetModuleHandle
+RegisterClassEx
+CREATESTRUCTW
+AssocQueryString
+IPreviewHandlerFrame
+IPreviewHandlerVisuals
+IObjectWithSite
+IInitializeWithStream
+IInitializeWithStreamNative
+IInitializeWithFile
+IInitializeWithItem
+SHCreateStreamOnFileEx
diff --git a/src/Files.App/UserControls/FilePreviews/ShellPreview.xaml.cs b/src/Files.App/UserControls/FilePreviews/ShellPreview.xaml.cs
index d0f09e7aed50..935227a1cc07 100644
--- a/src/Files.App/UserControls/FilePreviews/ShellPreview.xaml.cs
+++ b/src/Files.App/UserControls/FilePreviews/ShellPreview.xaml.cs
@@ -1,8 +1,11 @@
+// Copyright (c) 2024 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
using Files.App.ViewModels.Previews;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
-using Vanara.PInvoke;
using Windows.Foundation;
+using Windows.Win32.Foundation;
namespace Files.App.UserControls.FilePreviews
{
@@ -13,13 +16,15 @@ public sealed partial class ShellPreview : UserControl
public ShellPreview(ShellPreviewViewModel model)
{
ViewModel = model;
- this.InitializeComponent();
+
+ InitializeComponent();
}
private void PreviewHost_Loaded(object sender, RoutedEventArgs e)
{
ViewModel.LoadPreview(contentPresenter);
ViewModel.SizeChanged(GetPreviewSize());
+
if (XamlRoot.Content is FrameworkElement element)
{
element.SizeChanged += PreviewHost_SizeChanged;
@@ -38,11 +43,13 @@ private RECT GetPreviewSize()
var physicalSize = contentPresenter.RenderSize;
var physicalPos = source.TransformPoint(new Point(0, 0));
var scale = XamlRoot.RasterizationScale;
- var result = new RECT();
- result.Left = (int)(physicalPos.X * scale + 0.5);
- result.Top = (int)(physicalPos.Y * scale + 0.5);
- result.Width = (int)(physicalSize.Width * scale + 0.5);
- result.Height = (int)(physicalSize.Height * scale + 0.5);
+
+ var result = RECT.FromXYWH(
+ (int)(physicalPos.X * scale + 0.5),
+ (int)(physicalPos.Y * scale + 0.5),
+ (int)(physicalSize.Width * scale + 0.5),
+ (int)(physicalSize.Height * scale + 0.5));
+
return result;
}
@@ -53,6 +60,7 @@ private void PreviewHost_Unloaded(object sender, RoutedEventArgs e)
element.SizeChanged -= PreviewHost_SizeChanged;
element.PointerEntered -= PreviewHost_PointerEntered;
}
+
ViewModel.UnloadPreview();
}
diff --git a/src/Files.App/Utils/Shell/ItemStreamHelper.cs b/src/Files.App/Utils/Shell/ItemStreamHelper.cs
deleted file mode 100644
index 523845e21d8c..000000000000
--- a/src/Files.App/Utils/Shell/ItemStreamHelper.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using Vanara.PInvoke;
-
-namespace Files.App.Utils.Shell
-{
- public static class ItemStreamHelper
- {
- static readonly Guid IShellItemIid = Guid.ParseExact("43826d1e-e718-42ee-bc55-a1e261c37bfe", "d");
-
- public static IntPtr IShellItemFromPath(string path)
- {
- IntPtr psi;
- Guid iid = IShellItemIid;
- var hr = Win32PInvoke.SHCreateItemFromParsingName(path, IntPtr.Zero, ref iid, out psi);
- if ((int)hr < 0)
- return IntPtr.Zero;
- return psi;
- }
-
- public static IntPtr IStreamFromPath(string path)
- {
- IntPtr pstm;
- var hr = Win32PInvoke.SHCreateStreamOnFileEx(path,
- STGM.STGM_READ | STGM.STGM_FAILIFTHERE | STGM.STGM_SHARE_DENY_NONE,
- 0, 0, IntPtr.Zero, out pstm);
- if ((int)hr < 0)
- return IntPtr.Zero;
- return pstm;
- }
-
- public static void ReleaseObject(IntPtr obj)
- {
- Marshal.Release(obj);
- }
- }
-}
diff --git a/src/Files.App/Utils/Shell/PreviewHandler.cs b/src/Files.App/Utils/Shell/PreviewHandler.cs
index 6cb5f4001798..18e7978a11ee 100644
--- a/src/Files.App/Utils/Shell/PreviewHandler.cs
+++ b/src/Files.App/Utils/Shell/PreviewHandler.cs
@@ -1,407 +1,276 @@
-using System;
-using System.Collections.Generic;
+// Copyright (c) 2024 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
+using LibGit2Sharp;
using System.Runtime.InteropServices;
-using System.Runtime.InteropServices.ComTypes;
-using Vanara.PInvoke;
using Windows.UI;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.Graphics.Gdi;
+using Windows.Win32.System.Com;
+using Windows.Win32.System.Ole;
+using Windows.Win32.UI.Shell;
+using Windows.Win32.UI.Shell.PropertiesSystem;
+using Windows.Win32.UI.WindowsAndMessaging;
namespace Files.App.Utils.Shell
{
///
- /// Credits: https://github.com/GeeLaw/PreviewHost/
+ /// Provides a set of functionalities to interact with Windows preview handlers.
///
- public sealed class PreviewHandler : IDisposable
+ ///
+ /// Credit:
+ ///
+ public unsafe sealed class PreviewHandler : IDisposable
{
- #region IPreviewHandlerFrame support
+ // Fields
- [StructLayout(LayoutKind.Sequential)]
- public struct PreviewHandlerFrameInfo
- {
- public IntPtr AcceleratorTableHandle;
- public uint AcceleratorEntryCount;
- }
+ private readonly IPreviewHandlerFrame.Interface _previewHandlerFrame;
+ private readonly IPreviewHandler* _pPreviewHandler;
+ private readonly IPreviewHandlerVisuals* _previewHandlerVisuals;
+ private readonly HWND _hWnd;
+ private bool _disposed;
+ private bool _initialized;
+ private bool _shown;
- [ComImport, Guid("fec87aaf-35f9-447a-adb7-20234491401a"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- public interface IPreviewHandlerFrame
- {
- [PreserveSig]
- HRESULT GetWindowContext(out PreviewHandlerFrameInfo pinfo);
- [PreserveSig]
- HRESULT TranslateAccelerator(ref MSG pmsg);
- }
+ // Initializer
- public sealed class PreviewHandlerFrame : IPreviewHandlerFrame, IDisposable
+ ///
+ /// Initializes an instance of class.
+ ///
+ ///
+ ///
+ public PreviewHandler(Guid clsid, HWND frame)
{
- bool disposed;
- nint hwnd;
+ _disposed = true;
+ _initialized = false;
+ _shown = false;
+ _hWnd = frame;
- public PreviewHandlerFrame(nint frame)
- {
- disposed = true;
- disposed = false;
- hwnd = frame;
- }
-
- public void Dispose()
- {
- disposed = true;
- }
+ // Initialize preview handler's frame
+ _previewHandlerFrame = new CPreviewHandlerFrame(frame);
- public HRESULT GetWindowContext(out PreviewHandlerFrameInfo pinfo)
- {
- pinfo.AcceleratorTableHandle = IntPtr.Zero;
- pinfo.AcceleratorEntryCount = 0;
- if (disposed)
- return HRESULT.E_FAIL;
- return HRESULT.S_OK;
- }
-
- public HRESULT TranslateAccelerator(ref MSG pmsg)
+ try
{
- if (disposed)
- return HRESULT.E_FAIL;
- return HRESULT.S_FALSE;
- }
- }
+ HRESULT hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, out IPreviewHandler* pPreviewHandler);
+ if (hr.Value < 0)
+ throw new COMException("Cannot create class " + clsid.ToString() + " as IPreviewHandler.", hr.Value);
+ else if (pPreviewHandler is null)
+ throw new COMException("Cannot create class " + clsid.ToString() + " as IPreviewHandler.");
- #endregion IPreviewHandlerFrame support
+ _pPreviewHandler = pPreviewHandler;
- #region IPreviewHandler major interfaces
+ Debug.WriteLine($"IPreviewHandler was successfully initialized from {clsid:B}.");
- [ComImport, Guid("8895b1c6-b41f-4c1c-a562-0d564250836f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- interface IPreviewHandler
- {
- [PreserveSig]
- HRESULT SetWindow(IntPtr hwnd, ref RECT prc);
- [PreserveSig]
- HRESULT SetRect(ref RECT prc);
- [PreserveSig]
- HRESULT DoPreview();
- [PreserveSig]
- HRESULT Unload();
- [PreserveSig]
- HRESULT SetFocus();
- [PreserveSig]
- HRESULT QueryFocus(out IntPtr phwnd);
- // TranslateAccelerator is not used here.
- }
+ // Get IObjectWithSite
+ ComPtr pObjectWithSite = default;
+ _pPreviewHandler->QueryInterface(typeof(IObjectWithSite).GUID, out *(void**)pObjectWithSite.GetAddressOf());
+ if (pObjectWithSite.IsNull)
+ throw new COMException("Cannot cast class " + clsid.ToString() + " as IObjectWithSite.");
- [ComImport, Guid("196bf9a5-b346-4ef0-aa1e-5dcdb76768b1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- interface IPreviewHandlerVisuals
- {
- [PreserveSig]
- HRESULT SetBackgroundColor(uint color);
- [PreserveSig]
- HRESULT SetFont(ref LOGFONT plf);
- [PreserveSig]
- HRESULT SetTextColor(uint color);
- }
+ // Set site
+ var pPreviewHandlerFrame = Marshal.GetIUnknownForObject(_previewHandlerFrame);
+ hr = pObjectWithSite.Get()->SetSite((IUnknown*)pPreviewHandlerFrame);
+ if (hr.Value < 0)
+ throw new COMException("Cannot set site to the preview handler object.", hr.Value);
- static uint ColorRefFromColor(Color color)
- {
- return (((uint)color.B) << 16) | (((uint)color.G) << 8) | ((uint)color.R);
- }
+ Debug.WriteLine($"Site IPreviewHandlerFrame was successfully set to IPreviewHandler.");
- [ComImport, Guid("fc4801a3-2ba9-11cf-a229-00aa003d7352"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- interface IObjectWithSite
- {
- [PreserveSig]
- HRESULT SetSite([In, MarshalAs(UnmanagedType.IUnknown)] object pUnkSite);
- // GetSite is not used.
- }
+ // Get IPreviewHandlerVisuals
+ IPreviewHandlerVisuals* previewHandlerVisuals = default;
+ _pPreviewHandler->QueryInterface(typeof(IPreviewHandlerVisuals).GUID, out *(void**)&previewHandlerVisuals);
+ if (previewHandlerVisuals == null)
+ throw new COMException("Cannot cast class " + clsid.ToString() + " as IPreviewHandlerVisuals.");
- #endregion IPreviewHandler major interfaces
+ _previewHandlerVisuals = previewHandlerVisuals;
- bool disposed;
- bool init;
- bool shown;
- PreviewHandlerFrame comSite;
- nint hwnd;
- IPreviewHandler previewHandler;
- IPreviewHandlerVisuals visuals;
- IntPtr pPreviewHandler;
+ Debug.WriteLine($"IPreviewHandlerVisuals was successfully queried from IPreviewHandler.");
- public PreviewHandler(Guid clsid, nint frame)
- {
- disposed = true;
- init = false;
- shown = false;
- comSite = new PreviewHandlerFrame(frame);
- hwnd = frame;
- try
- {
- SetupHandler(clsid);
- disposed = false;
+ _disposed = false;
}
catch
{
- if (previewHandler != null)
- Marshal.ReleaseComObject(previewHandler);
- previewHandler = null;
- if (pPreviewHandler != IntPtr.Zero)
- Marshal.Release(pPreviewHandler);
- pPreviewHandler = IntPtr.Zero;
- comSite.Dispose();
- comSite = null;
- throw;
- }
- }
-
- static readonly Guid IPreviewHandlerIid = Guid.ParseExact("8895b1c6-b41f-4c1c-a562-0d564250836f", "d");
+ if (_pPreviewHandler is not null)
+ {
+ _pPreviewHandler->Release();
+ _pPreviewHandler = null;
+ }
- void SetupHandler(Guid clsid)
- {
- IntPtr pph;
- var iid = IPreviewHandlerIid;
- var cannotCreate = "Cannot create class " + clsid.ToString() + " as IPreviewHandler.";
- var cannotCast = "Cannot cast class " + clsid.ToString() + " as IObjectWithSite.";
- var cannotSetSite = "Cannot set site to the preview handler object.";
- // Important: manully calling CoCreateInstance is necessary.
- // If we use Activator.CreateInstance(Type.GetTypeFromCLSID(...)),
- // CLR will allow in-process server, which defeats isolation and
- // creates strange bugs.
- HRESULT hr = Win32PInvoke.CoCreateInstance(ref clsid, IntPtr.Zero, Win32PInvoke.ClassContext.LocalServer, ref iid, out pph);
- // See https://blogs.msdn.microsoft.com/adioltean/2005/06/24/when-cocreateinstance-returns-0x80080005-co_e_server_exec_failure/
- // CO_E_SERVER_EXEC_FAILURE also tends to happen when debugging in Visual Studio.
- // Moreover, to create the instance in a server at low integrity level, we need
- // to use another thread with low mandatory label. We keep it simple by creating
- // a same-integrity object.
- //if (hr == HRESULT.CO_E_SERVER_EXEC_FAILURE)
- // hr = CoCreateInstance(ref clsid, IntPtr.Zero, ClassContext.LocalServer, ref iid, out pph);
- if ((int)hr < 0)
- throw new COMException(cannotCreate, (int)hr);
- pPreviewHandler = pph;
- var previewHandlerObject = Marshal.GetUniqueObjectForIUnknown(pph);
- previewHandler = previewHandlerObject as IPreviewHandler;
- if (previewHandler == null)
- {
- Marshal.ReleaseComObject(previewHandlerObject);
- throw new COMException(cannotCreate);
+ throw;
}
- var objectWithSite = previewHandlerObject as IObjectWithSite;
- if (objectWithSite == null)
- throw new COMException(cannotCast);
- hr = objectWithSite.SetSite(comSite);
- if ((int)hr < 0)
- throw new COMException(cannotSetSite, (int)hr);
- visuals = previewHandlerObject as IPreviewHandlerVisuals;
- }
-
- #region Initialization interfaces
-
- [ComImport, Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- interface IInitializeWithStream
- {
- [PreserveSig]
- HRESULT Initialize(IStream psi, STGM grfMode);
}
- [ComImport, Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- interface IInitializeWithStreamNative
- {
- [PreserveSig]
- HRESULT Initialize(IntPtr psi, STGM grfMode);
- }
-
- static readonly Guid IInitializeWithStreamIid = Guid.ParseExact("b824b49d-22ac-4161-ac8a-9916e8fa3f7f", "d");
+ // Methods
- [ComImport, Guid("b7d14566-0509-4cce-a71f-0a554233bd9b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- interface IInitializeWithFile
+ ///
+ /// Initializes the preview handler with file.
+ ///
+ /// The file name to use to initialize the preview handler.
+ /// True If succeeded, otherwise, false.
+ public bool Initialize(string path)
{
- [PreserveSig]
- HRESULT Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, STGM grfMode);
- }
+ List exceptions = [];
- static readonly Guid IInitializeWithFileIid = Guid.ParseExact("b7d14566-0509-4cce-a71f-0a554233bd9b", "d");
+ // We try IStream first because this gives us the best security.
+ // If we initialize with string or IShellItem, we have no control over
+ // how the preview handler opens the file, which might decide to open the file for read/write exclusively.
+ try
+ {
+ using ComPtr pStream = default;
+ HRESULT hr = PInvoke.SHCreateStreamOnFileEx(path, (uint)(STGM.STGM_READ | STGM.STGM_FAILIFTHERE | STGM.STGM_SHARE_DENY_NONE), 0, false, null, pStream.GetAddressOf());
+ if (hr.Value < 0)
+ throw new InvalidComObjectException($"SHCreateItemFromParsingName failed to get IShellItem for preview handling with the error {hr.Value:X}.");
- [ComImport, Guid("7f73be3f-fb79-493c-a6c7-7ee14e245841"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- interface IInitializeWithItem
- {
- [PreserveSig]
- HRESULT Initialize(IntPtr psi, STGM grfMode);
- }
+ if (!pStream.IsNull)
+ {
+ ObjectDisposedException.ThrowIf(_disposed, this);
- static readonly Guid IInitializeWithItemIid = Guid.ParseExact("7f73be3f-fb79-493c-a6c7-7ee14e245841", "d");
+ if (_initialized)
+ throw new InvalidOperationException("Preview handler is already initialized and cannot be initialized again.");
- #endregion
+ using ComPtr pInitializeWithStream = default;
+ _pPreviewHandler->QueryInterface(typeof(IInitializeWithStream).GUID, out *(void**)pInitializeWithStream.GetAddressOf());
+ if (pInitializeWithStream.IsNull)
+ throw new COMException($"{nameof(IInitializeWithStream)} could not queried from IPreviewHandler.");
- ///
- /// Tries to initialize the preview handler with an IStream.
- ///
- /// This exception is thrown if IInitializeWithStream.Initialize fails for reason other than E_NOTIMPL.
- /// Thrown if mode is neither Read nor ReadWrite.
- /// The IStream interface used to initialize the preview handler.
- /// The storage mode, must be Read or ReadWrite.
- /// If the handler supports initialization with IStream, true; otherwise, false.
- public bool InitWithStream(IStream stream, STGM mode)
- {
- if (mode != STGM.STGM_READ && mode != STGM.STGM_READWRITE)
- throw new ArgumentOutOfRangeException("mode", mode, "The argument mode must be Read or ReadWrite.");
- var iws = previewHandler as IInitializeWithStream;
- if (iws == null)
- return false;
- var hr = iws.Initialize(stream, mode);
- if (hr == HRESULT.E_NOTIMPL)
- return false;
- if ((int)hr < 0)
- throw new COMException("IInitializeWithStream.Initialize failed.", (int)hr);
- init = true;
- return true;
- }
+ hr = pInitializeWithStream.Get()->Initialize(pStream.Get(), (uint)STGM.STGM_READ);
+ if (hr == HRESULT.E_NOTIMPL)
+ throw new NotImplementedException($"{nameof(IInitializeWithStream)}.Initialize() is not implemented.");
+ else if ((int)hr < 0)
+ throw new COMException($"{nameof(IInitializeWithStream)}.Initialize() failed.", (int)hr);
- ///
- /// Same as InitWithStream(IStream, STGM).
- ///
- /// See InitWithStream(IStream, STGM).
- /// See InitWithStream(IStream, STGM).
- /// The native pointer to the IStream interface.
- /// The storage mode.
- /// True or false, see InitWithStream(IStream, STGM).
- public bool InitWithStream(IntPtr pStream, STGM mode)
- {
- EnsureNotDisposed();
- EnsureNotInitialized();
- if (mode != STGM.STGM_READ && mode != STGM.STGM_READWRITE)
- throw new ArgumentOutOfRangeException("mode", mode, "The argument mode must be Read or ReadWrite.");
- var iws = previewHandler as IInitializeWithStreamNative;
- if (iws == null)
- return false;
- var hr = iws.Initialize(pStream, mode);
- if (hr == HRESULT.E_NOTIMPL)
- return false;
- if ((int)hr < 0)
- throw new COMException("IInitializeWithStream.Initialize failed.", (int)hr);
- init = true;
- return true;
- }
-
- ///
- /// Same as InitWithStream(IStream, STGM).
- ///
- /// See InitWithStream(IStream, STGM).
- /// See InitWithStream(IStream, STGM).
- /// The native pointer to the IShellItem interface.
- /// The storage mode.
- /// True or false, see InitWithStream(IStream, STGM).
- public bool InitWithItem(IntPtr psi, STGM mode)
- {
- EnsureNotDisposed();
- EnsureNotInitialized();
- if (mode != STGM.STGM_READ && mode != STGM.STGM_READWRITE)
- throw new ArgumentOutOfRangeException("mode", mode, "The argument mode must be Read or ReadWrite.");
- var iwi = previewHandler as IInitializeWithItem;
- if (iwi == null)
- return false;
- var hr = iwi.Initialize(psi, mode);
- if (hr == HRESULT.E_NOTIMPL)
- return false;
- if ((int)hr < 0)
- throw new COMException("IInitializeWithItem.Initialize failed.", (int)hr);
- init = true;
- return true;
- }
+ _initialized = true;
- ///
- /// Same as InitWithStream(IStream, STGM).
- ///
- /// See InitWithStream(IStream, STGM).
- /// See InitWithStream(IStream, STGM).
- /// The path to the file.
- /// The storage mode.
- /// True or false, see InitWithStream(IStream, STGM).
- public bool InitWithFile(string path, STGM mode)
- {
- EnsureNotDisposed();
- EnsureNotInitialized();
- if (mode != STGM.STGM_READ && mode != STGM.STGM_READWRITE)
- throw new ArgumentOutOfRangeException("mode", mode, "The argument mode must be Read or ReadWrite.");
- var iwf = previewHandler as IInitializeWithFile;
- if (iwf == null)
- return false;
- var hr = iwf.Initialize(path, mode);
- if (hr == HRESULT.E_NOTIMPL)
- return false;
- if ((int)hr < 0)
- throw new COMException("IInitializeWithFile.Initialize failed.", (int)hr);
- init = true;
- return true;
- }
+ Debug.WriteLine($"Preview handler was successfully initialized with {nameof(IInitializeWithStream)}.");
- ///
- /// Tries each way to initialize the object with a file.
- ///
- /// The file name.
- /// If initialization was successful, true; otherwise, an exception is thrown.
- public bool InitWithFileWithEveryWay(string path)
- {
- var exceptions = new List();
- var pobj = IntPtr.Zero;
- // Why should we try IStream first?
- // Because that gives us the best security.
- // If we initialize with string or IShellItem,
- // we have no control over how the preview handler
- // opens the file, which might decide to open the
- // file for read/write exclusively.
- try
- {
- pobj = ItemStreamHelper.IStreamFromPath(path);
- if (pobj != IntPtr.Zero
- && InitWithStream(pobj, STGM.STGM_READ))
return true;
+ }
}
catch (Exception ex)
{
exceptions.Add(ex);
}
- finally
- {
- if (pobj != IntPtr.Zero)
- ItemStreamHelper.ReleaseObject(pobj);
- pobj = IntPtr.Zero;
- }
- // Next try file because that could save us some P/Invokes.
+
try
{
- if (InitWithFile(path, STGM.STGM_READ))
- return true;
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
+ if (_initialized)
+ throw new InvalidOperationException("Preview handler is already initialized and cannot be initialized again.");
+
+ using ComPtr pInitializeWithFile = default;
+ _pPreviewHandler->QueryInterface(typeof(IInitializeWithFile).GUID, out *(void**)pInitializeWithFile.GetAddressOf());
+ if (pInitializeWithFile.IsNull)
+ throw new COMException($"{nameof(IInitializeWithFile)} could not queried from IPreviewHandler.");
+
+ HRESULT hr = pInitializeWithFile.Get()->Initialize(path, (uint)STGM.STGM_READ);
+ if (hr == HRESULT.E_NOTIMPL)
+ throw new NotImplementedException($"{nameof(IInitializeWithFile)}.Initialize() is not implemented.");
+ else if ((int)hr < 0)
+ throw new COMException($"{nameof(IInitializeWithFile)}.Initialize() failed.", (int)hr);
+
+ _initialized = true;
+
+ Debug.WriteLine($"Preview handler was successfully initialized with {nameof(IInitializeWithFile)}.");
+
+ return true;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
+
try
{
- pobj = ItemStreamHelper.IShellItemFromPath(path);
- if (pobj != IntPtr.Zero
- && InitWithItem(pobj, STGM.STGM_READ))
+ using ComPtr pShellItem = default;
+ HRESULT hr = PInvoke.SHCreateItemFromParsingName(path, null, typeof(IShellItem).GUID, out *(void**)&pShellItem);
+ if (hr.Value < 0)
+ throw new InvalidComObjectException($"SHCreateItemFromParsingName failed to get IShellItem for preview handling with the error {hr.Value:X}.");
+
+ if (!pShellItem.IsNull)
+ {
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
+ if (_initialized)
+ throw new InvalidOperationException("Preview handler is already initialized and cannot be initialized again.");
+
+ using ComPtr pInitializeWithItem = default;
+ _pPreviewHandler->QueryInterface(typeof(IInitializeWithItem).GUID, out *(void**)pInitializeWithItem.GetAddressOf());
+ if (pInitializeWithItem.IsNull)
+ throw new COMException($"{nameof(IInitializeWithItem)} could not queried from IPreviewHandler.");
+
+ hr = pInitializeWithItem.Get()->Initialize(pShellItem.Get(), (uint)STGM.STGM_READ);
+ if (hr == HRESULT.E_NOTIMPL)
+ throw new NotImplementedException($"{nameof(IInitializeWithItem)}.Initialize() is not implemented.");
+ else if ((int)hr < 0)
+ throw new COMException($"{nameof(IInitializeWithItem)}.Initialize() failed.", (int)hr);
+
+ _initialized = true;
+
+ Debug.WriteLine($"Preview handler was successfully initialized with {nameof(IInitializeWithItem)}.");
+
return true;
- if (exceptions.Count == 0)
- throw new NotSupportedException("The object cannot be initialized at all.");
+ }
+
+ if (exceptions.Count is 0)
+ throw new NotSupportedException("Preview handler could not be initialized at all.");
}
catch (Exception ex)
{
exceptions.Add(ex);
}
- finally
- {
- if (pobj != IntPtr.Zero)
- ItemStreamHelper.ReleaseObject(pobj);
- pobj = IntPtr.Zero;
- }
+
+ Debug.WriteLine($"Preview handler could not be initialized at all.");
+
throw new AggregateException(exceptions);
}
+ ///
+ /// Loads the preview data and renders the preview.
+ ///
+ public void DoPreview()
+ {
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
+ if (!_initialized)
+ // throw new InvalidOperationException("Object must be initialized before calling this method.");
+ return;
+
+ if (_shown)
+ throw new InvalidOperationException("The preview handler must not be shown to call this method.");
+
+ bool res = ResetWindow();
+
+ Debug.WriteLine($"Window of the preview handler" + (res ? "was successfully reset." : "failed to be reset."));
+
+ _pPreviewHandler->DoPreview();
+
+ _shown = true;
+
+ Debug.WriteLine($"IPreviewHandler.DoPreview was successfully done.");
+ }
+
+ ///
+ /// Unloads the preview handler and disposes this instance.
+ ///
+ public void UnloadPreview()
+ {
+ Dispose(true);
+ }
+
///
/// Calls IPreviewHandler.SetWindow.
///
public bool ResetWindow()
{
- EnsureNotDisposed();
- //EnsureInitialized();
- if (!init)
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
+ if (!_initialized)
+ // throw new InvalidOperationException("Object must be initialized before calling this method.");
return false;
- var hr = previewHandler.SetWindow(hwnd, new());
- return (int)hr >= 0;
+
+ HRESULT hr = _pPreviewHandler->SetWindow(_hWnd, new RECT());
+ return hr.Value >= 0;
}
///
@@ -409,11 +278,15 @@ public bool ResetWindow()
///
public bool ResetBounds(RECT previewerBounds)
{
- EnsureNotDisposed();
- //EnsureInitialized();
- if (!init)
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
+ //if (!_initialized)
+ // throw new InvalidOperationException("Object must be initialized before calling this method.");
+
+ if (!_initialized)
return false;
- var hr = previewHandler.SetRect(previewerBounds);
+
+ HRESULT hr = _pPreviewHandler->SetRect(previewerBounds);
return (int)hr >= 0;
}
@@ -424,8 +297,8 @@ public bool ResetBounds(RECT previewerBounds)
/// Whether the call succeeds.
public bool SetBackground(Color color)
{
- var hr = visuals?.SetBackgroundColor(ColorRefFromColor(color));
- return hr.HasValue && (int)hr.Value >= 0;
+ HRESULT hr = _previewHandlerVisuals->SetBackgroundColor(new(ConvertColorToColorRef(color)));
+ return hr.Value >= 0;
}
///
@@ -435,8 +308,8 @@ public bool SetBackground(Color color)
/// Whether the call succeeds.
public bool SetForeground(Color color)
{
- var hr = visuals?.SetTextColor(ColorRefFromColor(color));
- return hr.HasValue && (int)hr.Value >= 0;
+ HRESULT hr = _previewHandlerVisuals->SetTextColor(new(ConvertColorToColorRef(color)));
+ return hr.Value >= 0;
}
///
@@ -444,25 +317,10 @@ public bool SetForeground(Color color)
///
/// The LogFontW reference.
/// Whether the call succeeds.
- public bool SetFont(ref LOGFONT font)
+ public bool SetFont(ref LOGFONTW font)
{
- var hr = visuals?.SetFont(ref font);
- return hr.HasValue && (int)hr.Value >= 0;
- }
-
- ///
- /// Shows the preview if the object has been successfully initialized.
- ///
- public void DoPreview()
- {
- EnsureNotDisposed();
- //EnsureInitialized();
- if (!init)
- return;
- EnsureNotShown();
- ResetWindow();
- previewHandler.DoPreview();
- shown = true;
+ HRESULT hr = _previewHandlerVisuals->SetFont(font);
+ return hr.Value >= 0;
}
///
@@ -470,110 +328,125 @@ public void DoPreview()
///
public void Focus()
{
- EnsureNotDisposed();
- //EnsureInitialized();
- if (!init)
+ ObjectDisposedException.ThrowIf(_disposed, this);
+
+ if (!_initialized)
+ // throw new InvalidOperationException("Object must be initialized before calling this method.");
return;
- EnsureShown();
- previewHandler.SetFocus();
+
+ if (!_shown)
+ throw new InvalidOperationException("The preview handler must be shown to call this method.");
+
+ _pPreviewHandler->SetFocus();
}
///
/// Tells the preview handler to query focus.
///
/// The focused window.
- public IntPtr QueryFocus()
+ public nint QueryFocus()
{
- EnsureNotDisposed();
- //EnsureInitialized();
- if (!init)
- return IntPtr.Zero;
- EnsureShown();
- IntPtr result;
- var hr = previewHandler.QueryFocus(out result);
- if ((int)hr < 0)
- return IntPtr.Zero;
- return result;
- }
+ ObjectDisposedException.ThrowIf(_disposed, this);
- ///
- /// Unloads the preview and disposes the object. This method is idempotent.
- ///
- public void UnloadPreview()
- {
- Dispose(true);
- }
+ if (!_initialized)
+ // throw new InvalidOperationException("Object must be initialized before calling this method.");
+ return nint.Zero;
- void EnsureNotDisposed()
- {
- if (disposed)
- throw new ObjectDisposedException("PreviewHandler");
- }
+ if (!_shown)
+ throw new InvalidOperationException("The preview handler must be shown to call this method.");
- void EnsureInitialized()
- {
- if (!init)
- throw new InvalidOperationException("Object must be initialized before calling this method.");
+ HRESULT hr = _pPreviewHandler->QueryFocus(out HWND hWnd);
+ if (hr.Value < 0)
+ return nint.Zero;
+
+ return hWnd.Value;
}
- void EnsureNotInitialized()
+ private uint ConvertColorToColorRef(Color color)
{
- if (init)
- throw new InvalidOperationException("Object is already initialized and cannot be initialized again.");
+ return (((uint)color.B) << 16) | (((uint)color.G) << 8) | ((uint)color.R);
}
- void EnsureShown()
+ // Disposers
+
+ ~PreviewHandler()
{
- if (!shown)
- throw new InvalidOperationException("The preview handler must be shown to call this method.");
+ Dispose(false);
}
- void EnsureNotShown()
+ void IDisposable.Dispose()
{
- if (shown)
- throw new InvalidOperationException("The preview handler must not be shown to call this method.");
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
- #region IDisposable pattern
-
void Dispose(bool disposing)
{
- if (disposed)
+ if (_disposed)
return;
- disposed = true;
- init = false;
+
+ _disposed = true;
+ _initialized = false;
+
if (disposing)
{
- previewHandler.Unload();
- comSite.Dispose();
- Marshal.ReleaseComObject(previewHandler);
+ _pPreviewHandler->Unload();
+ _pPreviewHandler->Release();
}
else
{
// We're in the finalizer.
// Field previewHandler might have been finalized at this point.
// Get a new RCW.
- var phObject = Marshal.GetUniqueObjectForIUnknown(pPreviewHandler);
- var ph = phObject as IPreviewHandler;
- if (ph != null)
- ph.Unload();
- Marshal.ReleaseComObject(phObject);
+
+ //var phObject = Marshal.GetUniqueObjectForIUnknown(_pPreviewHandler);
+ //var ph = phObject as IPreviewHandler;
+ //if (ph != null)
+ // ph.Unload();
+
+ //Marshal.ReleaseComObject(phObject);
}
- Marshal.Release(pPreviewHandler);
- }
- ~PreviewHandler()
- {
- Dispose(false);
+ _pPreviewHandler->Release();
+
+ Debug.WriteLine($"Preview handler was successfully disposed.");
}
- void IDisposable.Dispose()
+ // Private class
+
+ private unsafe class CPreviewHandlerFrame : IPreviewHandlerFrame.Interface
{
- Dispose(true);
- GC.SuppressFinalize(this);
- }
+ private bool _disposed = false;
+ private readonly HWND _hWnd = default;
+
+ public CPreviewHandlerFrame(HWND frame)
+ {
+ _hWnd = frame;
+ }
+
+ public void Dispose()
+ {
+ _disposed = true;
+ }
+
+ public HRESULT GetWindowContext(PREVIEWHANDLERFRAMEINFO* pInfo)
+ {
+ pInfo->haccel = HACCEL.Null;
+ pInfo->cAccelEntries = 0u;
+
+ if (_disposed)
+ return HRESULT.E_FAIL; // Disposed already
+
+ return HRESULT.S_OK;
+ }
- #endregion
+ public HRESULT TranslateAccelerator(MSG* pMsg)
+ {
+ if (_disposed)
+ return HRESULT.E_FAIL; // Disposed already
+ return HRESULT.S_FALSE;
+ }
+ }
}
}
diff --git a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs
index 1dd27cb8ad03..46cc16c22a8e 100644
--- a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs
+++ b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs
@@ -6,18 +6,17 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Hosting;
using System.Runtime.InteropServices;
-using System.Text;
-using Vanara.PInvoke;
using Windows.Win32;
-using Windows.Win32.System.Com;
+using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Direct3D;
using Windows.Win32.Graphics.Direct3D11;
using Windows.Win32.Graphics.DirectComposition;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.Graphics.Dxgi;
+using Windows.Win32.System.Com;
+using Windows.Win32.UI.Shell;
+using Windows.Win32.UI.WindowsAndMessaging;
using WinRT;
-using static Vanara.PInvoke.ShlwApi;
-using static Vanara.PInvoke.User32;
#pragma warning disable CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates.
@@ -25,75 +24,74 @@ namespace Files.App.ViewModels.Previews
{
public sealed class ShellPreviewViewModel : BasePreviewModel
{
- private const string IPreviewHandlerIid = "{8895b1c6-b41f-4c1c-a562-0d564250836f}";
- private static readonly Guid QueryAssociationsClsid = new Guid(0xa07034fd, 0x6caa, 0x4954, 0xac, 0x3f, 0x97, 0xa2, 0x72, 0x16, 0xf9, 0x8a);
- private static readonly Guid IQueryAssociationsIid = Guid.ParseExact("c46ca590-3c3f-11d2-bee6-0000f805ca57", "d");
+ // Fields
+
+ PreviewHandler? _currentPreviewHandler;
+ ContentExternalOutputLink? _contentExternalOutputLink;
+ WNDCLASSEXW _windowClass;
+ WNDPROC _windProc = null!;
+ HWND _hWnd = HWND.Null;
+ bool _isOfficePreview = false;
- PreviewHandler? currentHandler;
- ContentExternalOutputLink? outputLink;
- WindowClass? wCls;
- HWND hwnd = HWND.NULL;
- bool isOfficePreview = false;
+ // Initializer
public ShellPreviewViewModel(ListedItem item) : base(item)
{
}
+ // Methods
+
public async override Task> LoadPreviewAndDetailsAsync()
=> [];
- public static Guid? FindPreviewHandlerFor(string extension, nint hwnd)
+ public static unsafe Guid? FindPreviewHandlerFor(string extension, nint hwnd)
{
if (string.IsNullOrEmpty(extension))
return null;
- var hr = AssocCreate(QueryAssociationsClsid, IQueryAssociationsIid, out var queryAssoc);
- if (!hr.Succeeded)
- return null;
-
try
{
- if (queryAssoc == null)
- return null;
-
- queryAssoc.Init(ASSOCF.ASSOCF_INIT_DEFAULTTOSTAR, extension, nint.Zero, hwnd);
-
- var sb = new StringBuilder(128);
- uint cch = 64;
-
- queryAssoc.GetString(ASSOCF.ASSOCF_NOTRUNCATE, ASSOCSTR.ASSOCSTR_SHELLEXTENSION, IPreviewHandlerIid, sb, ref cch);
-
- Debug.WriteLine($"Preview handler for {extension}: {sb}");
- return Guid.Parse(sb.ToString());
+ fixed (char* pszOutput = new char[1024])
+ {
+ PWSTR pwszOutput = new(pszOutput);
+ uint cchOutput = 512u;
+
+ // Try to find registered preview handler associated with specified extension name
+ var res = PInvoke.AssocQueryString(
+ ASSOCF.ASSOCF_NOTRUNCATE,
+ ASSOCSTR.ASSOCSTR_SHELLEXTENSION,
+ extension,
+ "{8895b1c6-b41f-4c1c-a562-0d564250836f}",
+ pszOutput,
+ ref cchOutput);
+
+ return Guid.Parse(pwszOutput.ToString());
+ }
}
catch
{
return null;
}
- finally
- {
- Marshal.ReleaseComObject(queryAssoc);
- }
}
public void SizeChanged(RECT size)
{
- if (hwnd != HWND.NULL)
- SetWindowPos(hwnd, HWND.HWND_TOP, size.Left, size.Top, size.Width, size.Height, SetWindowPosFlags.SWP_NOACTIVATE);
+ if (_hWnd != HWND.Null)
+ PInvoke.SetWindowPos(_hWnd, (HWND)0, size.left, size.top, size.Width, size.Height, SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE);
- currentHandler?.ResetBounds(new(0, 0, size.Width, size.Height));
+ _currentPreviewHandler?.ResetBounds(new(0, 0, size.Width, size.Height));
- if (outputLink is not null)
- outputLink.PlacementVisual.Size = new(size.Width, size.Height);
+ if (_contentExternalOutputLink is not null)
+ _contentExternalOutputLink.PlacementVisual.Size = new(size.Width, size.Height);
}
- private nint WndProc(HWND hwnd, uint msg, nint wParam, nint lParam)
+ private unsafe LRESULT WndProc(HWND hwnd, uint msg, WPARAM wParam, LPARAM lParam)
{
- if (msg == (uint)WindowMessage.WM_CREATE)
+ if (msg == 0x0001 /*WM_CREATE*/)
{
- var clsid = FindPreviewHandlerFor(Item.FileExtension, hwnd.DangerousGetHandle());
+ var clsid = FindPreviewHandlerFor(Item.FileExtension, hwnd);
- isOfficePreview = new Guid?[]
+ _isOfficePreview = new Guid?[]
{
Guid.Parse("84F66100-FF7C-4fb4-B0C0-02CD7FB668FE"), // preview handler for Word files
Guid.Parse("65235197-874B-4A07-BDC5-E65EA825B718"), // preview handler for PowerPoint files
@@ -102,43 +100,71 @@ private nint WndProc(HWND hwnd, uint msg, nint wParam, nint lParam)
try
{
- currentHandler = new PreviewHandler(clsid.Value, hwnd.DangerousGetHandle());
- currentHandler.InitWithFileWithEveryWay(Item.ItemPath);
- currentHandler.DoPreview();
+ _currentPreviewHandler = new PreviewHandler(clsid.Value, hwnd);
+ _currentPreviewHandler.Initialize(Item.ItemPath);
+ _currentPreviewHandler.DoPreview();
}
catch
{
UnloadPreview();
}
}
- else if (msg == (uint)WindowMessage.WM_DESTROY)
+ else if (msg == 0x0002 /*WM_DESTROY*/)
{
- if (currentHandler is not null)
+ if (_currentPreviewHandler is not null)
{
- currentHandler.UnloadPreview();
- currentHandler = null;
+ _currentPreviewHandler.UnloadPreview();
+ _currentPreviewHandler = null;
}
}
- return DefWindowProc(hwnd, msg, wParam, lParam);
+ return PInvoke.DefWindowProc(hwnd, msg, wParam, lParam);
}
- public void LoadPreview(UIElement presenter)
+ public unsafe void LoadPreview(UIElement presenter)
{
var parent = MainWindow.Instance.WindowHandle;
+ var hInst = PInvoke.GetModuleHandle(default(PWSTR));
+ var szClassName = $"{GetType().Name}-{Guid.NewGuid()}";
+ var szWindowName = $"Preview";
- HINSTANCE hInst = Kernel32.GetModuleHandle();
-
- wCls = new WindowClass($"{GetType().Name}{Guid.NewGuid()}", hInst, WndProc);
+ fixed (char* pszClassName = szClassName)
+ {
+ _windProc = new(WndProc);
+ var pWindProc = Marshal.GetFunctionPointerForDelegate(_windProc);
+ var pfnWndProc = (delegate* unmanaged[Stdcall])pWindProc;
- hwnd = CreateWindowEx(
- WindowStylesEx.WS_EX_LAYERED | WindowStylesEx.WS_EX_COMPOSITED,
- wCls.ClassName,
- "Preview",
- WindowStyles.WS_CHILD | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_VISIBLE,
- 0, 0, 0, 0,
- hWndParent: parent,
- hInstance: hInst);
+ _windowClass = new WNDCLASSEXW()
+ {
+ cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEXW)),
+ lpfnWndProc = pfnWndProc,
+ hInstance = hInst,
+ lpszClassName = pszClassName,
+ style = 0,
+ hIcon = default,
+ hIconSm = default,
+ hCursor = default,
+ hbrBackground = default,
+ lpszMenuName = null,
+ cbClsExtra = 0,
+ cbWndExtra = 0,
+ };
+
+ PInvoke.RegisterClassEx(_windowClass);
+
+ fixed (char* pszWindowName = szWindowName)
+ {
+ _hWnd = PInvoke.CreateWindowEx(
+ WINDOW_EX_STYLE.WS_EX_LAYERED | WINDOW_EX_STYLE.WS_EX_COMPOSITED,
+ pszClassName,
+ pszWindowName,
+ WINDOW_STYLE.WS_CHILD | WINDOW_STYLE.WS_CLIPSIBLINGS | WINDOW_STYLE.WS_VISIBLE,
+ 0, 0, 0, 0,
+ new(parent),
+ HMENU.Null,
+ hInst);
+ }
+ }
_ = ChildWindowToXaml(parent, presenter);
}
@@ -184,20 +210,20 @@ private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter)
IUnknown* pControlSurface = default;
pDCompositionDevice->CreateVisual(&pChildVisual);
- pDCompositionDevice->CreateSurfaceFromHwnd(new(hwnd.DangerousGetHandle()), &pControlSurface);
+ pDCompositionDevice->CreateSurfaceFromHwnd(new(_hWnd), &pControlSurface);
pChildVisual->SetContent(pControlSurface);
if (pChildVisual is null || pControlSurface is null)
return false;
var compositor = ElementCompositionPreview.GetElementVisual(presenter).Compositor;
- outputLink = ContentExternalOutputLink.Create(compositor);
+ _contentExternalOutputLink = ContentExternalOutputLink.Create(compositor);
- var target = outputLink.As();
+ var target = _contentExternalOutputLink.As();
target.SetRoot(pChildVisual);
- outputLink.PlacementVisual.Size = new(0, 0);
- outputLink.PlacementVisual.Scale = new(1/(float)presenter.XamlRoot.RasterizationScale);
- ElementCompositionPreview.SetElementChildVisual(presenter, outputLink.PlacementVisual);
+ _contentExternalOutputLink.PlacementVisual.Size = new(0, 0);
+ _contentExternalOutputLink.PlacementVisual.Scale = new(1 / (float)presenter.XamlRoot.RasterizationScale);
+ ElementCompositionPreview.SetElementChildVisual(presenter, _contentExternalOutputLink.PlacementVisual);
pDCompositionDevice->Commit();
@@ -213,25 +239,13 @@ private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter)
return
PInvoke.DwmSetWindowAttribute(
- new((nint)hwnd),
+ new((nint)_hWnd),
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
&dwAttrib,
(uint)Marshal.SizeOf(dwAttrib))
.Succeeded;
}
- public void UnloadPreview()
- {
- if (hwnd != HWND.NULL)
- DestroyWindow(hwnd);
-
- //outputLink?.Dispose();
- outputLink = null;
-
- if (wCls is not null)
- UnregisterClass(wCls.ClassName, Kernel32.GetModuleHandle());
- }
-
public unsafe void PointerEntered(bool onPreview)
{
if (onPreview)
@@ -239,30 +253,51 @@ public unsafe void PointerEntered(bool onPreview)
var dwAttrib = Convert.ToUInt32(false);
PInvoke.DwmSetWindowAttribute(
- new((nint)hwnd),
+ new((nint)_hWnd),
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
&dwAttrib,
(uint)Marshal.SizeOf(dwAttrib));
- if (isOfficePreview)
- Win32Helper.SetWindowLong(hwnd, WindowLongFlags.GWL_EXSTYLE, 0);
+ if (_isOfficePreview)
+ PInvoke.SetWindowLong(_hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, 0);
}
else
{
- Win32Helper.SetWindowLong(
- hwnd,
- WindowLongFlags.GWL_EXSTYLE,
- (nint)(WindowStylesEx.WS_EX_LAYERED | WindowStylesEx.WS_EX_COMPOSITED));
+ PInvoke.SetWindowLong(
+ _hWnd,
+ WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE,
+ (int)(WINDOW_EX_STYLE.WS_EX_LAYERED | WINDOW_EX_STYLE.WS_EX_COMPOSITED));
var dwAttrib = Convert.ToUInt32(true);
PInvoke.DwmSetWindowAttribute(
- new((nint)hwnd),
+ new((nint)_hWnd),
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
&dwAttrib,
(uint)Marshal.SizeOf(dwAttrib));
}
}
+
+ // Disposer
+
+ public void UnloadPreview()
+ {
+ if (_hWnd != HWND.Null)
+ PInvoke.DestroyWindow(_hWnd);
+
+ try
+ {
+ var target = _contentExternalOutputLink.As();
+ Marshal.ReleaseComObject(target);
+ _contentExternalOutputLink?.Dispose();
+ }
+ finally
+ {
+ _contentExternalOutputLink = null;
+ }
+
+ PInvoke.UnregisterClass(_windowClass.lpszClassName, PInvoke.GetModuleHandle(default(PWSTR)));
+ }
}
}