Win32/Variant.cs (365 lines of code) (raw):
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Reflection;
namespace SharpGen.Runtime.Win32
{
/// <summary>
/// Variant COM.
/// </summary>
/// <unmanaged>PROPVARIANT</unmanaged>
[StructLayout(LayoutKind.Sequential)]
public struct Variant
{
private ushort vt;
private ushort reserved1;
private ushort reserved2;
private ushort reserved3;
private VariantValue variantValue;
/// <summary>
/// Gets the type of the element.
/// </summary>
/// <value>
/// The type of the element.
/// </value>
public VariantElementType ElementType
{
get
{
return (VariantElementType)(vt & 0x0fff);
}
set
{
vt = (ushort)((vt & 0xf000) | (ushort)value);
}
}
/// <summary>
/// Gets the type.
/// </summary>
public VariantType Type
{
get
{
return (VariantType)(vt & 0xf000);
}
set
{
vt = (ushort)((vt & 0x0fff) | (ushort)value);
}
}
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
public unsafe object Value
{
get
{
switch (Type)
{
case VariantType.Default:
switch (ElementType)
{
case VariantElementType.Empty:
case VariantElementType.Null:
return null;
case VariantElementType.Blob:
{
var buffer = new byte[(int)variantValue.recordValue.RecordInfo];
if (buffer.Length > 0)
{
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<byte>(buffer), buffer.Length);
}
return buffer;
}
case VariantElementType.Bool:
return variantValue.intValue != 0;
case VariantElementType.Byte:
return variantValue.signedByteValue;
case VariantElementType.UByte:
return variantValue.byteValue;
case VariantElementType.UShort:
return variantValue.ushortValue;
case VariantElementType.Short:
return variantValue.shortValue;
case VariantElementType.UInt:
case VariantElementType.UInt1:
return variantValue.uintValue;
case VariantElementType.Int:
case VariantElementType.Int1:
return variantValue.intValue;
case VariantElementType.ULong:
return variantValue.ulongValue;
case VariantElementType.Long:
return variantValue.longValue;
case VariantElementType.Float:
return variantValue.floatValue;
case VariantElementType.Double:
return variantValue.doubleValue;
case VariantElementType.BinaryString:
throw new NotSupportedException();
case VariantElementType.StringPointer:
return Marshal.PtrToStringAnsi(variantValue.pointerValue);
case VariantElementType.WStringPointer:
return Marshal.PtrToStringUni(variantValue.pointerValue);
case VariantElementType.ComUnknown:
case VariantElementType.Dispatch:
return new ComObject(variantValue.pointerValue);
case VariantElementType.IntPointer:
case VariantElementType.Pointer:
return variantValue.pointerValue;
case VariantElementType.FileTime:
return DateTime.FromFileTime(variantValue.longValue);
default:
return null;
}
case VariantType.Vector:
var size = (int)variantValue.recordValue.RecordInfo;
switch (ElementType)
{
case VariantElementType.Bool:
unsafe
{
var array = stackalloc RawBool[size];
var span = new ReadOnlySpan<RawBool>(array, size);
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, span, size);
return RawBoolHelpers.ConvertToBoolArray(span);
}
case VariantElementType.Byte:
{
var array = new sbyte[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<sbyte>(array), size);
return array;
}
case VariantElementType.UByte:
{
var array = new byte[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<byte>(array), size);
return array;
}
case VariantElementType.UShort:
{
var array = new ushort[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<ushort>(array), size);
return array;
}
case VariantElementType.Short:
{
var array = new short[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<short>(array), size);
return array;
}
case VariantElementType.UInt:
case VariantElementType.UInt1:
{
var array = new uint[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<uint>(array), size);
return array;
}
case VariantElementType.Int:
case VariantElementType.Int1:
{
var array = new int[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<int>(array), size);
return array;
}
case VariantElementType.ULong:
{
var array = new ulong[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<ulong>(array), size);
return array;
}
case VariantElementType.Long:
{
var array = new long[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<long>(array), size);
return array;
}
case VariantElementType.Float:
{
var array = new float[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<float>(array), size);
return array;
}
case VariantElementType.Double:
{
var array = new double[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<double>(array), size);
return array;
}
case VariantElementType.BinaryString:
{
throw new NotSupportedException();
}
case VariantElementType.StringPointer:
{
var array = new string[size];
for (int i = 0; i < size; i++)
array[i] = Marshal.PtrToStringAnsi(((IntPtr*)variantValue.recordValue.RecordPointer)[i]);
return array;
}
case VariantElementType.WStringPointer:
{
var array = new string[size];
for (int i = 0; i < size; i++)
array[i] = Marshal.PtrToStringUni(((IntPtr*)variantValue.recordValue.RecordPointer)[i]);
return array;
}
case VariantElementType.ComUnknown:
case VariantElementType.Dispatch:
{
var comArray = new ComObject[size];
for (int i = 0; i < size; i++)
comArray[i] = new ComObject(((IntPtr*)variantValue.recordValue.RecordPointer)[i]);
return comArray;
}
case VariantElementType.IntPointer:
case VariantElementType.Pointer:
{
var array = new IntPtr[size];
MemoryHelpers.Read(variantValue.recordValue.RecordPointer, new ReadOnlySpan<IntPtr>(array), size);
return array;
}
case VariantElementType.FileTime:
{
var fileTimeArray = new DateTime[size];
for (int i = 0; i < size; i++)
fileTimeArray[i] = DateTime.FromFileTime(((long*)variantValue.recordValue.RecordPointer)[i]);
return fileTimeArray;
}
default:
return null;
}
}
return null;
}
set
{
if (value == null)
{
Type = VariantType.Default;
ElementType = VariantElementType.Null;
return;
}
var type = value.GetType();
Type = VariantType.Default;
if (type.GetTypeInfo().IsPrimitive)
{
if (type == typeof(byte))
{
ElementType = VariantElementType.UByte;
variantValue.byteValue = (byte)value;
return;
}
if (type == typeof(sbyte))
{
ElementType = VariantElementType.Byte;
variantValue.signedByteValue = (sbyte)value;
return;
}
if (type == typeof(int))
{
ElementType = VariantElementType.Int;
variantValue.intValue = (int)value;
return;
}
if (type == typeof(uint))
{
ElementType = VariantElementType.UInt;
variantValue.uintValue = (uint)value;
return;
}
if (type == typeof(long))
{
ElementType = VariantElementType.Long;
variantValue.longValue= (long)value;
return;
}
if (type == typeof(ulong))
{
ElementType = VariantElementType.ULong;
variantValue.ulongValue = (ulong)value;
return;
}
if (type == typeof(short))
{
ElementType = VariantElementType.Short;
variantValue.shortValue= (short)value;
return;
}
if (type == typeof(ushort))
{
ElementType = VariantElementType.UShort;
variantValue.ushortValue = (ushort)value;
return;
}
if (type == typeof(float))
{
ElementType = VariantElementType.Float;
variantValue.floatValue = (float)value;
return;
}
if (type == typeof(double))
{
ElementType = VariantElementType.Double;
variantValue.doubleValue = (double)value;
return;
}
}
else if (value is ComObject obj)
{
ElementType = VariantElementType.ComUnknown;
variantValue.pointerValue = obj.NativePointer;
return;
}
else if (value is DateTime dateTime)
{
ElementType = VariantElementType.FileTime;
variantValue.longValue = dateTime.ToFileTime();
return;
}
else if (value is string str)
{
ElementType = VariantElementType.WStringPointer;
variantValue.pointerValue = Marshal.StringToCoTaskMemUni(str);
return;
}
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Type [{0}] is not handled", type.Name));
}
}
[StructLayout(LayoutKind.Explicit)]
private struct VariantValue
{
[FieldOffset(0)]
public byte byteValue;
[FieldOffset(0)]
public sbyte signedByteValue;
[FieldOffset(0)]
public ushort ushortValue;
[FieldOffset(0)]
public short shortValue;
[FieldOffset(0)]
public uint uintValue;
[FieldOffset(0)]
public int intValue;
[FieldOffset(0)]
public ulong ulongValue;
[FieldOffset(0)]
public long longValue;
[FieldOffset(0)]
public float floatValue;
[FieldOffset(0)]
public double doubleValue;
[FieldOffset(0)]
public IntPtr pointerValue;
[FieldOffset(0)]
public CurrencyValue currencyValue;
[FieldOffset(0)]
public RecordValue recordValue;
[StructLayout(LayoutKind.Sequential)]
public struct CurrencyLowHigh
{
public uint LowValue;
public int HighValue;
}
[StructLayout(LayoutKind.Explicit)]
public struct CurrencyValue
{
[FieldOffset(0)]
public CurrencyLowHigh LowHigh;
[FieldOffset(0)]
public long longValue;
}
[StructLayout(LayoutKind.Sequential)]
public struct RecordValue
{
public IntPtr RecordInfo;
public IntPtr RecordPointer;
}
};
}
}