diff --git a/Samples/InPlaceMetadataEncoding/Program.cs b/Samples/InPlaceMetadataEncoding/Program.cs index 67fbfc5..47b1228 100644 --- a/Samples/InPlaceMetadataEncoding/Program.cs +++ b/Samples/InPlaceMetadataEncoding/Program.cs @@ -35,8 +35,8 @@ static void Main(string[] args) { Console.WriteLine("The file format does not support the requested metadata."); } - catch (Exception ex) when (ex.HResult == WinCodecError.TOOMUCHMETADATA - || ex.HResult == WinCodecError.INSUFFICIENTBUFFER + catch (Exception ex) when (ex.HResult == WinCodecError.TOO_MUCH_METADATA + || ex.HResult == WinCodecError.INSUFFICIENT_BUFFER || ex.HResult == WinCodecError.IMAGE_METADATA_HEADER_UNKNOWN || ex.HResult == WinCodecError.UNSUPPORTED_OPERATION) { diff --git a/WIC/Constants/WinCodecError.cs b/WIC/Constants/WinCodecError.cs index e834166..bf51cd1 100644 --- a/WIC/Constants/WinCodecError.cs +++ b/WIC/Constants/WinCodecError.cs @@ -1,12 +1,17 @@ namespace WIC { + /// + /// + /// public static class WinCodecError { public const int PROPERTY_NOT_FOUND = unchecked((int)0x88982F40); public const int PROPERTY_NOT_SUPPORTED = unchecked((int)0x88982F41); public const int UNSUPPORTED_OPERATION = unchecked((int)0x88982F81); - public const int TOOMUCHMETADATA = unchecked((int)0x88982F52); - public const int INSUFFICIENTBUFFER = unchecked((int)0x88982F8C); + public const int TOO_MUCH_METADATA = unchecked((int)0x88982F52); + public const int INSUFFICIENT_BUFFER = unchecked((int)0x88982F8C); public const int IMAGE_METADATA_HEADER_UNKNOWN = unchecked((int)0x88982F63); + public const int COMPONENT_NOT_FOUND = unchecked((int)0x88982F50); + public const int CODEC_NO_THUMBNAIL = unchecked((int)0x88982F44); } } diff --git a/WIC/Enumerations/ExifColorSpace.cs b/WIC/Enumerations/ExifColorSpace.cs index ebf6047..b9632f0 100644 --- a/WIC/Enumerations/ExifColorSpace.cs +++ b/WIC/Enumerations/ExifColorSpace.cs @@ -3,6 +3,6 @@ public enum ExifColorSpace : int { SRGB = 1, - AdobeSRGB = 2, + AdobeRGB = 2, } } diff --git a/WIC/Extensions/IWICBitmapFrameDecodeExtensions.cs b/WIC/Extensions/IWICBitmapFrameDecodeExtensions.cs index fc27107..b95d546 100644 --- a/WIC/Extensions/IWICBitmapFrameDecodeExtensions.cs +++ b/WIC/Extensions/IWICBitmapFrameDecodeExtensions.cs @@ -12,7 +12,23 @@ public static IWICMetadataBlockReader AsMetadataBlockReader(this IWICBitmapFrame public static IWICColorContext[] GetColorContexts(this IWICBitmapFrameDecode bitmapFrameDecode) { - return FetchIntoBufferHelper.FetchArray(bitmapFrameDecode.GetColorContexts); + var wic = new WICImagingFactory(); + + bitmapFrameDecode.GetColorContexts(0, null, out int length); + + var colorContexts = new IWICColorContext[length]; + + if (length > 0) + { + for (int i = 0; i < length; i++) + { + colorContexts[i] = wic.CreateColorContext(); + } + + bitmapFrameDecode.GetColorContexts(length, colorContexts, out _); + } + + return colorContexts; } public static void Initialize(this IWICBitmapFrameEncode bitmapFrameEncode, IPropertyBag2 pIEncoderOptions = null) diff --git a/WIC/Extensions/IWICImagingFactoryExtensions.cs b/WIC/Extensions/IWICImagingFactoryExtensions.cs index dd15bbd..405e8e6 100644 --- a/WIC/Extensions/IWICImagingFactoryExtensions.cs +++ b/WIC/Extensions/IWICImagingFactoryExtensions.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.IO; namespace WIC { @@ -45,14 +46,19 @@ public static IWICBitmapDecoder CreateDecoderFromFilename(this IWICImagingFactor } } - public static IWICBitmapDecoder CreateDecoderFromStream(this IWICImagingFactory imagingFactory, IStream pIStream, WICDecodeOptions metadataOptions, Guid? pguidVendor = null) + public static IWICBitmapDecoder CreateDecoderFromStream(this IWICImagingFactory imagingFactory, IStream stream, WICDecodeOptions metadataOptions, Guid? pguidVendor = null) { using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) { - return imagingFactory.CreateDecoderFromStream(pIStream, pguidVendorPtr, metadataOptions); + return imagingFactory.CreateDecoderFromStream(stream, pguidVendorPtr, metadataOptions); } } + public static IWICBitmapDecoder CreateDecoderFromStream(this IWICImagingFactory imagingFactory, Stream stream, WICDecodeOptions metadataOptions, Guid? pguidVendor = null) + { + return imagingFactory.CreateDecoderFromStream(stream.AsCOMStream(), metadataOptions, pguidVendor); + } + public static IWICBitmapEncoder CreateEncoder(this IWICImagingFactory factory, Guid guidContainerFormat, Guid? pguidVendor = null) { using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) diff --git a/WIC/Extensions/IWICMetadataQueryReaderExtensions.cs b/WIC/Extensions/IWICMetadataQueryReaderExtensions.cs index cb9ef96..eb65689 100644 --- a/WIC/Extensions/IWICMetadataQueryReaderExtensions.cs +++ b/WIC/Extensions/IWICMetadataQueryReaderExtensions.cs @@ -1,19 +1,39 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; -using static WIC.PropVariantHelper; namespace WIC { [EditorBrowsable(EditorBrowsableState.Advanced)] public static class IWICMetadataQueryReaderExtensions { + /// + /// Retrieves the current path relative to the root metadata block. + /// + /// The current namespace location. + /// + /// If the query reader is relative to the top of the metadata hierarchy, it will return a single-char string. + ///
+ /// If the query reader is relative to a nested metadata block, this method will return the path to the current query reader. + ///
public static string GetLocation(this IWICMetadataQueryReader metadataQueryReader) { return FetchIntoBufferHelper.FetchString(metadataQueryReader.GetLocation); } - public static bool TryGetMetadataByName(this IWICMetadataQueryReader metadataQueryReader, string name, out T value) + /// + /// Retrieves the metadata block or item identified by a metadata query expression. + /// + /// The query expression to the requested metadata block or item. + /// The metadata block or item requested. + /// Thrown when the metadata block or item was not found (HRESULT 0x88982F40). + /// + /// GetMetadataByName uses metadata query expressions to access embedded metadata. For more information on the metadata query language, see the Metadata Query Language Overview. + ///
+ /// If multiple blocks or items exist that are expressed by the same query expression, the first metadata block or item found will be returned. + ///
+ public static object GetMetadataByName(this IWICMetadataQueryReader metadataQueryReader, string name) { if (metadataQueryReader is null) { @@ -28,17 +48,65 @@ public static bool TryGetMetadataByName(this IWICMetadataQueryReader metadata try { metadataQueryReader.GetMetadataByName(name, ref variant); - return TryDecode(ref variant, out value); + return PropVariantHelper.Decode(ref variant); + } + finally + { + PropVariantHelper.Dispose(ref variant); + } + } + + /// + /// Retrieves the metadata block or item identified by a metadata query expression. + /// + /// The query expression to the requested metadata block or item. + /// The metadata block or item requested or null if not found. + /// True when the metadata block or item was found, otherwise false. + /// + /// GetMetadataByName uses metadata query expressions to access embedded metadata. For more information on the metadata query language, see the Metadata Query Language Overview. + ///
+ /// If multiple blocks or items exist that are expressed by the same query expression, the first metadata block or item found will be returned. + ///
+ public static bool TryGetMetadataByName(this IWICMetadataQueryReader metadataQueryReader, string name, out object value) + { + if (metadataQueryReader is null) + { + throw new NullReferenceException(); + } + if (name is null) + { + throw new ArgumentNullException(nameof(name)); + } + + var variant = new PROPVARIANT(); + try + { + metadataQueryReader.GetMetadataByName(name, ref variant); + value = PropVariantHelper.Decode(ref variant); + return true; } catch (COMException ex) when (ex.ErrorCode == WinCodecError.PROPERTY_NOT_FOUND) { - value = default(T); + value = null; return false; } finally { - Dispose(ref variant); + PropVariantHelper.Dispose(ref variant); } } + + /// + /// Gets the names of all metadata items at the current relative location within the metadata hierarchy. + /// + /// An enumerable that contains query strings that can be used in the current . + /// + /// The retrieved enumerable only contains query strings for the metadata blocks and items in the current level of the hierarchy. + /// + public static IEnumerable GetNames(this IWICMetadataQueryReader metadataQueryReader) + { + return metadataQueryReader.GetEnumerator().AsEnumerable(); + } + } } diff --git a/WIC/Extensions/IWICMetadataQueryWriterExtensionsy.cs b/WIC/Extensions/IWICMetadataQueryWriterExtensionsy.cs index 4cec73e..3655f6f 100644 --- a/WIC/Extensions/IWICMetadataQueryWriterExtensionsy.cs +++ b/WIC/Extensions/IWICMetadataQueryWriterExtensionsy.cs @@ -7,14 +7,22 @@ namespace WIC [EditorBrowsable(EditorBrowsableState.Advanced)] public static class IWICMetadataQueryWriterExtensions { + /// + /// Sets a metadata item to a specific location. + /// + /// The name of the metadata item. + /// The metadata to set. + /// + /// SetMetadataByName uses metadata query expressions to set metadata. For more information on the metadata query language, see the Metadata Query Language Overview. + /// public static void SetMetadataByName(this IWICMetadataQueryWriter metadataQueryWriter, string name, object value) { if (metadataQueryWriter is null) { throw new NullReferenceException(); } - if (name is null) - { + if (name is null) + { throw new ArgumentNullException(nameof(name)); } if (value is null) @@ -29,7 +37,7 @@ public static void SetMetadataByName(this IWICMetadataQueryWriter metadataQueryW } finally { - variant.Dispose(); + PropVariantHelper.Dispose(ref variant); } } diff --git a/WIC/Helper/PropVariantHelper.cs b/WIC/Helper/PropVariantHelper.cs index 3314f32..870d119 100644 --- a/WIC/Helper/PropVariantHelper.cs +++ b/WIC/Helper/PropVariantHelper.cs @@ -13,7 +13,7 @@ static PropVariantHelper() { decoders = new Dictionary>() { - [VARTYPE.VT_BOOL] = variant => variant.UI2 == 0 ? false : true, + [VARTYPE.VT_BOOL] = variant => variant.UI2 != 0, [VARTYPE.VT_UI1] = variant => variant.UI1, [VARTYPE.VT_UI2] = variant => variant.UI2, [VARTYPE.VT_UI4] = variant => variant.UI4, @@ -31,7 +31,15 @@ static PropVariantHelper() [VARTYPE.VT_UNKNOWN] = variant => Marshal.GetObjectForIUnknown(variant.Ptr), [VARTYPE.VT_STREAM] = variant => Marshal.GetObjectForIUnknown(variant.Ptr), [VARTYPE.VT_STORAGE] = variant => Marshal.GetObjectForIUnknown(variant.Ptr), - [VARTYPE.VT_VECTOR] = DecodeVector, + [VARTYPE.VT_BLOB] = variant => + { + byte[] blob = new byte[variant.Vector.Length]; + if (variant.Vector.Length > 0) + { + Marshal.Copy(variant.Vector.Ptr, blob, 0, variant.Vector.Length); + } + return blob; + }, }; encoders = new Dictionary>() @@ -86,77 +94,64 @@ static PropVariantHelper() elementEncoders = new Dictionary>() { - [typeof(bool)] = (pointer, value) => Marshal.WriteInt16(pointer, (bool)value ? (short)1 : (short)0), - [typeof(byte)] = (pointer, value) => Marshal.WriteByte(pointer, (byte)value), - [typeof(ushort)] = (pointer, value) => Marshal.WriteInt16(pointer, (short)(ushort)value), - [typeof(uint)] = (pointer, value) => Marshal.WriteInt32(pointer, (int)(uint)value), - [typeof(ulong)] = (pointer, value) => Marshal.WriteInt64(pointer, (long)(ulong)value), - [typeof(sbyte)] = (pointer, value) => Marshal.WriteByte(pointer, (byte)(sbyte)value), - [typeof(short)] = (pointer, value) => Marshal.WriteInt16(pointer, (short)value), - [typeof(int)] = (pointer, value) => Marshal.WriteInt32(pointer, (int)value), - [typeof(long)] = (pointer, value) => Marshal.WriteInt64(pointer, (long)value), - [typeof(string)] = (pointer, value) => Marshal.WriteIntPtr(pointer, 0, Marshal.StringToCoTaskMemUni((string)value)), - [typeof(float)] = (pointer, value) => Marshal.WriteInt32(pointer, BitConverter.ToInt32(BitConverter.GetBytes((float)value), 0)), - [typeof(double)] = (pointer, value) => Marshal.WriteInt64(pointer, BitConverter.ToInt64(BitConverter.GetBytes((double)value), 0)) + [typeof(bool)] = (ptr, value) => Marshal.WriteInt16(ptr, (bool)value ? (short)1 : (short)0), + [typeof(byte)] = (ptr, value) => Marshal.WriteByte(ptr, (byte)value), + [typeof(ushort)] = (ptr, value) => Marshal.WriteInt16(ptr, (short)(ushort)value), + [typeof(uint)] = (ptr, value) => Marshal.WriteInt32(ptr, (int)(uint)value), + [typeof(ulong)] = (ptr, value) => Marshal.WriteInt64(ptr, (long)(ulong)value), + [typeof(sbyte)] = (ptr, value) => Marshal.WriteByte(ptr, (byte)(sbyte)value), + [typeof(short)] = (ptr, value) => Marshal.WriteInt16(ptr, (short)value), + [typeof(int)] = (ptr, value) => Marshal.WriteInt32(ptr, (int)value), + [typeof(long)] = (ptr, value) => Marshal.WriteInt64(ptr, (long)value), + [typeof(string)] = (ptr, value) => Marshal.WriteIntPtr(ptr, 0, Marshal.StringToCoTaskMemUni((string)value)), + [typeof(float)] = (ptr, value) => Marshal.WriteInt32(ptr, BitConverter.ToInt32(BitConverter.GetBytes((float)value), 0)), + [typeof(double)] = (ptr, value) => Marshal.WriteInt64(ptr, BitConverter.ToInt64(BitConverter.GetBytes((double)value), 0)) }; - Action disposePtr = variant => - { - Marshal.FreeCoTaskMem(variant.Ptr); - }; - Action disposeBSTR = variant => - { - Marshal.FreeBSTR(variant.Ptr); - }; - Action disposeComObject = variant => - { - Marshal.Release(variant.Ptr); - }; - disposers = new Dictionary>() + disposers = new Dictionary>() { - [VARTYPE.VT_LPSTR] = disposePtr, - [VARTYPE.VT_LPWSTR] = disposePtr, - [VARTYPE.VT_BSTR] = disposeBSTR, - [VARTYPE.VT_UNKNOWN] = disposeComObject, - [VARTYPE.VT_STREAM] = disposeComObject, - [VARTYPE.VT_STORAGE] = disposeComObject, - [VARTYPE.VT_VECTOR] = DisposeVector, + [VARTYPE.VT_LPSTR] = Marshal.FreeCoTaskMem, + [VARTYPE.VT_LPWSTR] = Marshal.FreeCoTaskMem, + [VARTYPE.VT_BSTR] = Marshal.FreeBSTR, + [VARTYPE.VT_UNKNOWN] = ptr => Marshal.Release(ptr), + [VARTYPE.VT_STREAM] = ptr => Marshal.Release(ptr), + [VARTYPE.VT_STORAGE] = ptr => Marshal.Release(ptr) }; } - private static readonly IReadOnlyDictionary> decoders; + private const VARTYPE VectorFlags = VARTYPE.VT_ARRAY | VARTYPE.VT_VECTOR | VARTYPE.VT_BYREF; + private static readonly IReadOnlyDictionary> decoders; private static readonly IReadOnlyDictionary> encoders; private static readonly IReadOnlyDictionary elementSizes; private static readonly IReadOnlyDictionary vectorTypes; private static readonly IReadOnlyDictionary> elementEncoders; + private static readonly IReadOnlyDictionary> disposers; - private static readonly IReadOnlyDictionary> disposers; - - public static bool TryDecode(ref PROPVARIANT variant, out T value) + public static object Decode(ref PROPVARIANT variant) { - const VARTYPE flagMask = VARTYPE.VT_ARRAY | VARTYPE.VT_VECTOR | VARTYPE.VT_BYREF; - bool hasFlag = (variant.Type & flagMask) != 0; - if ((hasFlag && decoders.TryGetValue(variant.Type & flagMask, out var decoder)) - || decoders.TryGetValue(variant.Type, out decoder)) + if ((variant.Type & VectorFlags) != 0) { - value = (T)decoder.Invoke(variant); - return true; + return DecodeVector(variant); + } + else if (decoders.TryGetValue(variant.Type, out var decoder)) + { + return decoder.Invoke(variant); } else { - value = default(T); - return false; + throw new NotSupportedException($"Can not decode value of type {variant.Type}."); } } public static PROPVARIANT Encode(object value) { var type = value.GetType(); + if (type.IsArray) { - return EncodeArray((ICollection)value, type.GetElementType()); + return EncodeVector((Array)value, type.GetElementType()); } if (encoders.TryGetValue(type, out var encoder)) { @@ -164,103 +159,87 @@ public static PROPVARIANT Encode(object value) } else { - throw new NotSupportedException("Value type is not supported"); + throw new NotSupportedException($"Can not encode value of type {value.GetType()}."); } } - public static PROPVARIANT EncodeArray(ICollection array, Type elementType) + public static void Dispose(ref PROPVARIANT variant) { - if (!elementEncoders.TryGetValue(elementType, out var elementEncoder)) + if ((variant.Type & VectorFlags) != 0) { - throw new NotSupportedException("Array element type is not supported"); + DisposeVector(variant); } - - int elementSize = elementSizes[elementType]; - - IntPtr vectorPtr = Marshal.AllocCoTaskMem(array.Count * elementSize); - IntPtr elementPtr = vectorPtr; - foreach (var value in array) + else if (disposers.TryGetValue(variant.Type, out var disposer)) { - elementEncoder(elementPtr, value); - elementPtr += elementSize; + disposer.Invoke(variant.Ptr); } - - return new PROPVARIANT() - { - Type = VARTYPE.VT_VECTOR | vectorTypes[elementType], - Vector = new PROPVARIANT_Vector() - { - Length = array.Count, - Ptr = vectorPtr - } - }; } - private static object DecodeVector(PROPVARIANT variant) + private static Array DecodeVector(PROPVARIANT variant) { Type elementType; int elementSize; Func elementDecoder; - switch (variant.Type & ~VARTYPE.VT_VECTOR) + switch (variant.Type & ~VectorFlags) { case VARTYPE.VT_I1: elementType = typeof(sbyte); - elementDecoder = ptr => (object)(sbyte)Marshal.ReadByte(ptr); + elementDecoder = ptr => (sbyte)Marshal.ReadByte(ptr); elementSize = 1; break; case VARTYPE.VT_I2: elementType = typeof(short); - elementDecoder = ptr => (object)Marshal.ReadInt16(ptr); + elementDecoder = ptr => Marshal.ReadInt16(ptr); elementSize = 2; break; case VARTYPE.VT_I4: elementType = typeof(int); - elementDecoder = ptr => (object)Marshal.ReadInt32(ptr); + elementDecoder = ptr => Marshal.ReadInt32(ptr); elementSize = 4; break; case VARTYPE.VT_I8: elementType = typeof(long); - elementDecoder = ptr => (object)Marshal.ReadInt64(ptr); + elementDecoder = ptr => Marshal.ReadInt64(ptr); elementSize = 8; break; case VARTYPE.VT_UI1: elementType = typeof(byte); - elementDecoder = ptr => (object)Marshal.ReadByte(ptr); + elementDecoder = ptr => Marshal.ReadByte(ptr); elementSize = 1; break; case VARTYPE.VT_UI2: elementType = typeof(ushort); - elementDecoder = ptr => (object)(ushort)Marshal.ReadInt16(ptr); + elementDecoder = ptr => (ushort)Marshal.ReadInt16(ptr); elementSize = 2; break; case VARTYPE.VT_UI4: elementType = typeof(uint); - elementDecoder = ptr => (object)(uint)Marshal.ReadInt32(ptr); + elementDecoder = ptr => (uint)Marshal.ReadInt32(ptr); elementSize = 4; break; case VARTYPE.VT_UI8: elementType = typeof(ulong); - elementDecoder = ptr => (object)(ulong)Marshal.ReadInt64(ptr); + elementDecoder = ptr => (ulong)Marshal.ReadInt64(ptr); elementSize = 8; break; case VARTYPE.VT_LPSTR: elementType = typeof(string); - elementDecoder = Marshal.PtrToStringAnsi; + elementDecoder = ptr => Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(ptr)); elementSize = IntPtr.Size; break; case VARTYPE.VT_LPWSTR: elementType = typeof(string); - elementDecoder = Marshal.PtrToStringUni; + elementDecoder = ptr => Marshal.PtrToStringUni(Marshal.ReadIntPtr(ptr)); elementSize = IntPtr.Size; break; @@ -268,7 +247,7 @@ private static object DecodeVector(PROPVARIANT variant) case VARTYPE.VT_STREAM: case VARTYPE.VT_STORAGE: elementType = typeof(object); - elementDecoder = Marshal.GetObjectForIUnknown; + elementDecoder = ptr => Marshal.GetObjectForIUnknown(Marshal.ReadIntPtr(ptr)); elementSize = IntPtr.Size; break; @@ -288,72 +267,50 @@ private static object DecodeVector(PROPVARIANT variant) return vector; } - public static void Dispose(ref PROPVARIANT variant) + public static PROPVARIANT EncodeVector(Array array, Type elementType) { - const VARTYPE flagMask = VARTYPE.VT_ARRAY | VARTYPE.VT_VECTOR | VARTYPE.VT_BYREF; - bool hasFlag = (variant.Type & flagMask) != (VARTYPE)0; - Action disposer; - if (disposers.TryGetValue(variant.Type, out disposer) - || (hasFlag && disposers.TryGetValue(variant.Type & flagMask, out disposer))) + if (!elementEncoders.TryGetValue(elementType, out var elementEncoder)) { - disposer.Invoke(variant); + throw new NotSupportedException($"Can not encode array of {elementType}."); } - variant = new PROPVARIANT(); - } - private static void DisposeVector(PROPVARIANT variant) - { - Action elementDisposer = null; + int elementSize = elementSizes[elementType]; - switch (variant.Type & ~VARTYPE.VT_VECTOR) + IntPtr vectorPtr = Marshal.AllocCoTaskMem(array.Length * elementSize); + IntPtr elementPtr = vectorPtr; + foreach (var value in array) { - case VARTYPE.VT_BOOL: - case VARTYPE.VT_I1: - case VARTYPE.VT_I2: - case VARTYPE.VT_I4: - case VARTYPE.VT_I8: - case VARTYPE.VT_UI1: - case VARTYPE.VT_UI2: - case VARTYPE.VT_UI4: - case VARTYPE.VT_UI8: - case VARTYPE.VT_R4: - case VARTYPE.VT_R8: - break; - - case VARTYPE.VT_BSTR: - elementDisposer = Marshal.FreeBSTR; - break; - - case VARTYPE.VT_LPSTR: - case VARTYPE.VT_LPWSTR: - elementDisposer = Marshal.FreeCoTaskMem; - break; - - case VARTYPE.VT_UNKNOWN: - case VARTYPE.VT_STREAM: - case VARTYPE.VT_STORAGE: - elementDisposer = ptr => { Marshal.Release(ptr); }; - break; - - default: - throw new NotImplementedException(); + elementEncoder(elementPtr, value); + elementPtr += elementSize; } - IntPtr vectorPtr = variant.Vector.Ptr; + return new PROPVARIANT() + { + Type = VARTYPE.VT_VECTOR | vectorTypes[elementType], + Vector = new PROPVARIANT_Vector() + { + Length = array.Length, + Ptr = vectorPtr + } + }; + } + private static void DisposeVector(PROPVARIANT variant) + { // if necessary, dispose each of the vector's elements: - if (elementDisposer != null) + if (disposers.TryGetValue(variant.Type & ~VectorFlags, out var disposer)) { - IntPtr elementPtr = vectorPtr; - for (int i = 0, n = variant.Vector.Length; i < n; ++i) + IntPtr elementPtr = variant.Vector.Ptr; + for (int i = 0; i < variant.Vector.Length; i++) { - elementDisposer.Invoke(Marshal.ReadIntPtr(elementPtr)); + IntPtr elementValuePtr = Marshal.ReadIntPtr(elementPtr); + disposer.Invoke(elementValuePtr); elementPtr += IntPtr.Size; } } // finally, dispose the vector array itself: - Marshal.FreeCoTaskMem(vectorPtr); + Marshal.FreeCoTaskMem(variant.Vector.Ptr); } } } diff --git a/WIC/Interfaces/IWICMetadataQueryReader.cs b/WIC/Interfaces/IWICMetadataQueryReader.cs index d3f2af9..f1c814b 100644 --- a/WIC/Interfaces/IWICMetadataQueryReader.cs +++ b/WIC/Interfaces/IWICMetadataQueryReader.cs @@ -3,22 +3,59 @@ namespace WIC { + /// + /// Exposes methods for retrieving metadata blocks and items from a decoder or its image frames using a metadata query expression. + /// [ComImport] [Guid(IID.IWICMetadataQueryReader)] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IWICMetadataQueryReader { + /// + /// Gets the metadata query readers container format. + /// + /// The metadata query readers cointainer format GUID. Guid GetContainerFormat(); + /// + /// Retrieves the current path relative to the root metadata block. + /// + /// The length of the wzNamespace buffer. + /// Pointer that receives the current namespace location. + /// The actual buffer length that was needed to retrieve the current namespace location. + /// + /// If you pass NULL to wzNamespace, GetLocation ignores cchMaxLength and returns the required buffer length to store the path in the variable that pcchActualLength points to. + ///
+ /// If the query reader is relative to the top of the metadata hierarchy, it will return a single-char string. + ///
+ /// If the query reader is relative to a nested metadata block, this method will return the path to the current query reader. + ///
void GetLocation( [In] int cchMaxLength, [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzNamespace, [Out] out int pcchActualLength); + /// + /// Retrieves the metadata block or item identified by a metadata query expression. + /// + /// The query expression to the requested metadata block or item. + /// When this method returns, contains the metadata block or item requested. + /// + /// GetMetadataByName uses metadata query expressions to access embedded metadata. For more information on the metadata query language, see the Metadata Query Language Overview. + ///
+ /// If multiple blocks or items exist that are expressed by the same query expression, the first metadata block or item found will be returned. + ///
void GetMetadataByName( [In, MarshalAs(UnmanagedType.LPWStr)] string wzName, [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + /// + /// Gets an enumerator of all metadata items at the current relative location within the metadata hierarchy. + /// + /// An enumerator that contains query strings that can be used in the current . + /// + /// The retrieved enumerator only contains query strings for the metadata blocks and items in the current level of the hierarchy. + /// IEnumString GetEnumerator(); } } diff --git a/WIC/Interfaces/IWICMetadataQueryWriter.cs b/WIC/Interfaces/IWICMetadataQueryWriter.cs index 30eb302..90cc289 100644 --- a/WIC/Interfaces/IWICMetadataQueryWriter.cs +++ b/WIC/Interfaces/IWICMetadataQueryWriter.cs @@ -3,6 +3,9 @@ namespace WIC { + /// + /// Exposes methods for setting or removing metadata blocks and items to an encoder or its image frames using a metadata query expression. + /// [ComImport] [Guid(IID.IWICMetadataQueryWriter)] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] @@ -25,10 +28,31 @@ public interface IWICMetadataQueryWriter : IWICMetadataQueryReader #endregion + /// + /// Sets a metadata item to a specific location. + /// + /// The name of the metadata item. + /// The metadata to set. + /// + /// SetMetadataByName uses metadata query expressions to set metadata. For more information on the metadata query language, see the Metadata Query Language Overview. + ///
+ /// If the value set is a nested metadata block then use variant type VT_UNKNOWN and pvarValue pointing to the IWICMetadataQueryWriter of the new metadata block. + ///
+ /// The ordering of metadata items is at the discretion of the query writer since relative locations are not specified. + ///
void SetMetadataByName( [In, MarshalAs(UnmanagedType.LPWStr)] string wzName, [In, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + /// + /// Removes a metadata item from a specific location using a metadata query expression. + /// + /// The name of the metadata item to remove. + /// + /// RemoveMetadataByName uses metadata query expressions to remove metadata. For more information on the metadata query language, see the Metadata Query Language Overview. + ///
+ /// If the metadata item is a metadata block, it is removed from the metadata hierarchy. + ///
void RemoveMetadataByName( [In, MarshalAs(UnmanagedType.LPWStr)] string wzName); } diff --git a/WIC/Structures/PROPVARIANT.cs b/WIC/Structures/PROPVARIANT.cs index a807b34..d841258 100644 --- a/WIC/Structures/PROPVARIANT.cs +++ b/WIC/Structures/PROPVARIANT.cs @@ -4,7 +4,7 @@ namespace WIC { [StructLayout(LayoutKind.Explicit, Pack = 8)] - public struct PROPVARIANT : IDisposable + public struct PROPVARIANT { [FieldOffset(0)] public VARTYPE Type; @@ -33,12 +33,6 @@ public struct PROPVARIANT : IDisposable [FieldOffset(8)] public ulong UI8; - [FieldOffset(8)] - public PROPVARIANT_SplitI8 SplitI8; - - [FieldOffset(8)] - public PROPVARIANT_SplitUI8 SplitUI8; - [FieldOffset(8)] public float R4; @@ -50,25 +44,6 @@ public struct PROPVARIANT : IDisposable [FieldOffset(8)] public PROPVARIANT_Vector Vector; - - public void Dispose() - { - PropVariantHelper.Dispose(ref this); - } - } - - [StructLayout(LayoutKind.Sequential, Pack = 4)] - public struct PROPVARIANT_SplitI8 - { - public int A; - public int B; - } - - [StructLayout(LayoutKind.Sequential, Pack = 4)] - public struct PROPVARIANT_SplitUI8 - { - public uint A; - public uint B; } [StructLayout(LayoutKind.Sequential)] diff --git a/WIC/WIC.csproj b/WIC/WIC.csproj index 6564227..f32488f 100644 --- a/WIC/WIC.csproj +++ b/WIC/WIC.csproj @@ -14,8 +14,12 @@ windows imaging image-processing windows-imaging-component wic com-interop bitmap docoding encoding WIC A .NET Standard library that makes the Windows Imaging Component (WIC) available to managed code so that it can be used in .NET Core, .NET Framework and UWP (.NET Native). The library provides a thin layer of abstractions and extension methods to make it easier to work with the raw WIC interface. - 1.0.0 - WIC.DotNet + 1.1.1 + WIC.DotNet + true + snupkg + true + true + $(MSBuildProjectDirectory)=C:\ -