initial
This commit is contained in:
		
							
								
								
									
										219
									
								
								Definitions/Reference.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								Definitions/Reference.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Runtime.InteropServices.ComTypes;
 | 
			
		||||
 | 
			
		||||
namespace PhotoThumbnail.Definitions
 | 
			
		||||
{
 | 
			
		||||
    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");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <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>
 | 
			
		||||
    /// 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. 
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Flags]
 | 
			
		||||
    public enum AccessModes
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Indicates that, in direct mode, each change to a storage 
 | 
			
		||||
        /// or stream element is written as it occurs.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Direct = 0x00000000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Indicates that, in transacted mode, changes are buffered 
 | 
			
		||||
        /// and written only if an explicit commit operation is called. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Transacted = 0x00010000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Provides a faster implementation of a compound file 
 | 
			
		||||
        /// in a limited, but frequently used, case. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Simple = 0x08000000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Indicates that the object is read-only, 
 | 
			
		||||
        /// meaning that modifications cannot be made.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Read = 0x00000000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Enables you to save changes to the object, 
 | 
			
		||||
        /// but does not permit access to its data. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Write = 0x00000001,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Enables access and modification of object data.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        ReadWrite = 0x00000002,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Specifies that subsequent openings of the object are 
 | 
			
		||||
        /// not denied read or write access. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        ShareDenyNone = 0x00000040,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Prevents others from subsequently opening the object in Read mode. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        ShareDenyRead = 0x00000030,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Prevents others from subsequently opening the object 
 | 
			
		||||
        /// for Write or ReadWrite access.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        ShareDenyWrite = 0x00000020,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Prevents others from subsequently opening the object in any mode. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        ShareExclusive = 0x00000010,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Opens the storage object with exclusive access to the most 
 | 
			
		||||
        /// recently committed version.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Priority = 0x00040000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 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. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        DeleteOnRelease = 0x04000000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 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. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        NoScratch = 0x00100000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Indicates that an existing storage object 
 | 
			
		||||
        /// or stream should be removed before the new object replaces it. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Create = 0x00001000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Creates the new object while preserving existing data in a stream named "Contents". 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Convert = 0x00020000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Causes the create operation to fail if an existing object with the specified name exists.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        FailIfThere = 0x00000000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 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. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        NoSnapshot = 0x00200000,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Supports direct mode for single-writer, multireader file operations. 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        DirectSingleWriterMultipleReader = 0x00400000
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // <summary>
 | 
			
		||||
    /// Thumbnail Alpha Types
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public enum ThumbnailAlphaType
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Let the system decide.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Unknown = 0,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// No transparency
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        NoAlphaChannel = 1,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Has transparency
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        HasAlphaChannel = 2,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ComVisible interface for native IThumbnailProvider
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [ComImport]
 | 
			
		||||
    [Guid("e357fccd-a995-4576-b01f-234630154e96")]
 | 
			
		||||
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
    interface IThumbnailProvider
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a pointer to a bitmap to display as a thumbnail
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="squareLength"></param>
 | 
			
		||||
        /// <param name="bitmapHandle"></param>
 | 
			
		||||
        /// <param name="bitmapType"></param>
 | 
			
		||||
        void GetThumbnail(uint squareLength, [Out] out IntPtr bitmapHandle, [Out] out uint bitmapType);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Provides means by which to initialize with a stream.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [ComImport]
 | 
			
		||||
    [Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f")]
 | 
			
		||||
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
    interface IInitializeWithStream
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes with a stream.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="stream"></param>
 | 
			
		||||
        /// <param name="fileMode"></param>
 | 
			
		||||
        void Initialize(IStream stream, AccessModes fileMode);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										265
									
								
								Definitions/StorageStream.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								Definitions/StorageStream.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,265 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Runtime.InteropServices.ComTypes;
 | 
			
		||||
 | 
			
		||||
namespace PhotoThumbnail.Definitions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// A wrapper for the native IStream object.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class StorageStream : Stream, IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        private IStream _stream;
 | 
			
		||||
        private readonly bool _isReadOnly;
 | 
			
		||||
 | 
			
		||||
        internal StorageStream(IStream stream, bool readOnly)
 | 
			
		||||
        {
 | 
			
		||||
            _stream = stream ?? throw new ArgumentNullException("stream");
 | 
			
		||||
            _isReadOnly = readOnly;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Reads a single byte from the stream, moving the current position ahead by 1.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>A single byte from the stream, -1 if end of stream.</returns>
 | 
			
		||||
        public override int ReadByte()
 | 
			
		||||
        {
 | 
			
		||||
            ThrowIfDisposed();
 | 
			
		||||
 | 
			
		||||
            byte[] buffer = new byte[1];
 | 
			
		||||
            if (Read(buffer, 0, 1) > 0)
 | 
			
		||||
            {
 | 
			
		||||
                return buffer[0];
 | 
			
		||||
            }
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes a single byte to the stream
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="value">Byte to write to stream</param>
 | 
			
		||||
        public override void WriteByte(byte value)
 | 
			
		||||
        {
 | 
			
		||||
            ThrowIfDisposed();
 | 
			
		||||
            byte[] buffer = new byte[] { value };
 | 
			
		||||
            Write(buffer, 0, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets whether the stream can be read from.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public override bool CanRead => _stream != null;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets whether seeking is supported by the stream.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public override bool CanSeek => _stream != null;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets whether the stream can be written to.
 | 
			
		||||
        /// Always false.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public override bool CanWrite => _stream != null && !_isReadOnly;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Reads a buffer worth of bytes from the stream.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">Buffer to fill</param>
 | 
			
		||||
        /// <param name="offset">Offset to start filling in the buffer</param>
 | 
			
		||||
        /// <param name="count">Number of bytes to read from the stream</param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public override int Read(byte[] buffer, int offset, int count)
 | 
			
		||||
        {
 | 
			
		||||
            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"); }
 | 
			
		||||
 | 
			
		||||
            int bytesRead = 0;
 | 
			
		||||
            if (count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                IntPtr ptr = Marshal.AllocCoTaskMem(sizeof(ulong));
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    if (offset == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        _stream.Read(buffer, count, ptr);
 | 
			
		||||
                        bytesRead = (int)Marshal.ReadInt64(ptr);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        byte[] tempBuffer = new byte[count];
 | 
			
		||||
                        _stream.Read(tempBuffer, count, ptr);
 | 
			
		||||
 | 
			
		||||
                        bytesRead = (int)Marshal.ReadInt64(ptr);
 | 
			
		||||
                        if (bytesRead > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            Array.Copy(tempBuffer, 0, buffer, offset, bytesRead);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    Marshal.FreeCoTaskMem(ptr);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return bytesRead;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes a buffer to the stream if able to do so.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">Buffer to write</param>
 | 
			
		||||
        /// <param name="offset">Offset in buffer to start writing</param>
 | 
			
		||||
        /// <param name="count">Number of bytes to write to the stream</param>
 | 
			
		||||
        public override void Write(byte[] buffer, int offset, int count)
 | 
			
		||||
        {
 | 
			
		||||
            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 (count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                IntPtr ptr = Marshal.AllocCoTaskMem(sizeof(ulong));
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    if (offset == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        _stream.Write(buffer, count, ptr);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        byte[] tempBuffer = new byte[count];
 | 
			
		||||
                        Array.Copy(buffer, offset, tempBuffer, 0, count);
 | 
			
		||||
                        _stream.Write(tempBuffer, count, ptr);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    Marshal.FreeCoTaskMem(ptr);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the length of the IStream
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public override long Length
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                ThrowIfDisposed();
 | 
			
		||||
                const int STATFLAG_NONAME = 1;
 | 
			
		||||
                _stream.Stat(out System.Runtime.InteropServices.ComTypes.STATSTG stats, STATFLAG_NONAME);
 | 
			
		||||
                return stats.cbSize;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the current position within the underlying IStream.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public override long Position
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                ThrowIfDisposed();
 | 
			
		||||
                return Seek(0, SeekOrigin.Current);
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                ThrowIfDisposed();
 | 
			
		||||
                Seek(value, SeekOrigin.Begin);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Seeks within the underlying IStream.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="offset">Offset</param>
 | 
			
		||||
        /// <param name="origin">Where to start seeking</param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public override long Seek(long offset, SeekOrigin origin)
 | 
			
		||||
        {
 | 
			
		||||
            ThrowIfDisposed();
 | 
			
		||||
            IntPtr ptr = Marshal.AllocCoTaskMem(sizeof(long));
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                _stream.Seek(offset, (int)origin, ptr);
 | 
			
		||||
                return Marshal.ReadInt64(ptr);
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                Marshal.FreeCoTaskMem(ptr);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Sets the length of the stream
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="value"></param>
 | 
			
		||||
        public override void SetLength(long value)
 | 
			
		||||
        {
 | 
			
		||||
            ThrowIfDisposed();
 | 
			
		||||
            _stream.SetSize(value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Commits data to be written to the stream if it is being cached.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public override void Flush()
 | 
			
		||||
        {
 | 
			
		||||
            _stream.Commit((int)StorageStreamCommitOptions.None);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Disposes the stream.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="disposing">True if called from Dispose(), false if called from finalizer.</param>
 | 
			
		||||
        protected override void Dispose(bool disposing)
 | 
			
		||||
        {
 | 
			
		||||
            _stream = null;
 | 
			
		||||
            base.Dispose(disposing);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThrowIfDisposed() { if (_stream == null) throw new ObjectDisposedException(GetType().Name); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Options for commiting (flushing) an IStream storage stream
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Flags]
 | 
			
		||||
    internal enum StorageStreamCommitOptions
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Uses default options
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        None = 0,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Overwrite option
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Overwrite = 1,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Only if current
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        OnlyIfCurrent = 2,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Commits to disk cache dangerously
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        DangerouslyCommitMerelyToDiskCache = 4,
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Consolidate
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        Consolidate = 8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										256
									
								
								Definitions/ThumbnailProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								Definitions/ThumbnailProvider.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,256 @@
 | 
			
		||||
using Microsoft.Win32;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
namespace PhotoThumbnail.Definitions
 | 
			
		||||
{
 | 
			
		||||
    public abstract class ThumbnailProvider : IThumbnailProvider, ICustomQueryInterface, IDisposable, IInitializeWithStream
 | 
			
		||||
    {
 | 
			
		||||
        private Bitmap GetBitmap(int sideLength)
 | 
			
		||||
        {
 | 
			
		||||
            if (_stream != null && this is IThumbnailFromStream stream)
 | 
			
		||||
            {
 | 
			
		||||
                return stream.ConstructBitmap(_stream, sideLength);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw new InvalidOperationException("ThumbnailProviderInterfaceNotImplemented: " + GetType().Name);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual ThumbnailAlphaType ThumbnailAlphaType => ThumbnailAlphaType.Unknown;
 | 
			
		||||
 | 
			
		||||
        private StorageStream _stream;
 | 
			
		||||
 | 
			
		||||
        #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))
 | 
			
		||||
            {
 | 
			
		||||
                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 == typeof(IThumbnailFromStream));
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            if (interfaces.Any(x => x == typeof(IThumbnailFromShellObject) || x == typeof(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("ThumbnailProviderDisabledProcessIsolation: " + type.Name);
 | 
			
		||||
                }
 | 
			
		||||
                interfaced = true;
 | 
			
		||||
            }
 | 
			
		||||
            */
 | 
			
		||||
 | 
			
		||||
            if (!interfaced)
 | 
			
		||||
            {
 | 
			
		||||
                throw new InvalidOperationException("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 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
									
								
								Definitions/ThumbnailProviderAttribute.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								Definitions/ThumbnailProviderAttribute.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace PhotoThumbnail.Definitions
 | 
			
		||||
{
 | 
			
		||||
    /// <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("name");
 | 
			
		||||
            Extensions = extensions ?? throw new ArgumentNullException("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