From c03de86d020fb53135cfa750ceae757635185164 Mon Sep 17 00:00:00 2001 From: Tsanie Lily Date: Tue, 28 Dec 2021 16:53:45 +0800 Subject: [PATCH] add preview handler --- PhotoThumbnail/PhotoThumbnail.csproj | 5 +- .../Interop/Common/ShellNativeEnums.cs | 377 ++++++++++++++++++ .../Interop/Common/ShellNativeStructs.cs | 283 ++++++++++--- .../Interop/HandlerNativeMethods.cs | 235 ++++++++++- .../ManagedInitializationInterfaces.cs | 42 ++ .../PreviewHandlers/PreviewHandler.cs | 377 ++++++++++++++++++ .../PreviewHandlerAttribute.cs | 48 +++ .../PreviewHandlers/WinFormsPreviewHandler.cs | 119 ++++++ .../PreviewHandlers/WpfPreviewHandler.cs | 186 +++++++++ .../Resources/LocalizedMessages.cs | 10 + ShellExtensions/ShellExtensions.projitems | 8 +- .../ManagedInitializationInterfaces.cs | 4 +- .../ThumbnailProviders/ThumbnailProvider.cs | 4 +- .../ThumbnailProviderAttribute.cs | 6 +- 14 files changed, 1613 insertions(+), 91 deletions(-) create mode 100644 ShellExtensions/Interop/Common/ShellNativeEnums.cs create mode 100644 ShellExtensions/PreviewHandlers/ManagedInitializationInterfaces.cs create mode 100644 ShellExtensions/PreviewHandlers/PreviewHandler.cs create mode 100644 ShellExtensions/PreviewHandlers/PreviewHandlerAttribute.cs create mode 100644 ShellExtensions/PreviewHandlers/WinFormsPreviewHandler.cs create mode 100644 ShellExtensions/PreviewHandlers/WpfPreviewHandler.cs diff --git a/PhotoThumbnail/PhotoThumbnail.csproj b/PhotoThumbnail/PhotoThumbnail.csproj index e2e1374..20a8b46 100644 --- a/PhotoThumbnail/PhotoThumbnail.csproj +++ b/PhotoThumbnail/PhotoThumbnail.csproj @@ -18,7 +18,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;THUMBNAIL prompt 4 true @@ -27,7 +27,7 @@ pdbonly true bin\Release\ - TRACE + TRACE;THUMBNAIL prompt 4 true @@ -46,7 +46,6 @@ - diff --git a/ShellExtensions/Interop/Common/ShellNativeEnums.cs b/ShellExtensions/Interop/Common/ShellNativeEnums.cs new file mode 100644 index 0000000..697c93c --- /dev/null +++ b/ShellExtensions/Interop/Common/ShellNativeEnums.cs @@ -0,0 +1,377 @@ +using System; + +namespace ShellExtensions.Interop.Common +{ + /// + /// The STGM constants are flags that indicate + /// conditions for creating and deleting the object and access modes + /// for the object. + /// + /// You can combine these flags, but you can only choose one flag + /// from each group of related flags. Typically one flag from each + /// of the access and sharing groups must be specified for all + /// functions and methods which use these constants. + /// + [Flags] + public enum AccessModes + { + /// + /// Indicates that, in direct mode, each change to a storage + /// or stream element is written as it occurs. + /// + Direct = 0x00000000, + + /// + /// Indicates that, in transacted mode, changes are buffered + /// and written only if an explicit commit operation is called. + /// + Transacted = 0x00010000, + + /// + /// Provides a faster implementation of a compound file + /// in a limited, but frequently used, case. + /// + Simple = 0x08000000, + + /// + /// Indicates that the object is read-only, + /// meaning that modifications cannot be made. + /// + Read = 0x00000000, + + /// + /// Enables you to save changes to the object, + /// but does not permit access to its data. + /// + Write = 0x00000001, + + /// + /// Enables access and modification of object data. + /// + ReadWrite = 0x00000002, + + /// + /// Specifies that subsequent openings of the object are + /// not denied read or write access. + /// + ShareDenyNone = 0x00000040, + + /// + /// Prevents others from subsequently opening the object in Read mode. + /// + ShareDenyRead = 0x00000030, + + /// + /// Prevents others from subsequently opening the object + /// for Write or ReadWrite access. + /// + ShareDenyWrite = 0x00000020, + + /// + /// Prevents others from subsequently opening the object in any mode. + /// + ShareExclusive = 0x00000010, + + /// + /// Opens the storage object with exclusive access to the most + /// recently committed version. + /// + Priority = 0x00040000, + + /// + /// Indicates that the underlying file is to be automatically destroyed when the root + /// storage object is released. This feature is most useful for creating temporary files. + /// + DeleteOnRelease = 0x04000000, + + /// + /// Indicates that, in transacted mode, a temporary scratch file is usually used + /// to save modifications until the Commit method is called. + /// Specifying NoScratch permits the unused portion of the original file + /// to be used as work space instead of creating a new file for that purpose. + /// + NoScratch = 0x00100000, + + /// + /// Indicates that an existing storage object + /// or stream should be removed before the new object replaces it. + /// + Create = 0x00001000, + + /// + /// Creates the new object while preserving existing data in a stream named "Contents". + /// + Convert = 0x00020000, + + /// + /// Causes the create operation to fail if an existing object with the specified name exists. + /// + FailIfThere = 0x00000000, + + /// + /// This flag is used when opening a storage object with Transacted + /// and without ShareExclusive or ShareDenyWrite. + /// In this case, specifying NoSnapshot prevents the system-provided + /// implementation from creating a snapshot copy of the file. + /// Instead, changes to the file are written to the end of the file. + /// + NoSnapshot = 0x00200000, + + /// + /// Supports direct mode for single-writer, multireader file operations. + /// + DirectSingleWriterMultipleReader = 0x00400000 + } + +#if PREVIEW_HANDLER + /// + /// HRESULT Wrapper + /// + public enum HResult + { + /// + /// S_OK + /// + Ok = 0x0000, + + /// + /// S_FALSE + /// + False = 0x0001, + + /// + /// E_INVALIDARG + /// + InvalidArguments = unchecked((int)0x80070057), + + /// + /// E_OUTOFMEMORY + /// + OutOfMemory = unchecked((int)0x8007000E), + + /// + /// E_NOINTERFACE + /// + NoInterface = unchecked((int)0x80004002), + + /// + /// E_FAIL + /// + Fail = unchecked((int)0x80004005), + + /// + /// E_ELEMENTNOTFOUND + /// + ElementNotFound = unchecked((int)0x80070490), + + /// + /// TYPE_E_ELEMENTNOTFOUND + /// + TypeElementNotFound = unchecked((int)0x8002802B), + + /// + /// NO_OBJECT + /// + NoObject = unchecked((int)0x800401E5), + + /// + /// Win32 Error code: ERROR_CANCELLED + /// + Win32ErrorCanceled = 1223, + + /// + /// ERROR_CANCELLED + /// + Canceled = unchecked((int)0x800704C7), + + /// + /// The requested resource is in use + /// + ResourceInUse = unchecked((int)0x800700AA), + + /// + /// The requested resources is read-only. + /// + AccessDenied = unchecked((int)0x80030005) + } + + [Flags] + internal enum WindowStyles + { + /// + /// The window has a thin-line border. + /// + Border = 0x00800000, + + /// + /// The window has a title bar (includes the WS_BORDER style). + /// + Caption = 0x00C00000, + + /// + /// The window is a child window. + /// A window with this style cannot have a menu bar. + /// This style cannot be used with the WS_POPUP style. + /// + Child = 0x40000000, + + /// + /// Same as the WS_CHILD style. + /// + ChildWindow = 0x40000000, + + /// + /// Excludes the area occupied by child windows when drawing occurs within the parent window. + /// This style is used when creating the parent window. + /// + ClipChildren = 0x02000000, + + /// + /// Clips child windows relative to each other; + /// that is, when a particular child window receives a WM_PAINT message, + /// the WS_CLIPSIBLINGS style clips all other overlapping child windows out of the region of the child window to be updated. + /// If WS_CLIPSIBLINGS is not specified and child windows overlap, it is possible, + /// when drawing within the client area of a child window, to draw within the client area of a neighboring child window. + /// + ClipSiblings = 0x04000000, + + /// + /// The window is initially disabled. A disabled window cannot receive input from the user. + /// To change this after a window has been created, use the EnableWindow function. + /// + Disabled = 0x08000000, + + /// + /// The window has a border of a style typically used with dialog boxes. + /// A window with this style cannot have a title bar. + /// + DialogFrame = 0x0040000, + + /// + /// The window is the first control of a group of controls. + /// The group consists of this first control and all controls defined after it, up to the next control with the WS_GROUP style. + /// The first control in each group usually has the WS_TABSTOP style so that the user can move from group to group. + /// The user can subsequently change the keyboard focus from one control in the group to the next control + /// in the group by using the direction keys. + /// + /// You can turn this style on and off to change dialog box navigation. + /// To change this style after a window has been created, use the SetWindowLong function. + /// + Group = 0x00020000, + + /// + /// The window has a horizontal scroll bar. + /// + HorizontalScroll = 0x00100000, + + /// + /// The window is initially minimized. + /// Same as the WS_MINIMIZE style. + /// + Iconic = 0x20000000, + + /// + /// The window is initially maximized. + /// + Maximize = 0x01000000, + + /// + /// The window has a maximize button. + /// Cannot be combined with the WS_EX_CONTEXTHELP style. + /// The WS_SYSMENU style must also be specifie + /// + MaximizeBox = 0x00010000, + + /// + /// The window is initially minimized. + /// Same as the WS_ICONIC style. + /// + Minimize = 0x20000000, + + /// + /// The window has a minimize button. + /// Cannot be combined with the WS_EX_CONTEXTHELP style. + /// The WS_SYSMENU style must also be specified. + /// + MinimizeBox = 0x00020000, + + /// + /// The window is an overlapped window. + /// An overlapped window has a title bar and a border. + /// Same as the WS_TILED style. + /// + Overlapped = 0x00000000, + + /// + /// The windows is a pop-up window. + /// This style cannot be used with the WS_CHILD style. + /// + Popup = unchecked((int)0x80000000), + + /// + /// The window has a sizing border. + /// Same as the WS_THICKFRAME style. + /// + SizeBox = 0x00040000, + + /// + /// The window has a window menu on its title bar. + /// The WS_CAPTION style must also be specified. + /// + SystemMenu = 0x00080000, + + /// + /// The window is a control that can receive the keyboard focus when the user presses the TAB key. + /// Pressing the TAB key changes the keyboard focus to the next control with the WS_TABSTOP style. + /// + /// You can turn this style on and off to change dialog box navigation. + /// To change this style after a window has been created, use the SetWindowLong function. + /// For user-created windows and modeless dialogs to work with tab stops, + /// alter the message loop to call the IsDialogMessage function. + /// + Tabstop = 0x00010000, + + /// + /// The window has a sizing border. + /// Same as the WS_SIZEBOX style. + /// + ThickFrame = 0x00040000, + + /// + /// The window is an overlapped window. + /// An overlapped window has a title bar and a border. + /// Same as the WS_OVERLAPPED style. + /// + Tiled = 0x00000000, + + /// + /// The window is initially visible. + /// + /// This style can be turned on and off by using the ShowWindow or SetWindowPos function. + /// + Visible = 0x10000000, + + /// + /// The window has a vertical scroll bar. + /// + VerticalScroll = 0x00200000, + + /// + /// The window is an overlapped window. + /// Same as the WS_OVERLAPPEDWINDOW style. + /// + TiledWindowMask = Overlapped | Caption | SystemMenu | ThickFrame | MinimizeBox | MaximizeBox, + + /// + /// The window is a pop-up window. + /// The WS_CAPTION and WS_POPUPWINDOW styles must be combined to make the window menu visible. + /// + PopupWindowMask = Popup | Border | SystemMenu, + + /// + /// The window is an overlapped window. Same as the WS_TILEDWINDOW style. + /// + OverlappedWindowMask = Overlapped | Caption | SystemMenu | ThickFrame | MinimizeBox | MaximizeBox, + } +#endif +} diff --git a/ShellExtensions/Interop/Common/ShellNativeStructs.cs b/ShellExtensions/Interop/Common/ShellNativeStructs.cs index 69d4a69..cec460a 100644 --- a/ShellExtensions/Interop/Common/ShellNativeStructs.cs +++ b/ShellExtensions/Interop/Common/ShellNativeStructs.cs @@ -1,125 +1,280 @@ -using System; +#if PREVIEW_HANDLER +using System; +using System.Runtime.InteropServices; namespace ShellExtensions.Interop.Common { + /// - /// The STGM constants are flags that indicate - /// conditions for creating and deleting the object and access modes - /// for the object. - /// - /// You can combine these flags, but you can only choose one flag - /// from each group of related flags. Typically one flag from each - /// of the access and sharing groups must be specified for all - /// functions and methods which use these constants. + /// Wraps the native Windows MSG structure. /// - [Flags] - public enum AccessModes + public struct Message { /// - /// Indicates that, in direct mode, each change to a storage - /// or stream element is written as it occurs. + /// Gets the window handle /// - Direct = 0x00000000, + public IntPtr WindowHandle { get; } /// - /// Indicates that, in transacted mode, changes are buffered - /// and written only if an explicit commit operation is called. + /// Gets the window message /// - Transacted = 0x00010000, + public uint Msg { get; } /// - /// Provides a faster implementation of a compound file - /// in a limited, but frequently used, case. + /// Gets the WParam /// - Simple = 0x08000000, + public IntPtr WParam { get; } /// - /// Indicates that the object is read-only, - /// meaning that modifications cannot be made. + /// Gets the LParam /// - Read = 0x00000000, + public IntPtr LParam { get; } /// - /// Enables you to save changes to the object, - /// but does not permit access to its data. + /// Gets the time /// - Write = 0x00000001, + public int Time { get; } /// - /// Enables access and modification of object data. + /// Gets the point /// - ReadWrite = 0x00000002, + public NativePoint Point { get; } /// - /// Specifies that subsequent openings of the object are - /// not denied read or write access. + /// Creates a new instance of the Message struct /// - ShareDenyNone = 0x00000040, + /// Window handle + /// Message + /// WParam + /// LParam + /// Time + /// Point + internal Message(IntPtr windowHandle, uint msg, IntPtr wparam, IntPtr lparam, int time, NativePoint point) + : this() + { + WindowHandle = windowHandle; + Msg = msg; + WParam = wparam; + LParam = lparam; + Time = time; + Point = point; + } /// - /// Prevents others from subsequently opening the object in Read mode. + /// Determines if two messages are equal. /// - ShareDenyRead = 0x00000030, + /// First message + /// Second message + /// True if first and second message are equal; false otherwise. + public static bool operator ==(Message first, Message second) + { + return first.WindowHandle == second.WindowHandle + && first.Msg == second.Msg + && first.WParam == second.WParam + && first.LParam == second.LParam + && first.Time == second.Time + && first.Point == second.Point; + } /// - /// Prevents others from subsequently opening the object - /// for Write or ReadWrite access. + /// Determines if two messages are not equal. /// - ShareDenyWrite = 0x00000020, + /// First message + /// Second message + /// True if first and second message are not equal; false otherwise. + public static bool operator !=(Message first, Message second) + { + return !(first == second); + } /// - /// Prevents others from subsequently opening the object in any mode. + /// Determines if this message is equal to another. /// - ShareExclusive = 0x00000010, + /// Another message + /// True if this message is equal argument; false otherwise. + public override bool Equals(object obj) + { + return obj != null && obj is Message message && this == message; + } /// - /// Opens the storage object with exclusive access to the most - /// recently committed version. + /// Gets a hash code for the message. /// - Priority = 0x00040000, + /// Hash code for this message. + public override int GetHashCode() + { + int hash = WindowHandle.GetHashCode(); + hash = hash * 31 + Msg.GetHashCode(); + hash = hash * 31 + WParam.GetHashCode(); + hash = hash * 31 + LParam.GetHashCode(); + hash = hash * 31 + Time.GetHashCode(); + hash = hash * 31 + Point.GetHashCode(); + return hash; + } + } + + /// + /// A wrapper for the native POINT structure. + /// + [StructLayout(LayoutKind.Sequential)] + public struct NativePoint + { + /// + /// Initialize the NativePoint + /// + /// The x coordinate of the point. + /// The y coordinate of the point. + public NativePoint(int x, int y) + : this() + { + X = x; + Y = y; + } /// - /// Indicates that the underlying file is to be automatically destroyed when the root - /// storage object is released. This feature is most useful for creating temporary files. + /// The X coordinate of the point /// - DeleteOnRelease = 0x04000000, + public int X { get; set; } /// - /// Indicates that, in transacted mode, a temporary scratch file is usually used - /// to save modifications until the Commit method is called. - /// Specifying NoScratch permits the unused portion of the original file - /// to be used as work space instead of creating a new file for that purpose. + /// The Y coordinate of the point /// - NoScratch = 0x00100000, + public int Y { get; set; } /// - /// Indicates that an existing storage object - /// or stream should be removed before the new object replaces it. + /// Determines if two NativePoints are equal. /// - Create = 0x00001000, + /// First NativePoint + /// Second NativePoint + /// True if first NativePoint is equal to the second; false otherwise. + public static bool operator ==(NativePoint first, NativePoint second) + { + return first.X == second.X + && first.Y == second.Y; + } /// - /// Creates the new object while preserving existing data in a stream named "Contents". + /// Determines if two NativePoints are not equal. /// - Convert = 0x00020000, + /// First NativePoint + /// Second NativePoint + /// True if first NativePoint is not equal to the second; false otherwise. + public static bool operator !=(NativePoint first, NativePoint second) + { + return !(first == second); + } /// - /// Causes the create operation to fail if an existing object with the specified name exists. + /// Determines if this NativePoint is equal to another. /// - FailIfThere = 0x00000000, + /// Another NativePoint to compare + /// True if this NativePoint is equal obj; false otherwise. + public override bool Equals(object obj) + { + return obj != null && obj is NativePoint point && this == point; + } /// - /// This flag is used when opening a storage object with Transacted - /// and without ShareExclusive or ShareDenyWrite. - /// In this case, specifying NoSnapshot prevents the system-provided - /// implementation from creating a snapshot copy of the file. - /// Instead, changes to the file are written to the end of the file. + /// Gets a hash code for the NativePoint. /// - NoSnapshot = 0x00200000, + /// Hash code for the NativePoint + public override int GetHashCode() + { + int hash = X.GetHashCode(); + hash = hash * 31 + Y.GetHashCode(); + return hash; + } + } + + /// + /// A wrapper for a RECT struct + /// + [StructLayout(LayoutKind.Sequential)] + public struct NativeRect + { + /// + /// Position of left edge + /// + public int Left { get; set; } /// - /// Supports direct mode for single-writer, multireader file operations. + /// Position of top edge /// - DirectSingleWriterMultipleReader = 0x00400000 + public int Top { get; set; } + + /// + /// Position of right edge + /// + public int Right { get; set; } + + /// + /// Position of bottom edge + /// + public int Bottom { get; set; } + + /// + /// Creates a new NativeRect initialized with supplied values. + /// + /// Position of left edge + /// Position of top edge + /// Position of right edge + /// Position of bottom edge + public NativeRect(int left, int top, int right, int bottom) + : this() + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + /// + /// Determines if two NativeRects are equal. + /// + /// First NativeRect + /// Second NativeRect + /// True if first NativeRect is equal to second; false otherwise. + public static bool operator ==(NativeRect first, NativeRect second) + { + return first.Left == second.Left + && first.Top == second.Top + && first.Right == second.Right + && first.Bottom == second.Bottom; + } + + /// + /// Determines if two NativeRects are not equal + /// + /// First NativeRect + /// Second NativeRect + /// True if first is not equal to second; false otherwise. + public static bool operator !=(NativeRect first, NativeRect second) + { + return !(first == second); + } + + /// + /// Determines if the NativeRect is equal to another Rect. + /// + /// Another NativeRect to compare + /// True if this NativeRect is equal to the one provided; false otherwise. + public override bool Equals(object obj) + { + return obj != null && obj is NativeRect rect && this == rect; + } + + /// + /// Creates a hash code for the NativeRect + /// + /// Returns hash code for this NativeRect + public override int GetHashCode() + { + int hash = Left.GetHashCode(); + hash = hash * 31 + Top.GetHashCode(); + hash = hash * 31 + Right.GetHashCode(); + hash = hash * 31 + Bottom.GetHashCode(); + return hash; + } } } +#endif diff --git a/ShellExtensions/Interop/HandlerNativeMethods.cs b/ShellExtensions/Interop/HandlerNativeMethods.cs index 0b74c70..937265a 100644 --- a/ShellExtensions/Interop/HandlerNativeMethods.cs +++ b/ShellExtensions/Interop/HandlerNativeMethods.cs @@ -7,7 +7,29 @@ namespace ShellExtensions.Interop { internal static class HandlerNativeMethods { +#if PREVIEW_HANDLER + [DllImport("user32.dll")] + internal static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); + + [DllImport("user32.dll")] + internal static extern IntPtr GetFocus(); + + [DllImport("user32.dll")] + internal static extern void SetWindowPos( + IntPtr hWnd, + IntPtr hWndInsertAfter, + int x, + int y, + int cx, + int cy, + SetWindowPositionOptions flags); + + internal static readonly Guid IPreviewHandlerGuid = new Guid("8895b1c6-b41f-4c1c-a562-0d564250836f"); +#endif + +#if THUMBNAIL internal static readonly Guid IThumbnailProviderGuid = new Guid("e357fccd-a995-4576-b01f-234630154e96"); +#endif internal static readonly Guid IInitializeWithFileGuid = new Guid("b7d14566-0509-4cce-a71f-0a554233bd9b"); internal static readonly Guid IInitializeWithStreamGuid = new Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"); @@ -16,25 +38,6 @@ namespace ShellExtensions.Interop internal static readonly Guid IMarshalGuid = new Guid("00000003-0000-0000-C000-000000000046"); } - #region Interfaces - - /// - /// ComVisible interface for native IThumbnailProvider - /// - [ComImport] - [Guid("e357fccd-a995-4576-b01f-234630154e96")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IThumbnailProvider - { - /// - /// Gets a pointer to a bitmap to display as a thumbnail - /// - /// - /// - /// - void GetThumbnail(uint squareLength, [Out] out IntPtr bitmapHandle, [Out] out uint bitmapType); - } - /// /// Provides means by which to initialize with a file. /// @@ -67,6 +70,26 @@ namespace ShellExtensions.Interop void Initialize(IStream stream, AccessModes fileMode); } +#if THUMBNAIL + #region Interfaces + + /// + /// ComVisible interface for native IThumbnailProvider + /// + [ComImport] + [Guid("e357fccd-a995-4576-b01f-234630154e96")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IThumbnailProvider + { + /// + /// Gets a pointer to a bitmap to display as a thumbnail + /// + /// + /// + /// + void GetThumbnail(uint squareLength, [Out] out IntPtr bitmapHandle, [Out] out uint bitmapType); + } + #endregion // @@ -89,4 +112,178 @@ namespace ShellExtensions.Interop /// HasAlphaChannel = 2, } +#endif + +#if PREVIEW_HANDLER + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("fc4801a3-2ba9-11cf-a229-00aa003d7352")] + interface IObjectWithSite + { + void SetSite([In, MarshalAs(UnmanagedType.IUnknown)] object pUnkSite); + void GetSite(ref Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppvSite); + } + + [ComImport] + [Guid("00000114-0000-0000-C000-000000000046")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IOleWindow + { + void GetWindow(out IntPtr phwnd); + void ContextSensitiveHelp([MarshalAs(UnmanagedType.Bool)] bool fEnterMode); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("8895b1c6-b41f-4c1c-a562-0d564250836f")] + interface IPreviewHandler + { + void SetWindow(IntPtr hwnd, ref NativeRect rect); + void SetRect(ref NativeRect rect); + void DoPreview(); + void Unload(); + void SetFocus(); + void QueryFocus(out IntPtr phwnd); + [PreserveSig] + HResult TranslateAccelerator(ref Message pmsg); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("fec87aaf-35f9-447a-adb7-20234491401a")] + interface IPreviewHandlerFrame + { + void GetWindowContext(IntPtr pinfo); + [PreserveSig] + HResult TranslateAccelerator(ref Message pmsg); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("8327b13c-b63f-4b24-9b8a-d010dcc3f599")] + interface IPreviewHandlerVisuals + { + void SetBackgroundColor(NativeColorRef color); + void SetFont(ref LogFont plf); + void SetTextColor(NativeColorRef color); + } + + #region Structs + + /// + /// Class for marshaling to native LogFont struct + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class LogFont + { + /// + /// Font height + /// + public int Height { get; set; } + + /// + /// Font width + /// + public int Width { get; set; } + + /// + /// Font escapement + /// + public int Escapement { get; set; } + + /// + /// Font orientation + /// + public int Orientation { get; set; } + + /// + /// Font weight + /// + public int Weight { get; set; } + + /// + /// Font italic + /// + public byte Italic { get; set; } + + /// + /// Font underline + /// + public byte Underline { get; set; } + + /// + /// Font strikeout + /// + public byte Strikeout { get; set; } + + /// + /// Font character set + /// + public byte CharacterSet { get; set; } + + /// + /// Font out precision + /// + public byte OutPrecision { get; set; } + + /// + /// Font clip precision + /// + public byte ClipPrecision { get; set; } + + /// + /// Font quality + /// + public byte Quality { get; set; } + + /// + /// Font pitch and family + /// + public byte PitchAndFamily { get; set; } + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + private string faceName = string.Empty; + + /// + /// Font face name + /// + public string FaceName { get => faceName; set => faceName = value; } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NativeColorRef + { + public uint Dword { get; set; } + } + + #endregion + + [Flags] + internal enum SetWindowPositionOptions + { + AsyncWindowPos = 0x4000, + DeferErase = 0x2000, + DrawFrame = FrameChanged, + FrameChanged = 0x0020, // The frame changed: send WM_NCCALCSIZE + HideWindow = 0x0080, + NoActivate = 0x0010, + CoCopyBits = 0x0100, + NoMove = 0x0002, + NoOwnerZOrder = 0x0200, // Don't do owner Z ordering + NoRedraw = 0x0008, + NoResposition = NoOwnerZOrder, + NoSendChanging = 0x0400, // Don't send WM_WINDOWPOSCHANGING + NoSize = 0x0001, + NoZOrder = 0x0004, + ShowWindow = 0x0040 + } + + internal enum SetWindowPositionInsertAfter + { + NoTopMost = -2, + TopMost = -1, + Top = 0, + Bottom = 1 + } +#endif } diff --git a/ShellExtensions/PreviewHandlers/ManagedInitializationInterfaces.cs b/ShellExtensions/PreviewHandlers/ManagedInitializationInterfaces.cs new file mode 100644 index 0000000..b7b86bc --- /dev/null +++ b/ShellExtensions/PreviewHandlers/ManagedInitializationInterfaces.cs @@ -0,0 +1,42 @@ +#if PREVIEW_HANDLER +using System.IO; + +namespace ShellExtensions +{ + /// + /// This interface exposes the function for initializing the + /// Preview Handler with a . + /// This interface can be used in conjunction with the other intialization interfaces, + /// but only 1 will be accessed according to the priorities preset by the Windows Shell: + /// + /// + /// + /// + public interface IPreviewFromStream + { + /// + /// Provides the to the item from which a preview should be created. + /// + /// Stream to the previewed file, this stream is only available in the scope of this method. + void Load(Stream stream); + } + + /// + /// This interface exposes the function for initializing the + /// Preview Handler with a . + /// This interface can be used in conjunction with the other intialization interfaces, + /// but only 1 will be accessed according to the priorities preset by the Windows Shell: + /// + /// + /// + /// + public interface IPreviewFromFile + { + /// + /// Provides the to the item from which a preview should be created. + /// + /// File information to the previewed file. + void Load(FileInfo info); + } +} +#endif \ No newline at end of file diff --git a/ShellExtensions/PreviewHandlers/PreviewHandler.cs b/ShellExtensions/PreviewHandlers/PreviewHandler.cs new file mode 100644 index 0000000..9e87656 --- /dev/null +++ b/ShellExtensions/PreviewHandlers/PreviewHandler.cs @@ -0,0 +1,377 @@ +#if PREVIEW_HANDLER +using Microsoft.Win32; +using ShellExtensions.Interop; +using ShellExtensions.Interop.Common; +using ShellExtensions.Resources; +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace ShellExtensions +{ + /// + /// This is the base class for all preview handlers and provides their basic functionality. + /// To create a custom preview handler a class must derive from this, use the , + /// and implement 1 or more of the following interfaces: + /// , + /// , + /// . + /// + public abstract class PreviewHandler : ICustomQueryInterface, IPreviewHandler, IPreviewHandlerVisuals, IOleWindow, IObjectWithSite, IInitializeWithStream, IInitializeWithFile + { + private bool _isPreviewShowing; + private IntPtr _parentHwnd; + private IPreviewHandlerFrame _frame; + + /// + /// Gets whether the preview is currently showing + /// + public bool IsPreviewShowing => _isPreviewShowing; + + /// + /// Called immediately before the preview is to be shown. + /// + protected virtual void Initialize() { } + + /// + /// Called when the preview is no longer shown. + /// + protected virtual void Uninitialize() { } + +#region Required functions - Abstract functions + + /// + /// This should return the window handle to be displayed in the Preview. + /// + protected abstract IntPtr Handle { get; } + + /// + /// Called to update the bounds and position of the preview control + /// + /// + protected abstract void UpdateBounds(NativeRect bounds); + + /// + /// Called when an exception occurs during the initialization of the control + /// + /// + protected abstract void HandleInitializeException(Exception caughtException); + + /// + /// Called when the preview control obtains focus. + /// + protected abstract void SetFocus(); + + /// + /// Called when a request is received to set or change the background color according to the user's preferences. + /// + /// An int representing the ARGB color + protected abstract void SetBackground(int argb); + + /// + /// Called when a request is received to set or change the foreground color according to the user's preferences. + /// + /// An int representing the ARGB color + protected abstract void SetForeground(int argb); + + /// + /// Called to set the font of the preview control according to the user's preferences. + /// + /// + protected abstract void SetFont(LogFont font); + + /// + /// Called to set the parent of the preview control. + /// + /// + protected abstract void SetParentHandle(IntPtr handle); + +#endregion + +#region IPreviewHandler Members + + void IPreviewHandler.SetWindow(IntPtr hwnd, ref NativeRect rect) + { + _parentHwnd = hwnd; + UpdateBounds(rect); + SetParentHandle(_parentHwnd); + } + + void IPreviewHandler.SetRect(ref NativeRect rect) + { + UpdateBounds(rect); + } + + void IPreviewHandler.DoPreview() + { + _isPreviewShowing = true; + try + { + Initialize(); + } + catch (Exception exc) + { + HandleInitializeException(exc); + } + } + + void IPreviewHandler.Unload() + { + Uninitialize(); + _isPreviewShowing = false; + } + + void IPreviewHandler.SetFocus() + { + SetFocus(); + } + + void IPreviewHandler.QueryFocus(out IntPtr phwnd) + { + phwnd = HandlerNativeMethods.GetFocus(); + } + + HResult IPreviewHandler.TranslateAccelerator(ref Message pmsg) + { + return _frame != null ? _frame.TranslateAccelerator(ref pmsg) : HResult.False; + } +#endregion + +#region IPreviewHandlerVisuals Members + + void IPreviewHandlerVisuals.SetBackgroundColor(NativeColorRef color) + { + SetBackground((int)color.Dword); + } + + void IPreviewHandlerVisuals.SetTextColor(NativeColorRef color) + { + SetForeground((int)color.Dword); + } + + void IPreviewHandlerVisuals.SetFont(ref LogFont plf) + { + SetFont(plf); + } + +#endregion + +#region IOleWindow Members + + void IOleWindow.GetWindow(out IntPtr phwnd) + { + phwnd = Handle; + } + + void IOleWindow.ContextSensitiveHelp(bool fEnterMode) + { + // Preview handlers don't support context sensitive help. (As far as I know.) + throw new NotImplementedException(); + } + +#endregion + +#region IObjectWithSite Members + + void IObjectWithSite.SetSite(object pUnkSite) + { + _frame = pUnkSite as IPreviewHandlerFrame; + } + + void IObjectWithSite.GetSite(ref Guid riid, out object ppvSite) + { + ppvSite = _frame; + } + +#endregion + +#region IInitializeWithStream Members + + void IInitializeWithStream.Initialize(System.Runtime.InteropServices.ComTypes.IStream stream, AccessModes fileMode) + { + IPreviewFromStream preview = this as IPreviewFromStream; + if (preview == null) + { + throw new InvalidOperationException( + string.Format(System.Globalization.CultureInfo.InvariantCulture, + LocalizedMessages.PreviewHandlerUnsupportedInterfaceCalled, + nameof(IPreviewFromStream))); + } + using (var storageStream = new StorageStream(stream, fileMode != AccessModes.ReadWrite)) + { + preview.Load(storageStream); + } + } + +#endregion + +#region IInitializeWithFile Members + + void IInitializeWithFile.Initialize(string filePath, AccessModes fileMode) + { + IPreviewFromFile preview = this as IPreviewFromFile; + if (preview == null) + { + throw new InvalidOperationException( + string.Format(System.Globalization.CultureInfo.InvariantCulture, + LocalizedMessages.PreviewHandlerUnsupportedInterfaceCalled, + nameof(IPreviewFromFile))); + } + preview.Load(new FileInfo(filePath)); + } + +#endregion + +#region ComRegistration + + /// + /// Called when the assembly is registered via RegAsm. + /// + /// Type to register. + [ComRegisterFunction] + private static void Register(Type registerType) + { + if (registerType != null && registerType.IsSubclassOf(typeof(PreviewHandler))) + { + object[] attrs = registerType.GetCustomAttributes(typeof(PreviewHandlerAttribute), true); + if (attrs != null && attrs.Length == 1) + { + PreviewHandlerAttribute attr = attrs[0] as PreviewHandlerAttribute; + ThrowIfNotValid(registerType); + RegisterPreviewHandler(registerType.GUID, attr); + } + else + { + throw new NotSupportedException( + string.Format(System.Globalization.CultureInfo.InvariantCulture, + LocalizedMessages.PreviewHandlerInvalidAttributes, registerType.Name)); + } + } + } + + /// + /// Called when the assembly is Unregistered via RegAsm. + /// + /// Type to unregister + [ComUnregisterFunction] + private static void Unregister(Type registerType) + { + if (registerType != null && registerType.IsSubclassOf(typeof(PreviewHandler))) + { + object[] attrs = registerType.GetCustomAttributes(typeof(PreviewHandlerAttribute), true); + if (attrs != null && attrs.Length == 1) + { + PreviewHandlerAttribute attr = attrs[0] as PreviewHandlerAttribute; + UnregisterPreviewHandler(registerType.GUID, attr); + } + } + } + + private static void RegisterPreviewHandler(Guid previewerGuid, PreviewHandlerAttribute attribute) + { + string guid = previewerGuid.ToString("B"); + // Create a new prevhost AppID so that this always runs in its own isolated process + using (RegistryKey appIdsKey = Registry.ClassesRoot.OpenSubKey("AppID", true)) + using (RegistryKey appIdKey = appIdsKey.CreateSubKey(attribute.AppId)) + { + appIdKey.SetValue("DllSurrogate", @"%SystemRoot%\system32\prevhost.exe", RegistryValueKind.ExpandString); + } + + // Add preview handler to preview handler list + using (RegistryKey handlersKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", true)) + { + handlersKey.SetValue(guid, attribute.Name, RegistryValueKind.String); + } + + // Modify preview handler registration + using (RegistryKey clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID")) + using (RegistryKey idKey = clsidKey.OpenSubKey(guid, true)) + { + idKey.SetValue("DisplayName", attribute.Name, RegistryValueKind.String); + idKey.SetValue("AppID", attribute.AppId, RegistryValueKind.String); + idKey.SetValue("DisableLowILProcessIsolation", attribute.DisableLowILProcessIsolation ? 1 : 0, RegistryValueKind.DWord); + + using (RegistryKey inproc = idKey.OpenSubKey("InprocServer32", true)) + { + inproc.SetValue("ThreadingModel", "Apartment", RegistryValueKind.String); + } + } + + foreach (string extension in attribute.Extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + Trace.WriteLine("Registering extension '" + extension + "' with previewer '" + guid + "'"); + + // Set preview handler for specific extension + using (RegistryKey extensionKey = Registry.ClassesRoot.CreateSubKey(extension)) + using (RegistryKey shellexKey = extensionKey.CreateSubKey("shellex")) + using (RegistryKey previewKey = shellexKey.CreateSubKey(HandlerNativeMethods.IPreviewHandlerGuid.ToString("B"))) + { + previewKey.SetValue(null, guid, RegistryValueKind.String); + } + } + } + + private static void UnregisterPreviewHandler(Guid previewerGuid, PreviewHandlerAttribute attribute) + { + string guid = previewerGuid.ToString("B"); + foreach (string extension in attribute.Extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + Trace.WriteLine("Unregistering extension '" + extension + "' with previewer '" + guid + "'"); + using (RegistryKey shellexKey = Registry.ClassesRoot.OpenSubKey(extension + "\\shellex", true)) + { + shellexKey.DeleteSubKey(HandlerNativeMethods.IPreviewHandlerGuid.ToString(), false); + } + } + + using (RegistryKey appIdsKey = Registry.ClassesRoot.OpenSubKey("AppID", true)) + { + appIdsKey.DeleteSubKey(attribute.AppId, false); + } + + using (RegistryKey classesKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", true)) + { + classesKey.DeleteValue(guid, false); + } + } + + private static void ThrowIfNotValid(Type type) + { + var interfaces = type.GetInterfaces(); + if (!interfaces.Any(x => x is IPreviewFromStream || x is IPreviewFromFile)) + { + throw new NotImplementedException( + string.Format(System.Globalization.CultureInfo.InvariantCulture, + LocalizedMessages.PreviewHandlerInterfaceNotImplemented, + type.Name)); + } + } + +#endregion + +#region ICustomQueryInterface Members + + CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) + { + ppv = IntPtr.Zero; + // Forces COM to not use the managed (free threaded) marshaler + if (iid == HandlerNativeMethods.IMarshalGuid) + { + return CustomQueryInterfaceResult.Failed; + } + + if ((iid == HandlerNativeMethods.IInitializeWithStreamGuid && !(this is IPreviewFromStream)) || + (iid == HandlerNativeMethods.IInitializeWithFileGuid && !(this is IPreviewFromFile))) + { + return CustomQueryInterfaceResult.Failed; + } + + return CustomQueryInterfaceResult.NotHandled; + } + + +#endregion + } +} +#endif \ No newline at end of file diff --git a/ShellExtensions/PreviewHandlers/PreviewHandlerAttribute.cs b/ShellExtensions/PreviewHandlers/PreviewHandlerAttribute.cs new file mode 100644 index 0000000..e01dacf --- /dev/null +++ b/ShellExtensions/PreviewHandlers/PreviewHandlerAttribute.cs @@ -0,0 +1,48 @@ +#if PREVIEW_HANDLER +using System; + +namespace ShellExtensions +{ + /// + /// This class attribute is applied to a Preview Handler to specify registration parameters. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class PreviewHandlerAttribute : Attribute + { + /// + /// Creates a new instance of the attribute. + /// + /// Name of the Handler + /// Semi-colon-separated list of file extensions supported by the handler. + /// A unique guid used for process isolation. + public PreviewHandlerAttribute(string name, string extensions, string appId) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + Extensions = extensions ?? throw new ArgumentNullException(nameof(extensions)); + AppId = appId ?? throw new ArgumentNullException(nameof(appId)); + DisableLowILProcessIsolation = false; + } + + /// + /// Gets the name of the handler. + /// + public string Name { get; private set; } + + /// + /// Gets the semi-colon-separated list of extensions supported by the preview handler. + /// + public string Extensions { get; private set; } + + /// + /// Gets the AppId associated with the handler for use with the surrogate host process. + /// + public string AppId { get; private set; } + + /// + /// Disables low integrity-level process isolation. + /// This should be avoided as it could be a security risk. + /// + public bool DisableLowILProcessIsolation { get; set; } + } +} +#endif \ No newline at end of file diff --git a/ShellExtensions/PreviewHandlers/WinFormsPreviewHandler.cs b/ShellExtensions/PreviewHandlers/WinFormsPreviewHandler.cs new file mode 100644 index 0000000..ee1627c --- /dev/null +++ b/ShellExtensions/PreviewHandlers/WinFormsPreviewHandler.cs @@ -0,0 +1,119 @@ +#if PREVIEW_HANDLER +using ShellExtensions.Interop; +using ShellExtensions.Interop.Common; +using ShellExtensions.Resources; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace ShellExtensions +{ + /// + /// This is the base class for all WinForms-based preview handlers and provides their basic functionality. + /// To create a custom preview handler that contains a WinForms user control, + /// a class must derive from this, use the , + /// and implement 1 or more of the following interfaces: + /// , + /// , + /// . + /// + public abstract class WinFormsPreviewHandler : PreviewHandler, IDisposable + { + /// + /// This control must be populated by the deriving class before the preview is shown. + /// + public UserControl Control { get; protected set; } + + protected void ThrowIfNoControl() + { + if (Control == null) + { + throw new InvalidOperationException(LocalizedMessages.PreviewHandlerControlNotInitialized); + } + } + + /// + /// Called when an exception is thrown during itialization of the preview control. + /// + /// + protected override void HandleInitializeException(Exception caughtException) + { + if (caughtException == null) { throw new ArgumentNullException(nameof(caughtException)); } + + Control = new UserControl(); + Control.Controls.Add(new TextBox + { + ReadOnly = true, + Multiline = true, + Dock = DockStyle.Fill, + Text = caughtException.ToString(), + BackColor = Color.OrangeRed + }); + } + + protected override void UpdateBounds(NativeRect bounds) + { + Control.Bounds = Rectangle.FromLTRB(bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); + Control.Visible = true; + } + + protected override void SetFocus() + { + Control.Focus(); + } + + protected override void SetBackground(int argb) + { + Control.BackColor = Color.FromArgb(argb); + } + + protected override void SetForeground(int argb) + { + Control.ForeColor = Color.FromArgb(argb); + } + + protected override void SetFont(Interop.LogFont font) + { + Control.Font = Font.FromLogFont(font); + } + + protected override IntPtr Handle + { + get + { + { + return Control.Handle; + } + } + } + + protected override void SetParentHandle(IntPtr handle) + { + HandlerNativeMethods.SetParent(Control.Handle, handle); + } + +#region IDisposable Members + + ~WinFormsPreviewHandler() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing && Control != null) + { + Control.Dispose(); + } + } + +#endregion + } +} +#endif \ No newline at end of file diff --git a/ShellExtensions/PreviewHandlers/WpfPreviewHandler.cs b/ShellExtensions/PreviewHandlers/WpfPreviewHandler.cs new file mode 100644 index 0000000..dd0aeb1 --- /dev/null +++ b/ShellExtensions/PreviewHandlers/WpfPreviewHandler.cs @@ -0,0 +1,186 @@ +#if PREVIEW_HANDLER +using ShellExtensions.Interop; +using ShellExtensions.Interop.Common; +using ShellExtensions.Resources; +using System; +using System.Windows.Controls; +using System.Windows.Interop; +using System.Windows.Media; + +namespace ShellExtensions.PreviewHandlers +{ + /// + /// This is the base class for all WPF-based preview handlers and provides their basic functionality. + /// To create a custom preview handler that contains a WPF user control, + /// a class must derive from this, use the , + /// and implement 1 or more of the following interfaces: + /// , + /// , + /// . + /// + public abstract class WpfPreviewHandler : PreviewHandler, IDisposable + { + HwndSource _source = null; + private IntPtr _parentHandle = IntPtr.Zero; + private NativeRect _bounds; + + /// + /// This control must be populated by the deriving class before the preview is shown. + /// + public UserControl Control { get; protected set; } + + /// + /// Throws an exception if the Control property has not been populated. + /// + protected void ThrowIfNoControl() + { + if (Control == null) + { + throw new InvalidOperationException(LocalizedMessages.PreviewHandlerControlNotInitialized); + } + } + + /// + /// Updates the placement of the Control. + /// + protected void UpdatePlacement() + { + if (_source != null) + { + HandlerNativeMethods.SetParent(_source.Handle, _parentHandle); + + HandlerNativeMethods.SetWindowPos(_source.Handle, new IntPtr((int)SetWindowPositionInsertAfter.Top), + 0, 0, Math.Abs(_bounds.Left - _bounds.Right), Math.Abs(_bounds.Top - _bounds.Bottom), SetWindowPositionOptions.ShowWindow); + } + } + + protected override void SetParentHandle(IntPtr handle) + { + _parentHandle = handle; + UpdatePlacement(); + } + + protected override void Initialize() + { + if (_source == null) + { + ThrowIfNoControl(); + + HwndSourceParameters p = new HwndSourceParameters + { + WindowStyle = (int)(WindowStyles.Child | WindowStyles.Visible | WindowStyles.ClipSiblings), + ParentWindow = _parentHandle, + Width = Math.Abs(_bounds.Left - _bounds.Right), + Height = Math.Abs(_bounds.Top - _bounds.Bottom) + }; + + _source = new HwndSource(p); + _source.CompositionTarget.BackgroundColor = Brushes.WhiteSmoke.Color; + _source.RootVisual = (Visual)Control.Content; + } + UpdatePlacement(); + } + + protected override IntPtr Handle + { + get + { + { + if (_source == null) + { + throw new InvalidOperationException(LocalizedMessages.WpfPreviewHandlerNoHandle); + } + return _source.Handle; + } + } + } + + protected override void UpdateBounds(NativeRect bounds) + { + _bounds = bounds; + UpdatePlacement(); + } + + protected override void HandleInitializeException(Exception caughtException) + { + if (caughtException == null) { return; } + + TextBox text = new TextBox + { + IsReadOnly = true, + MaxLines = 20, + Text = caughtException.ToString() + }; + Control = new UserControl() { Content = text }; + } + + protected override void SetFocus() + { + Control.Focus(); + } + + protected override void SetBackground(int argb) + { + Control.Background = new SolidColorBrush(Color.FromArgb( + (byte)((argb >> 24) & 0xFF), //a + (byte)((argb >> 16) & 0xFF), //r + (byte)((argb >> 8) & 0xFF), //g + (byte)(argb & 0xFF))); //b + } + + protected override void SetForeground(int argb) + { + Control.Foreground = new SolidColorBrush(Color.FromArgb( + (byte)((argb >> 24) & 0xFF), //a + (byte)((argb >> 16) & 0xFF), //r + (byte)((argb >> 8) & 0xFF), //g + (byte)(argb & 0xFF))); //b + } + + protected override void SetFont(LogFont font) + { + if (font == null) { throw new ArgumentNullException(nameof(font)); } + + Control.FontFamily = new FontFamily(font.FaceName); + Control.FontSize = font.Height; + Control.FontWeight = font.Weight > 0 && font.Weight < 1000 ? + System.Windows.FontWeight.FromOpenTypeWeight(font.Weight) : + System.Windows.FontWeights.Normal; + } + +#region IDisposable Members + + /// + /// Preview handler control finalizer + /// + ~WpfPreviewHandler() + { + Dispose(false); + } + + /// + /// Disposes the control + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Provides means to dispose the object. + /// When overriden, it is imperative that base.Dispose(true) is called within the implementation. + /// + /// + protected virtual void Dispose(bool disposing) + { + if (disposing && _source != null) + { + _source.Dispose(); + } + } + +#endregion + } +} +#endif \ No newline at end of file diff --git a/ShellExtensions/Resources/LocalizedMessages.cs b/ShellExtensions/Resources/LocalizedMessages.cs index 1d54508..05f1458 100644 --- a/ShellExtensions/Resources/LocalizedMessages.cs +++ b/ShellExtensions/Resources/LocalizedMessages.cs @@ -2,12 +2,22 @@ { internal sealed class LocalizedMessages { +#if PREVIEW_HANDLER + public const string PreviewHandlerControlNotInitialized = "Control has not yet been assigned. Methods requiring it cannot be called."; + public const string PreviewHandlerInterfaceNotImplemented = "{0} must implement one or more of IPreviewFromStream, IPreviewFromShellObject or IPreviewFromFile."; + public const string PreviewHandlerInvalidAttributes = "PreviewHandler '{0}' must have exactly one PreviewHandler attribute."; + public const string PreviewHandlerUnsupportedInterfaceCalled = "Unable to call interface {0} because it is not supported on this object."; + public const string WpfPreviewHandlerNoHandle = "Cannot retrieve handle because proxy window has not been created."; +#endif + public const string StorageStreamBufferOverflow = "The sum of offset and count must be less than or equal to the size of the buffer."; public const string StorageStreamCountLessThanZero = "Count must be greater than or equal to zero."; public const string StorageStreamIsReadonly = "The stream was initialized as read-only."; public const string StorageStreamOffsetLessThanZero = "Offset must be greater than or equal to zero."; +#if THUMBNAIL public const string ThumbnailProviderDisabledProcessIsolation = "{0} does not implement IThumbnailFromStream and so requires DisableProcessIsolation set to true."; public const string ThumbnailProviderInterfaceNotImplemented = "{0} must implement one or more of IThumbnailFromStream, IThumbnailFromShellObject or IThumbnailFromFile."; +#endif } } diff --git a/ShellExtensions/ShellExtensions.projitems b/ShellExtensions/ShellExtensions.projitems index a95c407..cba7531 100644 --- a/ShellExtensions/ShellExtensions.projitems +++ b/ShellExtensions/ShellExtensions.projitems @@ -9,8 +9,14 @@ ShellExtensions - + + + + + + + diff --git a/ShellExtensions/ThumbnailProviders/ManagedInitializationInterfaces.cs b/ShellExtensions/ThumbnailProviders/ManagedInitializationInterfaces.cs index 1b53d86..2f4b6bb 100644 --- a/ShellExtensions/ThumbnailProviders/ManagedInitializationInterfaces.cs +++ b/ShellExtensions/ThumbnailProviders/ManagedInitializationInterfaces.cs @@ -1,4 +1,5 @@ -using System.Drawing; +#if THUMBNAIL +using System.Drawing; using System.IO; namespace ShellExtensions @@ -52,3 +53,4 @@ namespace ShellExtensions Bitmap ConstructBitmap(FileInfo info, int sideSize); } } +#endif \ No newline at end of file diff --git a/ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs b/ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs index 1cac4df..b97e3db 100644 --- a/ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs +++ b/ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs @@ -1,4 +1,5 @@ -using Microsoft.Win32; +#if THUMBNAIL +using Microsoft.Win32; using ShellExtensions.Interop; using ShellExtensions.Interop.Common; using ShellExtensions.Resources; @@ -280,3 +281,4 @@ namespace ShellExtensions #endregion } } +#endif \ No newline at end of file diff --git a/ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs b/ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs index 4727a46..38a32f3 100644 --- a/ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs +++ b/ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs @@ -1,8 +1,9 @@ -using System; +#if THUMBNAIL +using System; namespace ShellExtensions { - /// + /// /// This class attribute is applied to a Thumbnail Provider to specify registration parameters /// and aesthetic attributes. /// @@ -134,3 +135,4 @@ namespace ShellExtensions VideoSprockets = 3 } } +#endif \ No newline at end of file