diff --git a/src/Files.App.CsWin32/NativeMethods.txt b/src/Files.App.CsWin32/NativeMethods.txt
index 9d7c337d7c4d..8746ea6d2b3a 100644
--- a/src/Files.App.CsWin32/NativeMethods.txt
+++ b/src/Files.App.CsWin32/NativeMethods.txt
@@ -133,3 +133,17 @@ IFileOperation
IShellItem2
PSGetPropertyKeyFromName
ShellExecuteEx
+E_FAIL
+S_OK
+S_FALSE
+MSG
+E_NOTIMPL
+LOGFONTW
+AssocCreate
+IQueryAssociations
+UnregisterClass
+SetWindowLong
+GetModuleHandle
+RegisterClassEx
+CREATESTRUCTW
+AssocQueryString
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/PreviewHandler.cs b/src/Files.App/Utils/Shell/PreviewHandler.cs
index 6cb5f4001798..06160b4c2a21 100644
--- a/src/Files.App/Utils/Shell/PreviewHandler.cs
+++ b/src/Files.App/Utils/Shell/PreviewHandler.cs
@@ -1,9 +1,13 @@
-using System;
-using System.Collections.Generic;
+// Copyright (c) 2024 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
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.UI.WindowsAndMessaging;
namespace Files.App.Utils.Shell
{
@@ -12,12 +16,16 @@ namespace Files.App.Utils.Shell
///
public sealed class PreviewHandler : IDisposable
{
+ // Fields
+
+ private static readonly Guid IPreviewHandlerIid = Guid.ParseExact("8895b1c6-b41f-4c1c-a562-0d564250836f", "d");
+
#region IPreviewHandlerFrame support
[StructLayout(LayoutKind.Sequential)]
public struct PreviewHandlerFrameInfo
{
- public IntPtr AcceleratorTableHandle;
+ public nint AcceleratorTableHandle;
public uint AcceleratorEntryCount;
}
@@ -49,7 +57,7 @@ public void Dispose()
public HRESULT GetWindowContext(out PreviewHandlerFrameInfo pinfo)
{
- pinfo.AcceleratorTableHandle = IntPtr.Zero;
+ pinfo.AcceleratorTableHandle = nint.Zero;
pinfo.AcceleratorEntryCount = 0;
if (disposed)
return HRESULT.E_FAIL;
@@ -72,7 +80,7 @@ public HRESULT TranslateAccelerator(ref MSG pmsg)
interface IPreviewHandler
{
[PreserveSig]
- HRESULT SetWindow(IntPtr hwnd, ref RECT prc);
+ HRESULT SetWindow(nint hwnd, ref RECT prc);
[PreserveSig]
HRESULT SetRect(ref RECT prc);
[PreserveSig]
@@ -82,7 +90,7 @@ interface IPreviewHandler
[PreserveSig]
HRESULT SetFocus();
[PreserveSig]
- HRESULT QueryFocus(out IntPtr phwnd);
+ HRESULT QueryFocus(out nint phwnd);
// TranslateAccelerator is not used here.
}
@@ -92,7 +100,7 @@ interface IPreviewHandlerVisuals
[PreserveSig]
HRESULT SetBackgroundColor(uint color);
[PreserveSig]
- HRESULT SetFont(ref LOGFONT plf);
+ HRESULT SetFont(ref LOGFONTW plf);
[PreserveSig]
HRESULT SetTextColor(uint color);
}
@@ -119,7 +127,9 @@ interface IObjectWithSite
nint hwnd;
IPreviewHandler previewHandler;
IPreviewHandlerVisuals visuals;
- IntPtr pPreviewHandler;
+ nint pPreviewHandler;
+
+ // Initializer
public PreviewHandler(Guid clsid, nint frame)
{
@@ -128,6 +138,7 @@ public PreviewHandler(Guid clsid, nint frame)
shown = false;
comSite = new PreviewHandlerFrame(frame);
hwnd = frame;
+
try
{
SetupHandler(clsid);
@@ -138,52 +149,64 @@ public PreviewHandler(Guid clsid, nint frame)
if (previewHandler != null)
Marshal.ReleaseComObject(previewHandler);
previewHandler = null;
- if (pPreviewHandler != IntPtr.Zero)
+ if (pPreviewHandler != nint.Zero)
Marshal.Release(pPreviewHandler);
- pPreviewHandler = IntPtr.Zero;
+ pPreviewHandler = nint.Zero;
comSite.Dispose();
comSite = null;
throw;
}
}
- static readonly Guid IPreviewHandlerIid = Guid.ParseExact("8895b1c6-b41f-4c1c-a562-0d564250836f", "d");
+ // Methods
- void SetupHandler(Guid clsid)
+ unsafe void SetupHandler(Guid clsid)
{
- IntPtr pph;
+ nint 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.
+
+ // Important: manually 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);
+ HRESULT hr = PInvoke.CoCreateInstance(
+ clsid,
+ null,
+ CLSCTX.CLSCTX_LOCAL_SERVER,
+ ref iid,
+ out void* previewHandlerPtr);
+
+ object previewHandlerObject = *(IPreviewHandler*)previewHandlerPtr;
+
// 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);
+ // hr = CoCreateInstance(ref clsid, nint.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;
+
+ pPreviewHandler = new(previewHandlerPtr);
+ previewHandler = (IPreviewHandler)previewHandlerObject;
if (previewHandler == null)
{
Marshal.ReleaseComObject(previewHandlerObject);
throw new COMException(cannotCreate);
}
+
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;
}
@@ -200,7 +223,7 @@ interface IInitializeWithStream
interface IInitializeWithStreamNative
{
[PreserveSig]
- HRESULT Initialize(IntPtr psi, STGM grfMode);
+ HRESULT Initialize(nint psi, STGM grfMode);
}
static readonly Guid IInitializeWithStreamIid = Guid.ParseExact("b824b49d-22ac-4161-ac8a-9916e8fa3f7f", "d");
@@ -218,7 +241,7 @@ interface IInitializeWithFile
interface IInitializeWithItem
{
[PreserveSig]
- HRESULT Initialize(IntPtr psi, STGM grfMode);
+ HRESULT Initialize(nint psi, STGM grfMode);
}
static readonly Guid IInitializeWithItemIid = Guid.ParseExact("7f73be3f-fb79-493c-a6c7-7ee14e245841", "d");
@@ -237,15 +260,19 @@ 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;
}
@@ -257,21 +284,26 @@ public bool InitWithStream(IStream stream, STGM mode)
/// The native pointer to the IStream interface.
/// The storage mode.
/// True or false, see InitWithStream(IStream, STGM).
- public bool InitWithStream(IntPtr pStream, STGM mode)
+ public bool InitWithStream(nint 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;
}
@@ -283,21 +315,26 @@ public bool InitWithStream(IntPtr pStream, STGM mode)
/// The native pointer to the IShellItem interface.
/// The storage mode.
/// True or false, see InitWithStream(IStream, STGM).
- public bool InitWithItem(IntPtr psi, STGM mode)
+ public bool InitWithItem(nint 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;
}
@@ -313,17 +350,22 @@ 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;
}
@@ -335,7 +377,8 @@ public bool InitWithFile(string path, STGM mode)
public bool InitWithFileWithEveryWay(string path)
{
var exceptions = new List();
- var pobj = IntPtr.Zero;
+ var pobj = nint.Zero;
+
// Why should we try IStream first?
// Because that gives us the best security.
// If we initialize with string or IShellItem,
@@ -345,7 +388,7 @@ public bool InitWithFileWithEveryWay(string path)
try
{
pobj = ItemStreamHelper.IStreamFromPath(path);
- if (pobj != IntPtr.Zero
+ if (pobj != nint.Zero
&& InitWithStream(pobj, STGM.STGM_READ))
return true;
}
@@ -355,10 +398,12 @@ public bool InitWithFileWithEveryWay(string path)
}
finally
{
- if (pobj != IntPtr.Zero)
+ if (pobj != nint.Zero)
ItemStreamHelper.ReleaseObject(pobj);
- pobj = IntPtr.Zero;
+
+ pobj = nint.Zero;
}
+
// Next try file because that could save us some P/Invokes.
try
{
@@ -369,12 +414,14 @@ public bool InitWithFileWithEveryWay(string path)
{
exceptions.Add(ex);
}
+
try
{
pobj = ItemStreamHelper.IShellItemFromPath(path);
- if (pobj != IntPtr.Zero
+ if (pobj != nint.Zero
&& InitWithItem(pobj, STGM.STGM_READ))
return true;
+
if (exceptions.Count == 0)
throw new NotSupportedException("The object cannot be initialized at all.");
}
@@ -384,10 +431,12 @@ public bool InitWithFileWithEveryWay(string path)
}
finally
{
- if (pobj != IntPtr.Zero)
+ if (pobj != nint.Zero)
ItemStreamHelper.ReleaseObject(pobj);
- pobj = IntPtr.Zero;
+
+ pobj = nint.Zero;
}
+
throw new AggregateException(exceptions);
}
@@ -397,9 +446,12 @@ public bool InitWithFileWithEveryWay(string path)
public bool ResetWindow()
{
EnsureNotDisposed();
+
//EnsureInitialized();
+
if (!init)
return false;
+
var hr = previewHandler.SetWindow(hwnd, new());
return (int)hr >= 0;
}
@@ -410,9 +462,12 @@ public bool ResetWindow()
public bool ResetBounds(RECT previewerBounds)
{
EnsureNotDisposed();
+
//EnsureInitialized();
+
if (!init)
return false;
+
var hr = previewHandler.SetRect(previewerBounds);
return (int)hr >= 0;
}
@@ -444,7 +499,7 @@ 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;
@@ -456,12 +511,18 @@ public bool SetFont(ref LOGFONT font)
public void DoPreview()
{
EnsureNotDisposed();
+
//EnsureInitialized();
+
if (!init)
return;
+
EnsureNotShown();
+
ResetWindow();
+
previewHandler.DoPreview();
+
shown = true;
}
@@ -471,9 +532,12 @@ public void DoPreview()
public void Focus()
{
EnsureNotDisposed();
+
//EnsureInitialized();
+
if (!init)
return;
+
EnsureShown();
previewHandler.SetFocus();
}
@@ -482,17 +546,23 @@ public void Focus()
/// Tells the preview handler to query focus.
///
/// The focused window.
- public IntPtr QueryFocus()
+ public nint QueryFocus()
{
EnsureNotDisposed();
+
//EnsureInitialized();
+
if (!init)
- return IntPtr.Zero;
+ return nint.Zero;
+
EnsureShown();
- IntPtr result;
+
+ nint result;
+
var hr = previewHandler.QueryFocus(out result);
if ((int)hr < 0)
- return IntPtr.Zero;
+ return nint.Zero;
+
return result;
}
@@ -534,7 +604,7 @@ void EnsureNotShown()
throw new InvalidOperationException("The preview handler must not be shown to call this method.");
}
- #region IDisposable pattern
+ // Dispose
void Dispose(bool disposing)
{
@@ -572,8 +642,5 @@ void IDisposable.Dispose()
Dispose(true);
GC.SuppressFinalize(this);
}
-
- #endregion
-
}
}
diff --git a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs
index 1dd27cb8ad03..3062e47e9669 100644
--- a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs
+++ b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs
@@ -6,18 +6,20 @@
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.UI.Shell;
+using Windows.Win32.UI.WindowsAndMessaging;
using WinRT;
-using static Vanara.PInvoke.ShlwApi;
-using static Vanara.PInvoke.User32;
+
+// Description: Feature is for evaluation purposes only and is subject to change or removal in future updates.
+// Justification: We have to use ContentExternalOutputLink for shell previews.
+#pragma warning disable CS8305
#pragma warning disable CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates.
@@ -25,75 +27,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 +103,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.Value);
+ _currentPreviewHandler.InitWithFileWithEveryWay(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);
}
@@ -175,29 +204,27 @@ private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter)
if (pD3D11Device is null)
return false;
- IDXGIDevice* pDXGIDevice = (IDXGIDevice*)pD3D11Device;
- if (PInvoke.DCompositionCreateDevice(pDXGIDevice, typeof(IDCompositionDevice).GUID, out var compositionDevicePtr).Failed)
+ IDXGIDevice dxgiDevice = (IDXGIDevice)d3d11Device;
+ if (PInvoke.DCompositionCreateDevice(dxgiDevice, typeof(IDCompositionDevice).GUID, out var compDevicePtr).Failed)
return false;
- var pDCompositionDevice = (IDCompositionDevice*)compositionDevicePtr;
- IDCompositionVisual* pChildVisual = default;
- IUnknown* pControlSurface = default;
+ IDCompositionDevice compDevice = (IDCompositionDevice)compDevicePtr;
pDCompositionDevice->CreateVisual(&pChildVisual);
- pDCompositionDevice->CreateSurfaceFromHwnd(new(hwnd.DangerousGetHandle()), &pControlSurface);
+ pDCompositionDevice->CreateSurfaceFromHwnd(_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 +240,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 +254,43 @@ 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);
+
+ //outputLink?.Dispose();
+ _contentExternalOutputLink = null;
+
+ PInvoke.UnregisterClass(_windowClass.lpszClassName, PInvoke.GetModuleHandle(default(PWSTR)));
+ }
}
}