66using Microsoft . UI . Xaml ;
77using Microsoft . UI . Xaml . Hosting ;
88using System . Runtime . InteropServices ;
9- using System . Text ;
10- using Vanara . PInvoke ;
119using Windows . Win32 ;
12- using Windows . Win32 . System . Com ;
10+ using Windows . Win32 . Foundation ;
1311using Windows . Win32 . Graphics . Direct3D ;
1412using Windows . Win32 . Graphics . Direct3D11 ;
1513using Windows . Win32 . Graphics . DirectComposition ;
1614using Windows . Win32 . Graphics . Dwm ;
1715using Windows . Win32 . Graphics . Dxgi ;
16+ using Windows . Win32 . System . Com ;
17+ using Windows . Win32 . UI . Shell ;
1818using Windows . Win32 . UI . WindowsAndMessaging ;
1919using 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
2723namespace 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