PropVariant.cs


Back to Interop with PROPVARIANTs in .NET



namespace System.Runtime.InteropServices.ComTypes
{
   
using System;
   
using System.Runtime.InteropServices;

   
/// <summary>
   
/// Represents the OLE struct PROPVARIANT.
   
/// </summary>
   
/// <remarks>
   
/// Must call Clear when finished to avoid memory leaks. If you get the value of
   
/// a VT_UNKNOWN prop, an implicit AddRef is called, thus your reference will
   
/// be active even after the PropVariant struct is cleared.
   
/// </remarks>
    [StructLayout(LayoutKind.Sequential)]
   
public struct PropVariant
    {
       
#region struct fields

       
// The layout of these elements needs to be maintained.
       
//
       
// NOTE: We could use LayoutKind.Explicit, but we want
       
//       to maintain that the IntPtr may be 8 bytes on
       
//       64-bit architectures, so we'll let the CLR keep
       
//       us aligned.
       
//
       
// NOTE: In order to allow x64 compat, we need to allow for
       
//       expansion of the IntPtr. However, the BLOB struct
       
//       uses a 4-byte int, followed by an IntPtr, so
       
//       although the p field catches most pointer values,
       
//       we need an additional 4-bytes to get the BLOB
       
//       pointer. The p2 field provides this, as well as
       
//       the last 4-bytes of an 8-byte value on 32-bit
       
//       architectures.

       
// This is actually a VarEnum value, but the VarEnum type
       
// shifts the layout of the struct by 4 bytes instead of the
       
// expected 2.
        ushort vt;
       
ushort wReserved1;
       
ushort wReserved2;
       
ushort wReserved3;
        IntPtr p;
       
int p2;

       
#endregion // struct fields

       
#region union members

       
sbyte cVal // CHAR cVal;
        {
           
get { return (sbyte)GetDataBytes()[0]; }
        }

       
byte bVal // UCHAR bVal;
        {
           
get { return GetDataBytes()[0]; }
        }

       
short iVal // SHORT iVal;
        {
           
get { return BitConverter.ToInt16(GetDataBytes(), 0); }
        }

       
ushort uiVal // USHORT uiVal;
        {
           
get { return BitConverter.ToUInt16(GetDataBytes(), 0); }
        }

       
int lVal // LONG lVal;
        {
           
get { return BitConverter.ToInt32(GetDataBytes(), 0); }
        }

       
uint ulVal // ULONG ulVal;
        {
           
get { return BitConverter.ToUInt32(GetDataBytes(), 0); }
        }

       
long hVal // LARGE_INTEGER hVal;
        {
           
get { return BitConverter.ToInt64(GetDataBytes(), 0); }
        }

       
ulong uhVal // ULARGE_INTEGER uhVal;
        {
           
get { return BitConverter.ToUInt64(GetDataBytes(), 0); }
        }

       
float fltVal // FLOAT fltVal;
        {
           
get { return BitConverter.ToSingle(GetDataBytes(), 0); }
        }

       
double dblVal // DOUBLE dblVal;
        {
           
get { return BitConverter.ToDouble(GetDataBytes(), 0); }
        }

       
bool boolVal // VARIANT_BOOL boolVal;
        {
           
get { return (iVal == 0 ? false : true); }
        }

       
int scode // SCODE scode;
        {
           
get { return lVal; }
        }

       
decimal cyVal // CY cyVal;
        {
           
get { return decimal.FromOACurrency(hVal); }
        }

        DateTime date
// DATE date;
        {
           
get { return DateTime.FromOADate(dblVal); }
        }

       
#endregion // union members

       
/// <summary>
       
/// Gets a byte array containing the data bits of the struct.
       
/// </summary>
       
/// <returns>A byte array that is the combined size of the data bits.</returns>
        private byte[] GetDataBytes()
        {
           
byte[] ret = new byte[IntPtr.Size + sizeof(int)];
           
if (IntPtr.Size == 4)
                BitConverter.GetBytes(p.ToInt32()).CopyTo(ret,
0);
           
else if (IntPtr.Size == 8)
                BitConverter.GetBytes(p.ToInt64()).CopyTo(ret,
0);
            BitConverter.GetBytes(p2).CopyTo(ret, IntPtr.Size);
           
return ret;
        }

       
/// <summary>
       
/// Called to properly clean up the memory referenced by a PropVariant instance.
       
/// </summary>
        [DllImport("ole32.dll")]
       
private extern static int PropVariantClear(ref PropVariant pvar);

       
/// <summary>
       
/// Called to clear the PropVariant's referenced and local memory.
       
/// </summary>
       
/// <remarks>
       
/// You must call Clear to avoid memory leaks.
       
/// </remarks>
        public void Clear()
        {
           
// Can't pass "this" by ref, so make a copy to call PropVariantClear with
            PropVariant var = this;
            PropVariantClear(
ref var);

           
// Since we couldn't pass "this" by ref, we need to clear the member fields manually
           
// NOTE: PropVariantClear already freed heap data for us, so we are just setting
           
//       our references to null.
            vt = (ushort)VarEnum.VT_EMPTY;
            wReserved1
= wReserved2 = wReserved3 = 0;
            p
= IntPtr.Zero;
            p2
= 0;
        }

       
/// <summary>
       
/// Gets the variant type.
       
/// </summary>
        public VarEnum Type
        {
           
get { return (VarEnum)vt; }
        }

       
/// <summary>
       
/// Gets the variant value.
       
/// </summary>
        public object Value
        {
           
get
            {
               
// TODO: Add support for reference types (ie. VT_REF | VT_I1)
               
// TODO: Add support for safe arrays

               
switch ((VarEnum)vt)
                {
                   
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:
                   
case VarEnum.VT_INT:
                       
return lVal;
                   
case VarEnum.VT_UI4:
                   
case VarEnum.VT_UINT:
                       
return ulVal;
                   
case VarEnum.VT_I8:
                       
return hVal;
                   
case VarEnum.VT_UI8:
                       
return uhVal;
                   
case VarEnum.VT_R4:
                       
return fltVal;
                   
case VarEnum.VT_R8:
                       
return dblVal;
                   
case VarEnum.VT_BOOL:
                       
return boolVal;
                   
case VarEnum.VT_ERROR:
                       
return scode;
                   
case VarEnum.VT_CY:
                       
return cyVal;
                   
case VarEnum.VT_DATE:
                       
return date;
                   
case VarEnum.VT_FILETIME:
                       
return DateTime.FromFileTime(hVal);
                   
case VarEnum.VT_BSTR:
                       
return Marshal.PtrToStringBSTR(p);
                   
case VarEnum.VT_BLOB:
                       
byte[] blobData = new byte[lVal];
                        IntPtr pBlobData;
                       
if (IntPtr.Size == 4)
                        {
                            pBlobData
= new IntPtr(p2);
                        }
                       
else if (IntPtr.Size == 8)
                        {
                           
// In this case, we need to derive a pointer at offset 12,
                           
// because the size of the blob is represented as a 4-byte int
                           
// but the pointer is immediately after that.
                            pBlobData = new IntPtr(BitConverter.ToInt64(GetDataBytes(), sizeof(int)));
                        }
                       
else
                           
throw new NotSupportedException();
                        Marshal.Copy(pBlobData, blobData,
0, lVal);
                       
return blobData;
                   
case VarEnum.VT_LPSTR:
                       
return Marshal.PtrToStringAnsi(p);
                   
case VarEnum.VT_LPWSTR:
                       
return Marshal.PtrToStringUni(p);
                   
case VarEnum.VT_UNKNOWN:
                       
return Marshal.GetObjectForIUnknown(p);
                   
case VarEnum.VT_DISPATCH:
                       
return p;
                   
default:
                       
throw new NotSupportedException("The type of this variable is not support ('" + vt.ToString() + "')");
                }
            }
        }
    }
}

 

Skip to main content