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;