restructure projects
This commit is contained in:
@ -0,0 +1,54 @@
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
||||
namespace ShellExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface exposes the <see cref="ConsructBitmap"/> function for initializing the
|
||||
/// Thumbnail Provider with a <typeparamref name="Stream"/>.
|
||||
/// 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:
|
||||
/// <typeparamref name="IThumbnailFromStream"/>
|
||||
/// <typeparamref name="IThumbnailFromShellObject"/>
|
||||
/// <typeparamref name="IThumbnailFromFile"/>
|
||||
/// </summary>
|
||||
public interface IThumbnailFromStream
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the <typeparamref name="Stream"/> to the item from which a thumbnail should be created.
|
||||
/// <remarks>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
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream to initialize the thumbnail</param>
|
||||
/// <param name="sideSize">Square side dimension in which the thumbnail should fit; the thumbnail will be scaled otherwise.</param>
|
||||
/// <returns></returns>
|
||||
Bitmap ConstructBitmap(Stream stream, int sideSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This interface exposes the <see cref="ConsructBitmap"/> 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:
|
||||
/// <typeparamref name="IThumbnailFromStream"/>
|
||||
/// <typeparamref name="IThumbnailFromShellObject"/>
|
||||
/// <typeparamref name="IThumbnailFromFile"/>
|
||||
/// </summary>
|
||||
public interface IThumbnailFromFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the <typeparamref name="FileInfo"/> to the item from which a thumbnail should be created.
|
||||
/// <remarks>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
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
/// <param name="info">FileInfo to initialize the thumbnail</param>
|
||||
/// <param name="sideSize">Square side dimension in which the thumbnail should fit; the thumbnail will be scaled otherwise.</param>
|
||||
/// <returns>Generated thumbnail</returns>
|
||||
Bitmap ConstructBitmap(FileInfo info, int sideSize);
|
||||
}
|
||||
}
|
282
ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs
Normal file
282
ShellExtensions/ThumbnailProviders/ThumbnailProvider.cs
Normal file
@ -0,0 +1,282 @@
|
||||
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 ShellExtensions
|
||||
{
|
||||
public abstract class ThumbnailProvider : IThumbnailProvider, ICustomQueryInterface, IDisposable, IInitializeWithStream, IInitializeWithFile
|
||||
{
|
||||
private Bitmap GetBitmap(int sideLength)
|
||||
{
|
||||
if (_stream != null && this is IThumbnailFromStream stream)
|
||||
{
|
||||
return stream.ConstructBitmap(_stream, sideLength);
|
||||
}
|
||||
if (_info != null && this is IThumbnailFromFile file)
|
||||
{
|
||||
return file.ConstructBitmap(_info, sideLength);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
void IThumbnailProvider.GetThumbnail(uint sideLength, out IntPtr hBitmap, out uint alphaType)
|
||||
{
|
||||
using (Bitmap map = GetBitmap((int)sideLength))
|
||||
{
|
||||
hBitmap = map.GetHbitmap();
|
||||
}
|
||||
alphaType = (uint)ThumbnailAlphaType;
|
||||
}
|
||||
|
||||
#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 IThumbnailFromStream)) ||
|
||||
(iid == HandlerNativeMethods.IInitializeWithFileGuid && !(this is IThumbnailFromFile)))
|
||||
{
|
||||
return CustomQueryInterfaceResult.Failed;
|
||||
}
|
||||
|
||||
return CustomQueryInterfaceResult.NotHandled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region COM Registration
|
||||
|
||||
/// <summary>
|
||||
/// Called when the assembly is registered via RegAsm.
|
||||
/// </summary>
|
||||
/// <param name="registerType">Type to be registered.</param>
|
||||
[ComRegisterFunction]
|
||||
private static void Register(Type registerType)
|
||||
{
|
||||
if (registerType != null && registerType.IsSubclassOf(typeof(ThumbnailProvider)))
|
||||
{
|
||||
object[] attributes = registerType.GetCustomAttributes(typeof(ThumbnailProviderAttribute), true);
|
||||
if (attributes != null && attributes.Length == 1)
|
||||
{
|
||||
ThumbnailProviderAttribute attribute = attributes[0] as ThumbnailProviderAttribute;
|
||||
ThrowIfInvalid(registerType, attribute);
|
||||
RegisterThumbnailHandler(registerType.GUID.ToString("B"), attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterThumbnailHandler(string guid, ThumbnailProviderAttribute attribute)
|
||||
{
|
||||
// set process isolation
|
||||
using (RegistryKey clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID"))
|
||||
using (RegistryKey guidKey = clsidKey.OpenSubKey(guid, true))
|
||||
{
|
||||
guidKey.SetValue("DisableProcessIsolation", attribute.DisableProcessIsolation ? 1 : 0, RegistryValueKind.DWord);
|
||||
|
||||
using (RegistryKey inproc = guidKey.OpenSubKey("InprocServer32", true))
|
||||
{
|
||||
inproc.SetValue("ThreadingModel", "Apartment", RegistryValueKind.String);
|
||||
}
|
||||
}
|
||||
|
||||
// register file as an approved extension
|
||||
using (RegistryKey approvedShellExtensions = Registry.LocalMachine.OpenSubKey(
|
||||
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true))
|
||||
{
|
||||
approvedShellExtensions.SetValue(guid, attribute.Name, RegistryValueKind.String);
|
||||
}
|
||||
|
||||
// register extension with each extension in the list
|
||||
string[] extensions = attribute.Extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string extension in extensions)
|
||||
{
|
||||
using (RegistryKey extensionKey = Registry.ClassesRoot.CreateSubKey(extension)) // Create makes it writable
|
||||
using (RegistryKey shellExKey = extensionKey.CreateSubKey("shellex"))
|
||||
using (RegistryKey providerKey = shellExKey.CreateSubKey(HandlerNativeMethods.IThumbnailProviderGuid.ToString("B")))
|
||||
{
|
||||
providerKey.SetValue(null, guid, RegistryValueKind.String);
|
||||
|
||||
if (attribute.ThumbnailCutoff == ThumbnailCutoffSize.Square20)
|
||||
{
|
||||
extensionKey.DeleteValue("ThumbnailCutoff", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
extensionKey.SetValue("ThumbnailCutoff", (int)attribute.ThumbnailCutoff, RegistryValueKind.DWord);
|
||||
}
|
||||
|
||||
|
||||
if (attribute.TypeOverlay != null)
|
||||
{
|
||||
extensionKey.SetValue("TypeOverlay", attribute.TypeOverlay, RegistryValueKind.String);
|
||||
}
|
||||
|
||||
if (attribute.ThumbnailAdornment == ThumbnailAdornment.Default)
|
||||
{
|
||||
extensionKey.DeleteValue("Treatment", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
extensionKey.SetValue("Treatment", (int)attribute.ThumbnailAdornment, RegistryValueKind.DWord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called when the assembly is registered via RegAsm.
|
||||
/// </summary>
|
||||
/// <param name="registerType">Type to register.</param>
|
||||
[ComUnregisterFunction]
|
||||
private static void Unregister(Type registerType)
|
||||
{
|
||||
if (registerType != null && registerType.IsSubclassOf(typeof(ThumbnailProvider)))
|
||||
{
|
||||
object[] attributes = registerType.GetCustomAttributes(typeof(ThumbnailProviderAttribute), true);
|
||||
if (attributes != null && attributes.Length == 1)
|
||||
{
|
||||
ThumbnailProviderAttribute attribute = attributes[0] as ThumbnailProviderAttribute;
|
||||
UnregisterThumbnailHandler(registerType.GUID.ToString("B"), attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnregisterThumbnailHandler(string guid, ThumbnailProviderAttribute attribute)
|
||||
{
|
||||
string[] extensions = attribute.Extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string extension in extensions)
|
||||
{
|
||||
using (RegistryKey extKey = Registry.ClassesRoot.OpenSubKey(extension, true))
|
||||
using (RegistryKey shellexKey = extKey.OpenSubKey("shellex", true))
|
||||
{
|
||||
shellexKey.DeleteSubKey(HandlerNativeMethods.IThumbnailProviderGuid.ToString("B"), false);
|
||||
|
||||
extKey.DeleteValue("ThumbnailCutoff", false);
|
||||
extKey.DeleteValue("TypeOverlay", false);
|
||||
extKey.DeleteValue("Treatment", false); // Thumbnail adornment
|
||||
}
|
||||
}
|
||||
|
||||
using (RegistryKey approvedShellExtensions = Registry.LocalMachine.OpenSubKey(
|
||||
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true))
|
||||
{
|
||||
approvedShellExtensions.DeleteValue(guid, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowIfInvalid(Type type, ThumbnailProviderAttribute attribute)
|
||||
{
|
||||
if (attribute is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attribute));
|
||||
}
|
||||
|
||||
var interfaces = type.GetInterfaces();
|
||||
bool interfaced = interfaces.Any(x => x is IThumbnailFromStream);
|
||||
|
||||
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.
|
||||
if (!interfaced && !attribute.DisableProcessIsolation)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
LocalizedMessages.ThumbnailProviderDisabledProcessIsolation,
|
||||
type.Name));
|
||||
}
|
||||
interfaced = true;
|
||||
}
|
||||
|
||||
if (!interfaced)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||
LocalizedMessages.ThumbnailProviderInterfaceNotImplemented,
|
||||
type.Name));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IInitializeWithStream Members
|
||||
|
||||
void IInitializeWithStream.Initialize(System.Runtime.InteropServices.ComTypes.IStream stream, AccessModes fileMode)
|
||||
{
|
||||
_stream = new StorageStream(stream, fileMode != AccessModes.ReadWrite);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IInitializeWithFile Members
|
||||
|
||||
void IInitializeWithFile.Initialize(string filePath, AccessModes fileMode)
|
||||
{
|
||||
_info = new FileInfo(filePath);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Finalizer for the thumbnail provider.
|
||||
/// </summary>
|
||||
~ThumbnailProvider()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the thumbnail provider.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disploses the thumbnail provider.
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _stream != null)
|
||||
{
|
||||
_stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
136
ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs
Normal file
136
ShellExtensions/ThumbnailProviders/ThumbnailProviderAttribute.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using System;
|
||||
|
||||
namespace ShellExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This class attribute is applied to a Thumbnail Provider to specify registration parameters
|
||||
/// and aesthetic attributes.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class ThumbnailProviderAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the attribute.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the provider</param>
|
||||
/// <param name="extensions">Semi-colon-separated list of extensions supported by this provider.</param>
|
||||
public ThumbnailProviderAttribute(string name, string extensions)
|
||||
{
|
||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
Extensions = extensions ?? throw new ArgumentNullException(nameof(extensions));
|
||||
|
||||
DisableProcessIsolation = false;
|
||||
ThumbnailCutoff = ThumbnailCutoffSize.Square20;
|
||||
TypeOverlay = null;
|
||||
ThumbnailAdornment = ThumbnailAdornment.Default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the provider
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the semi-colon-separated list of extensions supported by the provider.
|
||||
/// </summary>
|
||||
public string Extensions { get; private set; }
|
||||
|
||||
// optional parameters below.
|
||||
|
||||
/// <summary>
|
||||
/// Opts-out of running within the surrogate process DllHost.exe.
|
||||
/// This will reduce robustness and security.
|
||||
/// This value should be true if the provider does not implement <typeparamref name="IThumbnailFromStream"/>.
|
||||
/// </summary>
|
||||
// Note: The msdn documentation and property name are contradicting.
|
||||
// http://msdn.microsoft.com/en-us/library/cc144118(VS.85).aspx
|
||||
public bool DisableProcessIsolation { get; set; } // If true: Makes it run IN PROCESS.
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Below this size thumbnail images will not be generated - file icons will be used instead.
|
||||
/// </summary>
|
||||
public ThumbnailCutoffSize ThumbnailCutoff { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A resource reference string pointing to the icon to be used as an overlay on the bottom right of the thumbnail.
|
||||
/// ex. ISVComponent.dll@,-155
|
||||
/// ex. C:\Windows\System32\SampleIcon.ico
|
||||
/// If an empty string is provided, no overlay will be used.
|
||||
/// If the property is set to null, the default icon for the associated icon will be used as an overlay.
|
||||
/// </summary>
|
||||
public string TypeOverlay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the <typeparamref name="ThumbnailAdornment"/> for the thumbnail.
|
||||
/// <remarks>
|
||||
/// Only 32bpp bitmaps support adornments.
|
||||
/// While 24bpp bitmaps will be displayed, their adornments will not.
|
||||
/// If an adornment is specified by the file-type's associated application,
|
||||
/// the applications adornment will override the value specified in this registration.</remarks>
|
||||
/// </summary>
|
||||
public ThumbnailAdornment ThumbnailAdornment { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the minimum thumbnail size for which thumbnails will be generated.
|
||||
/// </summary>
|
||||
public enum ThumbnailCutoffSize
|
||||
{
|
||||
/// <summary>
|
||||
/// Default size of 20x20
|
||||
/// </summary>
|
||||
Square20 = -1, //For 20x20, you do not add any key in the registry
|
||||
|
||||
/// <summary>
|
||||
/// Size of 32x32
|
||||
/// </summary>
|
||||
Square32 = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Size of 16x16
|
||||
/// </summary>
|
||||
Square16 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Size of 48x48
|
||||
/// </summary>
|
||||
Square48 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Size of 16x16. An alternative to Square16.
|
||||
/// </summary>
|
||||
Square16B = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adornment applied to thumbnails.
|
||||
/// </summary>
|
||||
public enum ThumbnailAdornment
|
||||
{
|
||||
/// <summary>
|
||||
/// This will use the associated application's default icon as the adornment.
|
||||
/// </summary>
|
||||
Default = -1, // Default behaviour for no value added in registry
|
||||
|
||||
/// <summary>
|
||||
/// No adornment
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Drop shadow adornment
|
||||
/// </summary>
|
||||
DropShadow = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Photo border adornment
|
||||
/// </summary>
|
||||
PhotoBorder = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Video sprocket adornment
|
||||
/// </summary>
|
||||
VideoSprockets = 3
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user