From 0f48dbe845ac800a02448d5bbc017f13c3361684 Mon Sep 17 00:00:00 2001 From: Tsanie Lily Date: Tue, 28 Dec 2021 11:25:53 +0800 Subject: [PATCH] restructure projects --- PhotoThumbnail.sln | 6 ++ PhotoThumbnail/PhotoThumbnail.csproj | 13 +-- PhotoThumbnail/TgaThumbnailer.cs | 16 +++- .../Interop/Common/ShellNativeStructs.cs | 96 +------------------ .../Interop/HandlerNativeMethods.cs | 92 ++++++++++++++++++ .../Resources/LocalizedMessages.cs | 13 +++ ShellExtensions/ShellExtensions.projitems | 20 ++++ ShellExtensions/ShellExtensions.shproj | 13 +++ .../StorageStream.cs | 25 ++--- .../ManagedInitializationInterfaces.cs | 54 +++++++++++ .../ThumbnailProviders}/ThumbnailProvider.cs | 48 +++++++--- .../ThumbnailProviderAttribute.cs | 6 +- 12 files changed, 267 insertions(+), 135 deletions(-) rename Definitions/Reference.cs => ShellExtensions/Interop/Common/ShellNativeStructs.cs (53%) create mode 100644 ShellExtensions/Interop/HandlerNativeMethods.cs create mode 100644 ShellExtensions/Resources/LocalizedMessages.cs create mode 100644 ShellExtensions/ShellExtensions.projitems create mode 100644 ShellExtensions/ShellExtensions.shproj rename {Definitions => ShellExtensions}/StorageStream.cs (92%) create mode 100644 ShellExtensions/ThumbnailProviders/ManagedInitializationInterfaces.cs rename {Definitions => ShellExtensions/ThumbnailProviders}/ThumbnailProvider.cs (83%) rename {Definitions => ShellExtensions/ThumbnailProviders}/ThumbnailProviderAttribute.cs (97%) diff --git a/PhotoThumbnail.sln b/PhotoThumbnail.sln index 519958d..bbe12de 100644 --- a/PhotoThumbnail.sln +++ b/PhotoThumbnail.sln @@ -7,7 +7,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhotoThumbnail", "PhotoThum EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsoleApp", "TestConsoleApp\TestConsoleApp.csproj", "{C2573376-C9CF-41DE-B9A6-22790C048062}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ShellExtensions", "ShellExtensions\ShellExtensions.shproj", "{50974517-2D52-4470-9F60-E37B30D1491E}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ShellExtensions\ShellExtensions.projitems*{08c34c77-b778-46aa-a48b-bea6b9fe07fe}*SharedItemsImports = 4 + ShellExtensions\ShellExtensions.projitems*{50974517-2d52-4470-9f60-e37b30d1491e}*SharedItemsImports = 13 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU diff --git a/PhotoThumbnail/PhotoThumbnail.csproj b/PhotoThumbnail/PhotoThumbnail.csproj index 316fbb4..e2e1374 100644 --- a/PhotoThumbnail/PhotoThumbnail.csproj +++ b/PhotoThumbnail/PhotoThumbnail.csproj @@ -50,18 +50,6 @@ - - Definitions\Reference.cs - - - Definitions\StorageStream.cs - - - Definitions\ThumbnailProvider.cs - - - Definitions\ThumbnailProviderAttribute.cs - @@ -69,5 +57,6 @@ + \ No newline at end of file diff --git a/PhotoThumbnail/TgaThumbnailer.cs b/PhotoThumbnail/TgaThumbnailer.cs index e06ef16..f4e8fa4 100644 --- a/PhotoThumbnail/TgaThumbnailer.cs +++ b/PhotoThumbnail/TgaThumbnailer.cs @@ -1,4 +1,4 @@ -using PhotoThumbnail.Definitions; +using ShellExtensions; using System; using System.Drawing; using System.IO; @@ -11,7 +11,7 @@ namespace PhotoThumbnail [ClassInterface(ClassInterfaceType.None)] [ProgId("PhotoThumbnail.TgaThumbnailer")] [ThumbnailProvider("TgaThumbnailer", ".tga", ThumbnailAdornment = ThumbnailAdornment.PhotoBorder)] - public class TgaThumbnailer : ThumbnailProvider, IThumbnailFromStream + public class TgaThumbnailer : ThumbnailProvider, IThumbnailFromStream, IThumbnailFromFile { #region IThumbnailFromStream Members @@ -40,5 +40,17 @@ namespace PhotoThumbnail } #endregion + + #region IThumbnailFromFile Members + + public Bitmap ConstructBitmap(FileInfo info, int sideSize) + { + using (FileStream stream = File.OpenRead(info.FullName)) + { + return ConstructBitmap(stream, sideSize); + } + } + + #endregion } } diff --git a/Definitions/Reference.cs b/ShellExtensions/Interop/Common/ShellNativeStructs.cs similarity index 53% rename from Definitions/Reference.cs rename to ShellExtensions/Interop/Common/ShellNativeStructs.cs index 694150b..69d4a69 100644 --- a/Definitions/Reference.cs +++ b/ShellExtensions/Interop/Common/ShellNativeStructs.cs @@ -1,47 +1,7 @@ using System; -using System.Drawing; -using System.IO; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -namespace PhotoThumbnail.Definitions +namespace ShellExtensions.Interop.Common { - internal static class HandlerNativeMethods - { - internal static readonly Guid IThumbnailProviderGuid = new Guid("e357fccd-a995-4576-b01f-234630154e96"); - - //internal static readonly Guid IInitializeWithFileGuid = new Guid("b7d14566-0509-4cce-a71f-0a554233bd9b"); - internal static readonly Guid IInitializeWithStreamGuid = new Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"); - //internal static readonly Guid IInitializeWithItemGuid = new Guid("7f73be3f-fb79-493c-a6c7-7ee14e245841"); - - internal static readonly Guid IMarshalGuid = new Guid("00000003-0000-0000-C000-000000000046"); - } - - /// - /// This interface exposes the function for initializing the - /// Thumbnail Provider with a . - /// If this interfaces is not used, then the handler must opt out of process isolation. - /// 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 IThumbnailFromStream - { - /// - /// Provides the to the item from which a thumbnail should be created. - /// Only 32bpp bitmaps support adornments. - /// While 24bpp bitmaps will be displayed they will not display adornments. - /// Additional guidelines for developing thumbnails can be found at http://msdn.microsoft.com/en-us/library/cc144115(v=VS.85).aspx - /// - /// - /// Stream to initialize the thumbnail - /// Square side dimension in which the thumbnail should fit; the thumbnail will be scaled otherwise. - /// - Bitmap ConstructBitmap(Stream stream, int sideSize); - } - /// /// The STGM constants are flags that indicate /// conditions for creating and deleting the object and access modes @@ -162,58 +122,4 @@ namespace PhotoThumbnail.Definitions /// DirectSingleWriterMultipleReader = 0x00400000 } - - // - /// Thumbnail Alpha Types - /// - public enum ThumbnailAlphaType - { - /// - /// Let the system decide. - /// - Unknown = 0, - - /// - /// No transparency - /// - NoAlphaChannel = 1, - - /// - /// Has transparency - /// - HasAlphaChannel = 2, - } - - /// - /// 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 stream. - /// - [ComImport] - [Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IInitializeWithStream - { - /// - /// Initializes with a stream. - /// - /// - /// - void Initialize(IStream stream, AccessModes fileMode); - } } diff --git a/ShellExtensions/Interop/HandlerNativeMethods.cs b/ShellExtensions/Interop/HandlerNativeMethods.cs new file mode 100644 index 0000000..0b74c70 --- /dev/null +++ b/ShellExtensions/Interop/HandlerNativeMethods.cs @@ -0,0 +1,92 @@ +using ShellExtensions.Interop.Common; +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +namespace ShellExtensions.Interop +{ + internal static class HandlerNativeMethods + { + internal static readonly Guid IThumbnailProviderGuid = new Guid("e357fccd-a995-4576-b01f-234630154e96"); + + internal static readonly Guid IInitializeWithFileGuid = new Guid("b7d14566-0509-4cce-a71f-0a554233bd9b"); + internal static readonly Guid IInitializeWithStreamGuid = new Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"); + //internal static readonly Guid IInitializeWithItemGuid = new Guid("7f73be3f-fb79-493c-a6c7-7ee14e245841"); + + 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. + /// + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("b7d14566-0509-4cce-a71f-0a554233bd9b")] + interface IInitializeWithFile + { + /// + /// Initializes with a file. + /// + /// + /// + void Initialize([MarshalAs(UnmanagedType.LPWStr)] string filePath, AccessModes fileMode); + } + + /// + /// Provides means by which to initialize with a stream. + /// + [ComImport] + [Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IInitializeWithStream + { + /// + /// Initializes with a stream. + /// + /// + /// + void Initialize(IStream stream, AccessModes fileMode); + } + + #endregion + + // + /// Thumbnail Alpha Types + /// + public enum ThumbnailAlphaType + { + /// + /// Let the system decide. + /// + Unknown = 0, + + /// + /// No transparency + /// + NoAlphaChannel = 1, + + /// + /// Has transparency + /// + HasAlphaChannel = 2, + } +} diff --git a/ShellExtensions/Resources/LocalizedMessages.cs b/ShellExtensions/Resources/LocalizedMessages.cs new file mode 100644 index 0000000..1d54508 --- /dev/null +++ b/ShellExtensions/Resources/LocalizedMessages.cs @@ -0,0 +1,13 @@ +namespace ShellExtensions.Resources +{ + internal sealed class LocalizedMessages + { + 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."; + + 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."; + } +} diff --git a/ShellExtensions/ShellExtensions.projitems b/ShellExtensions/ShellExtensions.projitems new file mode 100644 index 0000000..a95c407 --- /dev/null +++ b/ShellExtensions/ShellExtensions.projitems @@ -0,0 +1,20 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 50974517-2d52-4470-9f60-e37b30d1491e + + + ShellExtensions + + + + + + + + + + + \ No newline at end of file diff --git a/ShellExtensions/ShellExtensions.shproj b/ShellExtensions/ShellExtensions.shproj new file mode 100644 index 0000000..12afa5a --- /dev/null +++ b/ShellExtensions/ShellExtensions.shproj @@ -0,0 +1,13 @@ + + + + 50974517-2d52-4470-9f60-e37b30d1491e + 14.0 + + + + + + + + diff --git a/Definitions/StorageStream.cs b/ShellExtensions/StorageStream.cs similarity index 92% rename from Definitions/StorageStream.cs rename to ShellExtensions/StorageStream.cs index b266d32..2446a87 100644 --- a/Definitions/StorageStream.cs +++ b/ShellExtensions/StorageStream.cs @@ -1,9 +1,10 @@ -using System; +using ShellExtensions.Resources; +using System; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; -namespace PhotoThumbnail.Definitions +namespace ShellExtensions { /// /// A wrapper for the native IStream object. @@ -15,7 +16,7 @@ namespace PhotoThumbnail.Definitions internal StorageStream(IStream stream, bool readOnly) { - _stream = stream ?? throw new ArgumentNullException("stream"); + _stream = stream ?? throw new ArgumentNullException(nameof(stream)); _isReadOnly = readOnly; } @@ -73,10 +74,10 @@ namespace PhotoThumbnail.Definitions { ThrowIfDisposed(); - if (buffer == null) { throw new ArgumentNullException("buffer"); } - if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "StorageStreamOffsetLessThanZero"); } - if (count < 0) { throw new ArgumentOutOfRangeException("count", "StorageStreamCountLessThanZero"); } - if (offset + count > buffer.Length) { throw new ArgumentException("StorageStreamBufferOverflow", "count"); } + if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } + if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset), LocalizedMessages.StorageStreamOffsetLessThanZero); } + if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), LocalizedMessages.StorageStreamCountLessThanZero); } + if (offset + count > buffer.Length) { throw new ArgumentException(LocalizedMessages.StorageStreamBufferOverflow, nameof(count)); } int bytesRead = 0; if (count > 0) @@ -119,11 +120,11 @@ namespace PhotoThumbnail.Definitions { ThrowIfDisposed(); - if (_isReadOnly) { throw new InvalidOperationException("StorageStreamIsReadonly"); } - if (buffer == null) { throw new ArgumentNullException("buffer"); } - if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "StorageStreamOffsetLessThanZero"); } - if (count < 0) { throw new ArgumentOutOfRangeException("count", "StorageStreamCountLessThanZero"); } - if (offset + count > buffer.Length) { throw new ArgumentException("StorageStreamBufferOverflow", "count"); } + if (_isReadOnly) { throw new InvalidOperationException(LocalizedMessages.StorageStreamIsReadonly); } + if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } + if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset), LocalizedMessages.StorageStreamOffsetLessThanZero); } + if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), LocalizedMessages.StorageStreamCountLessThanZero); } + if (offset + count > buffer.Length) { throw new ArgumentException(LocalizedMessages.StorageStreamBufferOverflow, nameof(count)); } if (count > 0) { diff --git a/ShellExtensions/ThumbnailProviders/ManagedInitializationInterfaces.cs b/ShellExtensions/ThumbnailProviders/ManagedInitializationInterfaces.cs new file mode 100644 index 0000000..1b53d86 --- /dev/null +++ b/ShellExtensions/ThumbnailProviders/ManagedInitializationInterfaces.cs @@ -0,0 +1,54 @@ +using System.Drawing; +using System.IO; + +namespace ShellExtensions +{ + /// + /// This interface exposes the function for initializing the + /// Thumbnail Provider with a . + /// If this interfaces is not used, then the handler must opt out of process isolation. + /// 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 IThumbnailFromStream + { + /// + /// Provides the to the item from which a thumbnail should be created. + /// Only 32bpp bitmaps support adornments. + /// While 24bpp bitmaps will be displayed they will not display adornments. + /// Additional guidelines for developing thumbnails can be found at http://msdn.microsoft.com/en-us/library/cc144115(v=VS.85).aspx + /// + /// + /// Stream to initialize the thumbnail + /// Square side dimension in which the thumbnail should fit; the thumbnail will be scaled otherwise. + /// + Bitmap ConstructBitmap(Stream stream, int sideSize); + } + + /// + /// This interface exposes the function for initializing the + /// Thumbnail Provider with file information. + /// 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 IThumbnailFromFile + { + /// + /// Provides the to the item from which a thumbnail should be created. + /// Only 32bpp bitmaps support adornments. + /// While 24bpp bitmaps will be displayed they will not display adornments. + /// Additional guidelines for developing thumbnails can be found at http://msdn.microsoft.com/en-us/library/cc144115(v=VS.85).aspx + /// + /// + /// FileInfo to initialize the thumbnail + /// Square side dimension in which the thumbnail should fit; the thumbnail will be scaled otherwise. + /// Generated thumbnail + Bitmap ConstructBitmap(FileInfo info, int sideSize); + } +} diff --git a/Definitions/ThumbnailProvider.cs b/ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs similarity index 83% rename from Definitions/ThumbnailProvider.cs rename to ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs index f343533..1cac4df 100644 --- a/Definitions/ThumbnailProvider.cs +++ b/ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs @@ -1,12 +1,16 @@ using Microsoft.Win32; +using ShellExtensions.Interop; +using ShellExtensions.Interop.Common; +using ShellExtensions.Resources; using System; using System.Drawing; +using System.IO; using System.Linq; using System.Runtime.InteropServices; -namespace PhotoThumbnail.Definitions +namespace ShellExtensions { - public abstract class ThumbnailProvider : IThumbnailProvider, ICustomQueryInterface, IDisposable, IInitializeWithStream + public abstract class ThumbnailProvider : IThumbnailProvider, ICustomQueryInterface, IDisposable, IInitializeWithStream, IInitializeWithFile { private Bitmap GetBitmap(int sideLength) { @@ -14,13 +18,21 @@ namespace PhotoThumbnail.Definitions { return stream.ConstructBitmap(_stream, sideLength); } + if (_info != null && this is IThumbnailFromFile file) + { + return file.ConstructBitmap(_info, sideLength); + } - throw new InvalidOperationException("ThumbnailProviderInterfaceNotImplemented: " + GetType().Name); + throw new InvalidOperationException( + string.Format(System.Globalization.CultureInfo.InvariantCulture, + LocalizedMessages.ThumbnailProviderInterfaceNotImplemented, + GetType().Name)); } public virtual ThumbnailAlphaType ThumbnailAlphaType => ThumbnailAlphaType.Unknown; private StorageStream _stream; + private FileInfo _info; #region IThumbnailProvider Members @@ -47,7 +59,8 @@ namespace PhotoThumbnail.Definitions return CustomQueryInterfaceResult.Failed; } - if (iid == HandlerNativeMethods.IInitializeWithStreamGuid && !(this is IThumbnailFromStream)) + if ((iid == HandlerNativeMethods.IInitializeWithStreamGuid && !(this is IThumbnailFromStream)) || + (iid == HandlerNativeMethods.IInitializeWithFileGuid && !(this is IThumbnailFromFile))) { return CustomQueryInterfaceResult.Failed; } @@ -186,26 +199,30 @@ namespace PhotoThumbnail.Definitions } var interfaces = type.GetInterfaces(); - bool interfaced = interfaces.Any(x => x == typeof(IThumbnailFromStream)); + bool interfaced = interfaces.Any(x => x is IThumbnailFromStream); - /* - if (interfaces.Any(x => x == typeof(IThumbnailFromShellObject) || x == typeof(IThumbnailFromFile))) + if (interfaces.Any(x => x is IThumbnailFromFile)) { // According to MSDN (http://msdn.microsoft.com/en-us/library/cc144114(v=VS.85).aspx) // A thumbnail provider that does not implement IInitializeWithStream must opt out of // running in the isolated process. The default behavior of the indexer opts in - // to process isolation regardless of which interfaces are implemented. + // to process isolation regardless of which interfaces are implemented. if (!interfaced && !attribute.DisableProcessIsolation) { - throw new InvalidOperationException("ThumbnailProviderDisabledProcessIsolation: " + type.Name); + throw new InvalidOperationException( + string.Format(System.Globalization.CultureInfo.InvariantCulture, + LocalizedMessages.ThumbnailProviderDisabledProcessIsolation, + type.Name)); } interfaced = true; } - */ if (!interfaced) { - throw new InvalidOperationException("ThumbnailProviderInterfaceNotImplemented: " + type.Name); + throw new InvalidOperationException( + string.Format(System.Globalization.CultureInfo.InvariantCulture, + LocalizedMessages.ThumbnailProviderInterfaceNotImplemented, + type.Name)); } } @@ -220,6 +237,15 @@ namespace PhotoThumbnail.Definitions #endregion + #region IInitializeWithFile Members + + void IInitializeWithFile.Initialize(string filePath, AccessModes fileMode) + { + _info = new FileInfo(filePath); + } + + #endregion + #region IDisposable Members /// diff --git a/Definitions/ThumbnailProviderAttribute.cs b/ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs similarity index 97% rename from Definitions/ThumbnailProviderAttribute.cs rename to ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs index 4c52317..4727a46 100644 --- a/Definitions/ThumbnailProviderAttribute.cs +++ b/ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace PhotoThumbnail.Definitions +namespace ShellExtensions { /// /// This class attribute is applied to a Thumbnail Provider to specify registration parameters @@ -16,8 +16,8 @@ namespace PhotoThumbnail.Definitions /// Semi-colon-separated list of extensions supported by this provider. public ThumbnailProviderAttribute(string name, string extensions) { - Name = name ?? throw new ArgumentNullException("name"); - Extensions = extensions ?? throw new ArgumentNullException("extensions"); + Name = name ?? throw new ArgumentNullException(nameof(name)); + Extensions = extensions ?? throw new ArgumentNullException(nameof(extensions)); DisableProcessIsolation = false; ThumbnailCutoff = ThumbnailCutoffSize.Square20;