Skip to content

Commit b7f4136

Browse files
committed
Initial commit
1 parent 2aa96c3 commit b7f4136

File tree

3 files changed

+125
-96
lines changed

3 files changed

+125
-96
lines changed

src/Files.App.CsWin32/NativeMethods.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ HWND
2828
LRESULT
2929
WPARAM
3030
LPARAM
31-
WM_LBUTTONUP
32-
WM_RBUTTONUP
33-
WM_DESTROY
31+
WM_*
3432
SetForegroundWindow
3533
GetForegroundWindow
3634
GetCurrentThreadId
@@ -173,3 +171,4 @@ FSCTL_LOCK_VOLUME
173171
FILE_FILE_COMPRESSION
174172
WM_WINDOWPOSCHANGING
175173
WINDOWPOS
174+
UnregisterClass

src/Files.App.CsWin32/Windows.Win32.Extras.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
using System.Runtime.InteropServices;
5+
using System.Runtime.InteropServices.Marshalling;
56
using Windows.Win32.Foundation;
67
using Windows.Win32.UI.WindowsAndMessaging;
78

@@ -37,4 +38,14 @@ public static unsafe nint SetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nInd
3738
: _SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
3839
}
3940
}
41+
42+
namespace Extras
43+
{
44+
[GeneratedComInterface, Guid("EACDD04C-117E-4E17-88F4-D1B12B0E3D89"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
45+
public partial interface IDCompositionTarget
46+
{
47+
[PreserveSig]
48+
int SetRoot(nint visual);
49+
}
50+
}
4051
}

src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs

Lines changed: 112 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -6,105 +6,97 @@
66
using Microsoft.UI.Xaml;
77
using Microsoft.UI.Xaml.Hosting;
88
using System.Runtime.InteropServices;
9-
using System.Text;
10-
using Vanara.PInvoke;
119
using Windows.Win32;
12-
using Windows.Win32.System.Com;
10+
using Windows.Win32.Foundation;
1311
using Windows.Win32.Graphics.Direct3D;
1412
using Windows.Win32.Graphics.Direct3D11;
1513
using Windows.Win32.Graphics.DirectComposition;
1614
using Windows.Win32.Graphics.Dwm;
1715
using Windows.Win32.Graphics.Dxgi;
16+
using Windows.Win32.System.Com;
17+
using Windows.Win32.UI.Shell;
1818
using Windows.Win32.UI.WindowsAndMessaging;
1919
using WinRT;
20-
using static Vanara.PInvoke.ShlwApi;
21-
using static Vanara.PInvoke.User32;
22-
using System.Runtime.InteropServices.Marshalling;
23-
2420

2521
#pragma warning disable CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates.
2622

2723
namespace Files.App.ViewModels.Previews
2824
{
2925
public sealed partial class ShellPreviewViewModel : BasePreviewModel
3026
{
31-
private const string IPreviewHandlerIid = "{8895b1c6-b41f-4c1c-a562-0d564250836f}";
32-
private static readonly Guid QueryAssociationsClsid = new Guid(0xa07034fd, 0x6caa, 0x4954, 0xac, 0x3f, 0x97, 0xa2, 0x72, 0x16, 0xf9, 0x8a);
33-
private static readonly Guid IQueryAssociationsIid = Guid.ParseExact("c46ca590-3c3f-11d2-bee6-0000f805ca57", "d");
34-
35-
PreviewHandler? currentHandler;
36-
ContentExternalOutputLink? outputLink;
37-
WindowClass? wCls;
38-
HWND hwnd = HWND.NULL;
39-
bool isOfficePreview = false;
27+
// Fields
28+
29+
ContentExternalOutputLink? _contentExternalOutputLink;
30+
PreviewHandler? _previewHandler;
31+
WNDCLASSEXW _windowClass;
32+
WNDPROC _windProc = null!;
33+
HWND _hWnd = HWND.Null;
34+
bool _isOfficePreview = false;
4035
ComPtr<IDCompositionVisual> pChildVisual = default;
4136

42-
[GeneratedComInterface, Guid("EACDD04C-117E-4E17-88F4-D1B12B0E3D89"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
43-
public partial interface IDCompositionTarget
44-
{
45-
[PreserveSig]
46-
int SetRoot(nint visual);
47-
}
37+
// Constructor
4838

4939
public ShellPreviewViewModel(ListedItem item) : base(item)
5040
{
5141
}
5242

53-
public async override Task<List<FileProperty>> LoadPreviewAndDetailsAsync()
54-
=> [];
43+
// Methods
44+
45+
public override Task<List<FileProperty>> LoadPreviewAndDetailsAsync()
46+
=> Task.FromResult<List<FileProperty>>([]);
5547

56-
public static Guid? FindPreviewHandlerFor(string extension, nint hwnd)
48+
public static unsafe Guid? FindPreviewHandlerFor(string extension, nint hwnd)
5749
{
5850
if (string.IsNullOrEmpty(extension))
5951
return null;
6052

61-
var hr = AssocCreate(QueryAssociationsClsid, IQueryAssociationsIid, out var queryAssoc);
62-
if (!hr.Succeeded)
63-
return null;
64-
6553
try
6654
{
67-
if (queryAssoc == null)
68-
return null;
69-
70-
queryAssoc.Init(ASSOCF.ASSOCF_INIT_DEFAULTTOSTAR, extension, nint.Zero, hwnd);
71-
72-
var sb = new StringBuilder(128);
73-
uint cch = 64;
74-
75-
queryAssoc.GetString(ASSOCF.ASSOCF_NOTRUNCATE, ASSOCSTR.ASSOCSTR_SHELLEXTENSION, IPreviewHandlerIid, sb, ref cch);
76-
77-
Debug.WriteLine($"Preview handler for {extension}: {sb}");
78-
return Guid.Parse(sb.ToString());
55+
fixed (char* pszAssoc = extension,
56+
pszExtra = "{8895b1c6-b41f-4c1c-a562-0d564250836f}",
57+
pszOutput = new char[1024])
58+
{
59+
PWSTR pwszAssoc = new(pszAssoc);
60+
PWSTR pwszExtra = new(pszExtra);
61+
PWSTR pwszOutput = new(pszOutput);
62+
uint cchOutput = 2024;
63+
64+
// Try to find registered preview handler associated with specified extension name
65+
var res = PInvoke.AssocQueryString(
66+
ASSOCF.ASSOCF_NOTRUNCATE,
67+
ASSOCSTR.ASSOCSTR_SHELLEXTENSION,
68+
pwszAssoc,
69+
pwszExtra,
70+
pwszOutput,
71+
&cchOutput);
72+
73+
return Guid.Parse(pwszOutput.ToString());
74+
}
7975
}
8076
catch
8177
{
8278
return null;
8379
}
84-
finally
85-
{
86-
Marshal.ReleaseComObject(queryAssoc);
87-
}
8880
}
8981

9082
public void SizeChanged(RECT size)
9183
{
92-
if (hwnd != HWND.NULL)
93-
SetWindowPos(hwnd, HWND.HWND_TOP, size.Left, size.Top, size.Width, size.Height, SetWindowPosFlags.SWP_NOACTIVATE);
84+
if (_hWnd != HWND.Null)
85+
PInvoke.SetWindowPos(_hWnd, new(0), size.left, size.top, size.Width, size.Height, SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE);
9486

95-
currentHandler?.ResetBounds(new(0, 0, size.Width, size.Height));
87+
_previewHandler?.ResetBounds(new(0, 0, size.Width, size.Height));
9688

97-
if (outputLink is not null)
98-
outputLink.PlacementVisual.Size = new(size.Width, size.Height);
89+
if (_contentExternalOutputLink is not null)
90+
_contentExternalOutputLink.PlacementVisual.Size = new(size.Width, size.Height);
9991
}
10092

101-
private nint WndProc(HWND hwnd, uint msg, nint wParam, nint lParam)
93+
private unsafe LRESULT WndProc(HWND hwnd, uint msg, WPARAM wParam, LPARAM lParam)
10294
{
103-
if (msg == (uint)WindowMessage.WM_CREATE)
95+
if (msg is PInvoke.WM_CREATE)
10496
{
105-
var clsid = FindPreviewHandlerFor(Item.FileExtension, hwnd.DangerousGetHandle());
97+
var clsid = FindPreviewHandlerFor(Item.FileExtension, hwnd);
10698

107-
isOfficePreview = new Guid?[]
99+
_isOfficePreview = new Guid?[]
108100
{
109101
Guid.Parse("84F66100-FF7C-4fb4-B0C0-02CD7FB668FE"), // preview handler for Word files
110102
Guid.Parse("65235197-874B-4A07-BDC5-E65EA825B718"), // preview handler for PowerPoint files
@@ -113,43 +105,71 @@ private nint WndProc(HWND hwnd, uint msg, nint wParam, nint lParam)
113105

114106
try
115107
{
116-
currentHandler = new PreviewHandler(clsid.Value, hwnd.DangerousGetHandle());
117-
currentHandler.InitWithFileWithEveryWay(Item.ItemPath);
118-
currentHandler.DoPreview();
108+
_previewHandler = new PreviewHandler(clsid!.Value, hwnd);
109+
_previewHandler.InitWithFileWithEveryWay(Item.ItemPath);
110+
_previewHandler.DoPreview();
119111
}
120112
catch
121113
{
122114
UnloadPreview();
123115
}
124116
}
125-
else if (msg == (uint)WindowMessage.WM_DESTROY)
117+
else if (msg is PInvoke.WM_DESTROY)
126118
{
127-
if (currentHandler is not null)
119+
if (_previewHandler is not null)
128120
{
129-
currentHandler.Dispose();
130-
currentHandler = null;
121+
_previewHandler.Dispose();
122+
_previewHandler = null;
131123
}
132124
}
133125

134-
return DefWindowProc(hwnd, msg, wParam, lParam);
126+
return PInvoke.DefWindowProc(hwnd, msg, wParam, lParam);
135127
}
136128

137-
public void LoadPreview(UIElement presenter)
129+
public unsafe void LoadPreview(UIElement presenter)
138130
{
139131
var parent = MainWindow.Instance.WindowHandle;
132+
var hInst = PInvoke.GetModuleHandle(default(PWSTR));
133+
var szClassName = $"{GetType().Name}-{Guid.NewGuid()}";
134+
var szWindowName = $"Preview";
140135

141-
HINSTANCE hInst = Kernel32.GetModuleHandle();
142-
143-
wCls = new WindowClass($"{GetType().Name}{Guid.NewGuid()}", hInst, WndProc);
136+
fixed (char* pszClassName = szClassName)
137+
{
138+
_windProc = new(WndProc);
139+
var pWindProc = Marshal.GetFunctionPointerForDelegate(_windProc);
140+
var pfnWndProc = (delegate* unmanaged[Stdcall]<HWND, uint, WPARAM, LPARAM, LRESULT>)pWindProc;
144141

145-
hwnd = CreateWindowEx(
146-
WindowStylesEx.WS_EX_LAYERED | WindowStylesEx.WS_EX_COMPOSITED,
147-
wCls.ClassName,
148-
"Preview",
149-
WindowStyles.WS_CHILD | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_VISIBLE,
150-
0, 0, 0, 0,
151-
hWndParent: parent,
152-
hInstance: hInst);
142+
_windowClass = new WNDCLASSEXW()
143+
{
144+
cbSize = (uint)sizeof(WNDCLASSEXW),
145+
lpfnWndProc = pfnWndProc,
146+
hInstance = hInst,
147+
lpszClassName = pszClassName,
148+
style = 0,
149+
hIcon = default,
150+
hIconSm = default,
151+
hCursor = default,
152+
hbrBackground = default,
153+
lpszMenuName = null,
154+
cbClsExtra = 0,
155+
cbWndExtra = 0,
156+
};
157+
158+
PInvoke.RegisterClassEx(_windowClass);
159+
160+
fixed (char* pszWindowName = szWindowName)
161+
{
162+
_hWnd = PInvoke.CreateWindowEx(
163+
WINDOW_EX_STYLE.WS_EX_LAYERED | WINDOW_EX_STYLE.WS_EX_COMPOSITED,
164+
pszClassName,
165+
pszWindowName,
166+
WINDOW_STYLE.WS_CHILD | WINDOW_STYLE.WS_CLIPSIBLINGS | WINDOW_STYLE.WS_VISIBLE,
167+
0, 0, 0, 0,
168+
new(parent),
169+
HMENU.Null,
170+
hInst);
171+
}
172+
}
153173

154174
_ = ChildWindowToXaml(parent, presenter);
155175
}
@@ -198,21 +218,21 @@ private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter)
198218

199219
// Create the visual
200220
hr = pDCompositionDevice.Get()->CreateVisual(pChildVisual.GetAddressOf());
201-
hr = pDCompositionDevice.Get()->CreateSurfaceFromHwnd(new(hwnd.DangerousGetHandle()), pControlSurface.GetAddressOf());
221+
hr = pDCompositionDevice.Get()->CreateSurfaceFromHwnd(_hWnd, pControlSurface.GetAddressOf());
202222
hr = pChildVisual.Get()->SetContent(pControlSurface.Get());
203223
if (pChildVisual.IsNull || pControlSurface.IsNull)
204224
return false;
205225

206226
// Get the compositor and set the visual on it
207227
var compositor = ElementCompositionPreview.GetElementVisual(presenter).Compositor;
208-
outputLink = ContentExternalOutputLink.Create(compositor);
228+
_contentExternalOutputLink = ContentExternalOutputLink.Create(compositor);
209229

210-
var target = outputLink.As<IDCompositionTarget>();
230+
var target = _contentExternalOutputLink.As<Windows.Win32.Extras.IDCompositionTarget>();
211231
target.SetRoot((nint)pChildVisual.Get());
212232

213-
outputLink.PlacementVisual.Size = new(0, 0);
214-
outputLink.PlacementVisual.Scale = new(1 / (float)presenter.XamlRoot.RasterizationScale);
215-
ElementCompositionPreview.SetElementChildVisual(presenter, outputLink.PlacementVisual);
233+
_contentExternalOutputLink.PlacementVisual.Size = new(0, 0);
234+
_contentExternalOutputLink.PlacementVisual.Scale = new(1 / (float)presenter.XamlRoot.RasterizationScale);
235+
ElementCompositionPreview.SetElementChildVisual(presenter, _contentExternalOutputLink.PlacementVisual);
216236

217237
// Commit the all pending DComp commands
218238
pDCompositionDevice.Get()->Commit();
@@ -221,7 +241,7 @@ private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter)
221241

222242
return
223243
PInvoke.DwmSetWindowAttribute(
224-
new((nint)hwnd),
244+
new((nint)_hWnd),
225245
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
226246
&dwAttrib,
227247
(uint)Marshal.SizeOf(dwAttrib))
@@ -230,16 +250,15 @@ private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter)
230250

231251
public void UnloadPreview()
232252
{
233-
if (hwnd != HWND.NULL)
234-
DestroyWindow(hwnd);
253+
if (_hWnd != HWND.Null)
254+
PInvoke.DestroyWindow(_hWnd);
235255

236-
outputLink?.Dispose();
237-
outputLink = null;
256+
_contentExternalOutputLink?.Dispose();
257+
_contentExternalOutputLink = null;
238258

239259
pChildVisual.Dispose();
240260

241-
if (wCls is not null)
242-
UnregisterClass(wCls.ClassName, Kernel32.GetModuleHandle());
261+
PInvoke.UnregisterClass(_windowClass.lpszClassName, PInvoke.GetModuleHandle(default(PWSTR)));
243262
}
244263

245264
public unsafe void PointerEntered(bool onPreview)
@@ -249,25 +268,25 @@ public unsafe void PointerEntered(bool onPreview)
249268
var dwAttrib = Convert.ToUInt32(false);
250269

251270
PInvoke.DwmSetWindowAttribute(
252-
new((nint)hwnd),
271+
new((nint)_hWnd),
253272
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
254273
&dwAttrib,
255274
(uint)Marshal.SizeOf(dwAttrib));
256275

257-
if (isOfficePreview)
258-
PInvoke.SetWindowLongPtr(new((nint)hwnd), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, 0);
276+
if (_isOfficePreview)
277+
PInvoke.SetWindowLongPtr(new((nint)_hWnd), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, 0);
259278
}
260279
else
261280
{
262281
PInvoke.SetWindowLongPtr(
263-
new((nint)hwnd),
282+
new((nint)_hWnd),
264283
WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE,
265284
(nint)(WINDOW_EX_STYLE.WS_EX_LAYERED | WINDOW_EX_STYLE.WS_EX_COMPOSITED));
266285

267286
var dwAttrib = Convert.ToUInt32(true);
268287

269288
PInvoke.DwmSetWindowAttribute(
270-
new((nint)hwnd),
289+
new((nint)_hWnd),
271290
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
272291
&dwAttrib,
273292
(uint)Marshal.SizeOf(dwAttrib));
@@ -276,4 +295,4 @@ public unsafe void PointerEntered(bool onPreview)
276295
}
277296
}
278297

279-
#pragma warning restore CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates.
298+
#pragma warning restore CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates.

0 commit comments

Comments
 (0)