Setting Decimal value on PropVariant

16 באוקטובר 2009

PROPVARIANT is Important
PROPVARIANT is an important COM structure that is used in many windows shell features like: Shell Namespace Extensions, Taskbar Jump List, Sensors and Windows Ribbon Framework, just to name a few.

In order to use these shell features in managed code, one must provide a .NET wrapper for this struct.
Now, you might have expected that if this struct is so important, there would be good .NET wrappers for it..

Unfortunately, this is not the case. The problem with this struct is that it is pretty complicated. It’s constructed from some simple fields and a union of ALL COM types. This includes 73 (!) types, from simple native types till SAFEARRAYs (COM arrays) of COM object types. So in order to marshal this type properly between native-managed boundaries, one must handle marshalling according to the actual union type (marshalling an integer is different than marshalling a string and different than marshalling a pointer to an array of objects).

Enter VT_DECIMAL
One more thing that makes it hard to implement a managed version is that PROPVARIANT itself has a special treatment for one type, namely DECIMAL.

PROPVARIANT structure has a size of 20 bits (on 32 bits systems), its definition is something like:

struct PROPVARIANT
{
    VARTYPE vt;
    PROPVAR_PAD1 wReserved1;
    PROPVAR_PAD2 wReserved2;
    PROPVAR_PAD3 wReserved3;
    union
    {
        CHAR cVal;
        UCHAR bVal;
        INT intVal;
        UINT uintVal;
        // … you got the point …
        BSTR *pbstrVal;
        IUnknown **ppunkVal;
        IDispatch **ppdispVal;
        LPSAFEARRAY *pparray;
    }
}

The VARTYPE member specify the type of value the struct currently hold.
Now, If the VARTYPE equals VT_DECIMAL then the entire structure (including the VARTYPE part itself!) is being read as if it was a DECIMAL structure. Let me stress that again: On VT_DECIMAL the VARTYPE-part of the struct overlaps the DECIMAL structure.

The reason why this can work is that VARTYPE has a size of 2 bytes and it turns out that the first 2 bytes of the DECIMAL structure are reserved. So the values of the DECIMAL type and VARTYPE member can coexist.

struct DECIMAL
{
    USHORT wReserved;
    BYTE scale;
    BYTE sign;
    ULONG Hi32;
    ULONGLONG Lo64;
}

Current Implementations
The main point you should have understand until this point is that implementing a managed wrapper for PROPVARIANT is hard. Specifically if you want to support many types.

Google PROPVARIANT finds several implementations, which supports only a limited set of types.

The best implementation I came across is the one in Windows API Code Pack, which is itself based on a previous work by Adam Root.

In Adam’s implementation the VT_DECIMAL case is not handled. In contrast, Windows API Code Pack v1.0 has an implementation but unfortunately it is quite buggy. Let’s check it together:

public void SetDecimal(decimal value)
{
    valueType = (ushort)VarEnum.VT_DECIMAL;
    valueData = Marshal.AllocCoTaskMem(Marshal.SizeOf(VarEnum.VT_DECIMAL));
    Marshal.StructureToPtr(value, valueData, false);
}

The first problem is the call for Marshal.SizeOf on an enum value. If this was possible, it would have return the size of the enum, which is 4 bytes. Not the size of a DECIMAL struct (20).

The second problem is that Marshal.SizeOf can’t be called on an Enum, it immediately throws an ArgumentException with: “Type 'System.Runtime.InteropServices.VarEnum' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed. “.

The third problem is in the concept. Setting a DECIMAL into a PROPVARIANT should not allocate any memory. The memory is already there, just copy the DECIMAL value onto the PROPVARIANT place.
The valueData element should not hold a pointer to the decimal. It should contain part of the decimal itself!

Our Implementation
Our solution for setting a decimal into the PROPVARIANT structure is quite simple. It uses our knowledge on the exact structure of both DOUBLE and PROPVARIANT structures. The following code should be part of the PropVariant.cs file in Windows API Code Pack:

public void SetDecimal(decimal value)

{

    int[] bits = Decimal.GetBits(value);

    valueData = (IntPtr)bits[0];

    valueDataExt = bits[1];

    wReserved3 = (ushort)(bits[2] >> 16);

    wReserved2 = (ushort)(bits[2] & 0x0000FFFF);

    wReserved1 = (ushort)(bits[3] >> 16);

    valueType = (ushort)VarEnum.VT_DECIMAL;

}

 

decimal decVal // DECIMAL decVal

{

    get

    {

        int[] bits = new int[4];

        bits[0] = (int)valueData;

        bits[1] = valueDataExt;

        bits[2] = (wReserved3 << 16) | wReserved2;

        bits[3] = (wReserved1 << 16);

        return new decimal(bits);

    }

}

What we do is taking the decimal apart, and setting the PropVariant fields according to the correct layout.

Now, since I don’t want to create my own version of Windows API Code Pack, I wrote a similar implementation that changes the private PropVariant fields from outside using reflection. Not the best piece of code but has the advantage of working on top of Windows API Code Pack v1.0. Hopefully they will fix it in a future version.

By the way, extension methods won’t work here since the PropVariant is defined as a struct and so is immutable, so making the extension method change its internals won’t have an affect on the original PropVariant. (Also, no “ref this PropVariant propVariant” is possible).

Here is the less intrusive version:

public static PropVariant GetPropVariant(decimal decimalValue)
{
    // get decimal binary representation
    int[] bits = Decimal.GetBits(decimalValue);
    FieldInfo field;
    BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
    
    // setting private members using FieldInfo.SetValue works on class, but not on struct
    object objPropVariant = new PropVariant();
    
    // set PropVariant internal private members using reflection,
    // according to decimal binary representation
    field = typeof(PropVariant).GetField("valueData", bindingFlags);
    field.SetValue(objPropVariant, (IntPtr)bits[0]);
    field = typeof(PropVariant).GetField("valueDataExt", bindingFlags);
    field.SetValue(objPropVariant, bits[1]);
    field = typeof(PropVariant).GetField("wReserved3", bindingFlags);
    field.SetValue(objPropVariant, (ushort)(bits[2] >> 16));
    field = typeof(PropVariant).GetField("wReserved2", bindingFlags);
    field.SetValue(objPropVariant, (ushort)(bits[2] & 0x0000FFFF));
    field = typeof(PropVariant).GetField("wReserved1", bindingFlags);
    field.SetValue(objPropVariant, (ushort)(bits[3] >> 16));
    field = typeof(PropVariant).GetField("valueType", bindingFlags);
    field.SetValue(objPropVariant, (ushort)VarEnum.VT_DECIMAL);
    return (PropVariant)objPropVariant;
}

public static decimal GetDecimal(PropVariant propVariant)
{
    FieldInfo field;
    BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
    object objPropVariant = propVariant;
    
    // get PropVariant internal private members using reflection,
    // according to decimal binary representation
    IntPtr valueData;
    Int32 valueDataExt;
    UInt16 wReserved3;
    UInt16 wReserved2;
    UInt16 wReserved1;
    field = typeof(PropVariant).GetField("valueData", bindingFlags);
    valueData = (IntPtr)field.GetValue(objPropVariant);
    field = typeof(PropVariant).GetField("valueDataExt", bindingFlags);
    valueDataExt = (Int32)field.GetValue(objPropVariant);
    field = typeof(PropVariant).GetField("wReserved3", bindingFlags);
    wReserved3 = (UInt16)field.GetValue(objPropVariant);
    field = typeof(PropVariant).GetField("wReserved2", bindingFlags);
    wReserved2 = (UInt16)field.GetValue(objPropVariant);
    field = typeof(PropVariant).GetField("wReserved1", bindingFlags);
    wReserved1 = (UInt16)field.GetValue(objPropVariant);
    int[] bits = new int[4];
    bits[0] = (int)valueData;
    bits[1] = valueDataExt;
    bits[2] = (wReserved3 << 16) | wReserved2;
    bits[3] = (wReserved1 << 16);
    return new decimal(bits);
}

For the sake of completeness I provide PropVariant.cs, which is based on Windows API Code Pack v1.0 version with my fix for proper support for decimal values. Note that it was tested only on a 32bit machine.

Update: As I’ve mentioned at the end of the post Windows Ribbon for WinForms, Part 13 – DropDownColorPicker, I’ve created a newer version of PropVariant class which is a combination of both Windows API Code Pack version and PreviewRibbon version.

That’s it for now,
Arik Poznanski.

kick it on DotNetKicks.com Shout it

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים