|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
//
using MS.Internal;
using MS.Win32.PresentationCore;
using System.Runtime.InteropServices;
using MS.Internal.PresentationCore;
#pragma warning disable 1634, 1691 // suppressing PreSharp warnings
//
// This class wraps a PROPVARIANT type for interop with the unmanaged metadata APIs. Only
// the capabilities used by this API are supported (so for example, there is no SAFEARRAY, IDispatch,
// general VT_UNKNOWN support, etc.)
//
// The types are mapped to C# types as follows:
//
// byte <=> VT_UI1
// sbyte <=> VT_I1
// char <=> VT_LPSTR (size 1)
// ushort <=> VT_UI2
// short <=> VT_I2
// String <=> VT_LPWSTR
// uint <=> VT_UI4
// int <=> VT_I4
// UInt64 <=> VT_UI8
// Int64 <=> VT_I8
// float <=> VT_R4
// double <=> VT_R8
// Guid <=> VT_CLSID
// bool <=> VT_BOOL
// BitmapMetadata <=> VT_UNKNOWN (IWICMetadataQueryReader)
// BitmapMetadataBlob <=> VT_BLOB
//
// For array types:
//
// byte[] <=> VT_UI1|VT_VECTOR
// sbyte[] <=> VT_I1|VT_VECTOR
// char[] <=> VT_LPSTR (size is length of array - treated as ASCII string) - read back is String, use ToCharArray().
// char[][] <=> VT_LPSTR|VT_VECTOR (array of ASCII strings)
// ushort[] <=> VT_UI2|VT_VECTOR
// short[] <=> VT_I2|VT_VECTOR
// String[] <=> VT_LPWSTR|VT_VECTOR
// uint[] <=> VT_UI4|VT_VECTOR
// int[] <=> VT_I4|VT_VECTOR
// UInt64[] <=> VT_UI8|VT_VECTOR
// Int64[] <=> VT_I8|VT_VECTOR
// float[] <=> VT_R4|VT_VECTOR
// double[] <=> VT_R8|VT_VECTOR
// Guid[] <=> VT_CLSID|VT_VECTOR
// bool[] <=> VT_BOOL|VT_VECTOR
//
namespace System.Windows.Media.Imaging
{
#region PropVariant
[StructLayout(LayoutKind.Sequential, Pack=0)]
internal struct PROPARRAY
{
internal UInt32 cElems;
internal IntPtr pElems;
}
[StructLayout(LayoutKind.Explicit, Pack=1)]
internal struct PROPVARIANT
{
[FieldOffset(0)] internal ushort varType;
[FieldOffset(2)] internal ushort wReserved1;
[FieldOffset(4)] internal ushort wReserved2;
[FieldOffset(6)] internal ushort wReserved3;
[FieldOffset(8)] internal byte bVal;
[FieldOffset(8)] internal sbyte cVal;
[FieldOffset(8)] internal ushort uiVal;
[FieldOffset(8)] internal short iVal;
[FieldOffset(8)] internal UInt32 uintVal;
[FieldOffset(8)] internal Int32 intVal;
[FieldOffset(8)] internal UInt64 ulVal;
[FieldOffset(8)] internal Int64 lVal;
[FieldOffset(8)] internal float fltVal;
[FieldOffset(8)] internal double dblVal;
[FieldOffset(8)] internal short boolVal;
[FieldOffset(8)] internal IntPtr pclsidVal; //this is for GUID ID pointer
[FieldOffset(8)] internal IntPtr pszVal; //this is for ansi string pointer
[FieldOffset(8)] internal IntPtr pwszVal; //this is for Unicode string pointer
[FieldOffset(8)] internal IntPtr punkVal; //this is for punkVal (interface pointer)
[FieldOffset(8)] internal PROPARRAY ca;
[FieldOffset(8)] internal System.Runtime.InteropServices.ComTypes.FILETIME filetime;
/// <summary>
/// CopyBytes - Poor man's mem copy. Copies cbData from pbFrom to pbTo.
/// </summary>
/// <param name="pbTo"> byte* pointing to the "to" array.</param>
/// <param name="cbTo"> int - count of bytes of receiving buffer.</param>
/// <param name="pbFrom"> byte* pointing to the "from" array.</param>
/// <param name="cbFrom"> int - count of bytes to copy from buffer.</param>
private static unsafe void CopyBytes(
byte* pbTo,
int cbTo,
byte* pbFrom,
int cbFrom
)
{
if (cbFrom>cbTo)
{
throw new InvalidOperationException(SR.Image_InsufficientBufferSize);
}
byte* pCurFrom = (byte*)pbFrom;
byte* pCurTo = (byte*)pbTo;
for (int i = 0; i < cbFrom; i++)
{
pCurTo[i] = pCurFrom[i];
}
}
internal void InitVector(Array array, Type type, VarEnum varEnum)
{
Init(array, type, varEnum | VarEnum.VT_VECTOR);
}
internal void Init(Array array, Type type, VarEnum vt)
{
varType = (ushort) vt;
ca.cElems = 0;
ca.pElems = IntPtr.Zero;
int length = array.Length;
if (length > 0)
{
long size = Marshal.SizeOf(type) * length;
IntPtr destPtr =IntPtr.Zero;
GCHandle handle = new GCHandle();
try
{
destPtr = Marshal.AllocCoTaskMem((int) size);
handle = GCHandle.Alloc(array, GCHandleType.Pinned);
unsafe
{
CopyBytes((byte *) destPtr, (int)size, (byte *)handle.AddrOfPinnedObject(), (int)size);
}
ca.cElems = (uint)length;
ca.pElems = destPtr;
destPtr = IntPtr.Zero;
}
finally
{
if (handle.IsAllocated)
{
handle.Free();
}
if (destPtr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(destPtr);
}
}
}
}
internal void Init(String[] value, bool fAscii)
{
varType = (ushort) (fAscii ? VarEnum.VT_LPSTR : VarEnum.VT_LPWSTR);
varType |= (ushort) VarEnum.VT_VECTOR;
ca.cElems = 0;
ca.pElems = IntPtr.Zero;
int length = value.Length;
if (length > 0)
{
IntPtr destPtr = IntPtr.Zero;
int sizeIntPtr = 0;
unsafe
{
sizeIntPtr = sizeof(IntPtr);
}
long size = sizeIntPtr * length;
int index = 0;
try
{
IntPtr pString = IntPtr.Zero;
destPtr = Marshal.AllocCoTaskMem((int)size);
for (index=0; index<length; index++)
{
if (fAscii)
{
pString = Marshal.StringToCoTaskMemAnsi(value[index]);
}
else
{
pString = Marshal.StringToCoTaskMemUni(value[index]);
}
Marshal.WriteIntPtr(destPtr, (int)index*sizeIntPtr, pString);
}
ca.cElems = (uint) length;
ca.pElems = destPtr;
destPtr = IntPtr.Zero;
}
finally
{
if (destPtr != IntPtr.Zero)
{
for (int i=0; i<index; i++)
{
IntPtr pString = Marshal.ReadIntPtr(destPtr, i*sizeIntPtr);
Marshal.FreeCoTaskMem(pString);
}
Marshal.FreeCoTaskMem(destPtr);
}
}
}
}
internal void Init(object value)
{
if (value == null)
{
varType = (ushort)VarEnum.VT_EMPTY;
}
else if (value is Array)
{
Type type = value.GetType();
if (type == typeof(sbyte[]))
{
InitVector(value as Array, typeof(sbyte), VarEnum.VT_I1);
}
else if (type == typeof(byte[]))
{
InitVector(value as Array, typeof(byte), VarEnum.VT_UI1);
}
else if (value is char[])
{
varType = (ushort) VarEnum.VT_LPSTR;
pszVal = Marshal.StringToCoTaskMemAnsi(new String(value as char[]));
}
else if (value is char[][])
{
char[][] charArray = value as char[][];
String[] strArray = new String[charArray.GetLength(0)];
for (int i=0; i<charArray.Length; i++)
{
strArray[i] = new String(charArray[i] as char[]);
}
Init (strArray, true);
}
else if (type == typeof(short[]))
{
InitVector(value as Array, typeof(short), VarEnum.VT_I2);
}
else if (type == typeof(ushort[]))
{
InitVector(value as Array, typeof(ushort), VarEnum.VT_UI2);
}
else if (type == typeof(int[]))
{
InitVector(value as Array, typeof(int), VarEnum.VT_I4);
}
else if (type == typeof(uint[]))
{
InitVector(value as Array, typeof(uint), VarEnum.VT_UI4);
}
else if (type == typeof(Int64[]))
{
InitVector(value as Array, typeof(Int64), VarEnum.VT_I8);
}
else if (type == typeof(UInt64[]))
{
InitVector(value as Array, typeof(UInt64), VarEnum.VT_UI8);
}
else if (value is float[])
{
InitVector(value as Array, typeof(float), VarEnum.VT_R4);
}
else if (value is double[])
{
InitVector(value as Array, typeof(double), VarEnum.VT_R8);
}
else if (value is Guid[])
{
InitVector(value as Array, typeof(Guid), VarEnum.VT_CLSID);
}
else if (value is String[])
{
Init (value as String[], false);
}
else if (value is bool[])
{
bool[] boolArray =value as bool[];
short[] array = new short[boolArray.Length];
for (int i=0; i<boolArray.Length; i++)
{
array[i] = (short) (boolArray[i] ? -1 : 0);
}
InitVector(array, typeof(short), VarEnum.VT_BOOL);
}
else
{
throw new System.InvalidOperationException(SR.Image_PropertyNotSupported);
}
}
else
{
Type type = value.GetType();
if (value is String)
{
varType = (ushort) VarEnum.VT_LPWSTR;
pwszVal = Marshal.StringToCoTaskMemUni(value as String);
}
else if (type == typeof(sbyte))
{
varType = (ushort) VarEnum.VT_I1;
cVal = (sbyte) value;
}
else if (type == typeof(byte))
{
varType = (ushort) VarEnum.VT_UI1;
bVal = (byte) value;
}
else if (type == typeof(System.Runtime.InteropServices.ComTypes.FILETIME))
{
varType = (ushort)VarEnum.VT_FILETIME;
filetime = (System.Runtime.InteropServices.ComTypes.FILETIME)value;
}
else if (value is char)
{
varType = (ushort)VarEnum.VT_LPSTR;
pszVal = Marshal.StringToCoTaskMemAnsi(new String(stackalloc char[] { (char)value }));
}
else if (type == typeof(short))
{
varType = (ushort)VarEnum.VT_I2;
iVal = (short)value;
}
else if (type == typeof(ushort))
{
varType = (ushort)VarEnum.VT_UI2;
uiVal = (ushort)value;
}
else if (type == typeof(int))
{
varType = (ushort)VarEnum.VT_I4;
intVal = (int)value;
}
else if (type == typeof(uint))
{
varType = (ushort)VarEnum.VT_UI4;
uintVal = (uint)value;
}
else if (type == typeof(Int64))
{
varType = (ushort)VarEnum.VT_I8;
lVal = (Int64)value;
}
else if (type == typeof(UInt64))
{
varType = (ushort)VarEnum.VT_UI8;
ulVal = (UInt64)value;
}
else if (value is float)
{
varType = (ushort)VarEnum.VT_R4;
fltVal = (float)value;
}
else if (value is double)
{
varType = (ushort)VarEnum.VT_R8;
dblVal = (double)value;
}
else if (value is Guid)
{
byte[] guid = ((Guid)value).ToByteArray();
varType = (ushort)VarEnum.VT_CLSID;
pclsidVal = Marshal.AllocCoTaskMem(guid.Length);
Marshal.Copy(guid, 0, pclsidVal, guid.Length);
}
else if (value is bool)
{
varType = (ushort)VarEnum.VT_BOOL;
boolVal = (short)(((bool)value) ? -1 : 0);
}
else if (value is BitmapMetadataBlob)
{
Init((value as BitmapMetadataBlob).InternalGetBlobValue(), typeof(byte), VarEnum.VT_BLOB);
}
else if (value is BitmapMetadata)
{
IntPtr punkTemp = IntPtr.Zero;
BitmapMetadata metadata = value as BitmapMetadata;
SafeMILHandle metadataHandle = metadata.InternalMetadataHandle;
if (metadataHandle == null || metadataHandle.IsInvalid)
{
throw new NotImplementedException();
}
Guid wicMetadataQueryReader = MILGuidData.IID_IWICMetadataQueryReader;
HRESULT.Check(UnsafeNativeMethods.MILUnknown.QueryInterface(
metadataHandle,
ref wicMetadataQueryReader,
out punkTemp));
varType = (ushort)VarEnum.VT_UNKNOWN;
punkVal = punkTemp;
}
else
{
throw new System.InvalidOperationException(SR.Image_PropertyNotSupported);
}
}
}
internal void Clear()
{
VarEnum vt = (VarEnum) varType;
if ((vt & VarEnum.VT_VECTOR) != 0 || vt == VarEnum.VT_BLOB)
{
if (ca.pElems != IntPtr.Zero)
{
vt = vt & ~VarEnum.VT_VECTOR;
if (vt == VarEnum.VT_UNKNOWN)
{
IntPtr punkPtr = ca.pElems;
int sizeIntPtr = 0;
unsafe
{
sizeIntPtr = sizeof(IntPtr);
}
for (uint i=0; i<ca.cElems; i++)
{
#pragma warning suppress 6031 // Return value ignored on purpose.
UnsafeNativeMethods.MILUnknown.Release(Marshal.ReadIntPtr(punkPtr, (int) (i*sizeIntPtr)));
}
}
else if (vt == VarEnum.VT_LPWSTR || vt == VarEnum.VT_LPSTR)
{
IntPtr strPtr = ca.pElems;
int sizeIntPtr = 0;
unsafe
{
sizeIntPtr = sizeof(IntPtr);
}
for (uint i=0; i<ca.cElems; i++)
{
Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(strPtr, (int) (i*sizeIntPtr)));
}
}
Marshal.FreeCoTaskMem(ca.pElems);
}
}
else if (vt == VarEnum.VT_LPWSTR ||
vt == VarEnum.VT_LPSTR ||
vt == VarEnum.VT_CLSID)
{
Marshal.FreeCoTaskMem(pwszVal);
}
else if (vt == VarEnum.VT_UNKNOWN)
{
#pragma warning suppress 6031 // Return value ignored on purpose.
UnsafeNativeMethods.MILUnknown.Release(punkVal);
}
vt = VarEnum.VT_EMPTY;
}
internal object ToObject(object syncObject)
{
VarEnum vt = (VarEnum) varType;
if ((vt & VarEnum.VT_VECTOR) != 0)
{
switch (vt & (~VarEnum.VT_VECTOR))
{
case VarEnum.VT_EMPTY:
return null;
case VarEnum.VT_I1:
{
sbyte[] array = new sbyte[ca.cElems];
for (int i=0; i<ca.cElems; i++)
array[i] = (sbyte) Marshal.ReadByte(ca.pElems, i);
return array;
}
case VarEnum.VT_UI1:
{
byte[] array = new byte[ca.cElems];
Marshal.Copy(ca.pElems, array, 0, (int)ca.cElems);
return array;
}
case VarEnum.VT_I2:
{
short[] array = new short[ca.cElems];
Marshal.Copy(ca.pElems, array, 0, (int)ca.cElems);
return array;
}
case VarEnum.VT_UI2:
{
ushort[] array = new ushort[ca.cElems];
for (int i=0; i<ca.cElems; i++)
array[i] = (ushort) Marshal.ReadInt16(ca.pElems, i*sizeof(ushort));
return array;
}
case VarEnum.VT_I4:
{
int[] array = new int[ca.cElems];
Marshal.Copy(ca.pElems, array, 0, (int)ca.cElems);
return array;
}
case VarEnum.VT_UI4:
{
uint[] array = new uint[ca.cElems];
for (int i=0; i<ca.cElems; i++)
array[i] = (uint) Marshal.ReadInt32(ca.pElems, i*sizeof(uint));
return array;
}
case VarEnum.VT_I8:
{
Int64[] array = new Int64[ca.cElems];
Marshal.Copy(ca.pElems, array, 0, (int)ca.cElems);
return array;
}
case VarEnum.VT_UI8:
{
UInt64[] array = new UInt64[ca.cElems];
for (int i=0; i<ca.cElems; i++)
array[i] = (UInt64) Marshal.ReadInt64(ca.pElems, i*sizeof(UInt64));
return array;
}
case VarEnum.VT_R4:
{
float[] array = new float[ca.cElems];
Marshal.Copy(ca.pElems, array, 0, (int)ca.cElems);
return array;
}
case VarEnum.VT_R8:
{
double[] array = new double[ca.cElems];
Marshal.Copy(ca.pElems, array, 0, (int)ca.cElems);
return array;
}
case VarEnum.VT_BOOL:
{
bool[] array = new bool[ca.cElems];
for (int i=0; i<ca.cElems; i++)
array[i] = (bool) (Marshal.ReadInt16(ca.pElems, i*sizeof(ushort)) != 0);
return array;
}
case VarEnum.VT_CLSID:
{
Guid[] array = new Guid[ca.cElems];
for (int i=0; i<ca.cElems; i++)
{
byte[] guid = new byte[16];
Marshal.Copy(ca.pElems, guid, i*16, 16);
array[i] = new Guid(guid);
}
return array;
}
case VarEnum.VT_LPSTR:
{
String[] array = new String[ca.cElems];
int sizeIntPtr = 0;
unsafe
{
sizeIntPtr = sizeof(IntPtr);
}
for (int i=0; i<ca.cElems; i++)
{
IntPtr ptr = Marshal.ReadIntPtr(ca.pElems, i*sizeIntPtr);
array[i] = Marshal.PtrToStringAnsi(ptr);
}
return array;
}
case VarEnum.VT_LPWSTR:
{
String[] array = new String[ca.cElems];
int sizeIntPtr = 0;
unsafe
{
sizeIntPtr = sizeof(IntPtr);
}
for (int i=0; i<ca.cElems; i++)
{
IntPtr ptr = Marshal.ReadIntPtr(ca.pElems, i*sizeIntPtr);
array[i] = Marshal.PtrToStringUni(ptr);
}
return array;
}
case VarEnum.VT_UNKNOWN:
default:
break;
}
}
else
{
switch (vt)
{
case VarEnum.VT_EMPTY:
return null;
case VarEnum.VT_I1:
return cVal;
case VarEnum.VT_UI1:
return bVal;
case VarEnum.VT_I2:
return iVal;
case VarEnum.VT_UI2:
return uiVal;
case VarEnum.VT_I4:
return intVal;
case VarEnum.VT_UI4:
return uintVal;
case VarEnum.VT_I8:
return lVal;
case VarEnum.VT_UI8:
return ulVal;
case VarEnum.VT_R4:
return fltVal;
case VarEnum.VT_R8:
return dblVal;
case VarEnum.VT_FILETIME:
return filetime;
case VarEnum.VT_BOOL:
return (bool) (boolVal != 0);
case VarEnum.VT_CLSID:
byte[] guid = new byte[16];
Marshal.Copy(pclsidVal, guid, 0, 16);
return new Guid(guid);
case VarEnum.VT_LPSTR:
return Marshal.PtrToStringAnsi(pszVal);
case VarEnum.VT_LPWSTR:
return Marshal.PtrToStringUni(pwszVal);
case VarEnum.VT_BLOB:
{
byte[] blob = new byte[ca.cElems];
Marshal.Copy(ca.pElems, blob, 0, (int)ca.cElems);
return new BitmapMetadataBlob(blob);
}
case VarEnum.VT_UNKNOWN:
{
IntPtr queryHandle = IntPtr.Zero;
Guid guidIWICQueryWriter = MILGuidData.IID_IWICMetadataQueryWriter;
Guid guidIWICQueryReader = MILGuidData.IID_IWICMetadataQueryReader;
try
{
int hr = UnsafeNativeMethods.MILUnknown.QueryInterface(punkVal, ref guidIWICQueryWriter, out queryHandle);
if (hr == HRESULT.S_OK)
{
// It's a IWICMetadataQueryWriter interface - read and write
SafeMILHandle metadataHandle = new SafeMILHandle(queryHandle);
// To avoid releasing the queryHandle in finally.
queryHandle = IntPtr.Zero;
return new BitmapMetadata(metadataHandle, false, false, syncObject);
}
else
{
hr = UnsafeNativeMethods.MILUnknown.QueryInterface(punkVal, ref guidIWICQueryReader, out queryHandle);
if (hr == HRESULT.S_OK)
{
// It's a IWICMetadataQueryReader interface - read only
SafeMILHandle metadataHandle = new SafeMILHandle(queryHandle);
// To avoid releasing the queryHandle in finally.
queryHandle = IntPtr.Zero;
return new BitmapMetadata(metadataHandle, true, false, syncObject);
}
HRESULT.Check(hr);
}
}
finally
{
if (queryHandle != IntPtr.Zero)
{
UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref queryHandle);
}
}
break;
}
default:
break;
}
}
throw new System.NotSupportedException(SR.Image_PropertyNotSupported);
}
internal bool RequiresSyncObject
{
get
{
return (varType == (ushort) VarEnum.VT_UNKNOWN);
}
}
}
#endregion
}
|