ItemPropertySet: accessing the Property System from managed code...

So, this code should eventually make it to the VistaBridge Windows SDK code sample (" no official support" ) . It's in C++/CLI as it uses macros and templates quite a lot...

It is really a beta version as I’d love to have other people use it and tell me what’s wrong or missing.

I’m including here the code without most property macro (they are 800+ !) but you can download the zip file and examine it.

As you can see below, you can access the properties by name or by PropertyKey (in case its’a custom one):

PropertyKey pk = new PropertyKey(new Guid("F29F85E0-4FF9-1068-AB91-08002B27B3D9"), 6);

using (ItemPropertySet ips = new ItemPropertySet(textboxPath.Text, GetPropertyStoreFlag.ReadWrite)) {

    object o = ips.GetValue(pk);

    string comment = o as string;

    System.Diagnostics.Debug.Assert(comment == ips.Comment);

    ips.Comment = "A first comment to test the writing of property";

    System.Diagnostics.Debug.Assert(ips.Comment == (string) ips.GetValue(pk));

}

using (ItemPropertySet ips = new ItemPropertySet(textboxPath.Text, GetPropertyStoreFlag.ReadWrite)) {

    ips.SetValue(pk, "A second comment to test the writing of property");

}

The C++/CLI code:

#include "stdafx.h"

using namespace System::Runtime::InteropServices;

using namespace System;

// ********************************************************************************

// *

// * The VariantType template class helps mapping the VARTYPE (e.g. VT_LPWSTR) to a

// * CLR type (e.g. String ^).

// *

// ********************************************************************************

template <typename T>

class VariantType {

public:

   const static VARTYPE vartype;

};

template <typename T>

const VARTYPE VariantType<T>::vartype = VT_ILLEGAL;

const VARTYPE VariantType<Int16>::vartype = VT_I2;

const VARTYPE VariantType<Int32>::vartype = VT_I4;

const VARTYPE VariantType<Byte>::vartype = VT_UI1;

const VARTYPE VariantType<UInt16>::vartype = VT_UI2;

const VARTYPE VariantType<UInt32>::vartype = VT_UI4;

const VARTYPE VariantType<UInt64>::vartype = VT_UI8;

const VARTYPE VariantType<Double>::vartype = VT_R8;

const VARTYPE VariantType<bool>::vartype = VT_BOOL;

const VARTYPE VariantType<ComTypes::FILETIME>::vartype = VT_FILETIME;

const VARTYPE VariantType<Guid>::vartype = VT_CLSID;

const VARTYPE VariantType<String ^>::vartype = VT_LPWSTR;

// ********************************************************************************

// *

// * Overloaded GetValueFromPropertyVariant functions to help with the mapping from PROPVARIANT.

// *

// ********************************************************************************

static HRESULT GetValue(const PROPVARIANT & propertyVariant, Byte % value) {

   if (propertyVariant.vt == VT_UI1) {

      value = propertyVariant.bVal;

      return S_OK;

   }

   return E_INVALIDARG;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, Int16 % value) {

   SHORT i16;

   HRESULT hr = PropVariantGetInt16Elem(propertyVariant, 0, & i16);

   if SUCCEEDED(hr)

      value = i16;

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, Int32 % value) {

   LONG i32;

   HRESULT hr = PropVariantGetInt32Elem(propertyVariant, 0, & i32);

   if SUCCEEDED(hr)

      value = i32;

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, UInt16 % value) {

   USHORT ui16;

   HRESULT hr = PropVariantGetUInt16Elem(propertyVariant, 0, & ui16);

   if SUCCEEDED(hr)

      value = ui16;

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, UInt32 % value) {

   ULONG ui32;

   HRESULT hr = PropVariantGetUInt32Elem(propertyVariant, 0, & ui32);

   if SUCCEEDED(hr)

      value = ui32;

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, UInt64 % value) {

   ULONGLONG ui64;

   HRESULT hr = PropVariantGetUInt64Elem(propertyVariant, 0, & ui64);

   if SUCCEEDED(hr)

      value = ui64;

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, Double % value) {

   Double d;

   HRESULT hr = PropVariantGetDoubleElem(propertyVariant, 0, & d);

   if SUCCEEDED(hr)

      value = d;

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, bool % value) {

   BOOL b;

   HRESULT hr = PropVariantGetBooleanElem(propertyVariant, 0, & b);

   if SUCCEEDED(hr)

      value = (b != FALSE);

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, ComTypes::FILETIME % value) {

   ::FILETIME filetime;

   HRESULT hr = PropVariantGetFileTimeElem(propertyVariant, 0, & filetime);

   if SUCCEEDED(hr) {

      value.dwHighDateTime = filetime.dwHighDateTime;

      value.dwLowDateTime = filetime.dwLowDateTime;

   }

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, System::Guid % value) {

   CLSID clsid;

   HRESULT hr = PropVariantToCLSID(propertyVariant, & clsid);

   if SUCCEEDED(hr) {

      value = Guid( clsid.Data1, clsid.Data2, clsid.Data3, clsid.Data4[0], clsid.Data4[1], clsid.Data4[2], clsid.Data4[3], clsid.Data4[4], clsid.Data4[5], clsid.Data4[6], clsid.Data4[7]);

   }

   return hr;

}

static HRESULT GetValue(const PROPVARIANT & propertyVariant, String ^ % value) {

   BSTR bstr;

   HRESULT hr = PropVariantToBSTR(propertyVariant,& bstr);

   if SUCCEEDED(hr) {

      try {

         value = System::Runtime::InteropServices::Marshal::PtrToStringBSTR( (IntPtr) bstr);

      } finally {

         SysFreeString(bstr);

      }

   }

   return hr;

}

// ********************************************************************************

// *

// * GetValueFromPropertyVariant template functions

// *

// ********************************************************************************

template <typename T>

static void GetValueFromPropertyVariant(const PROPVARIANT & propertyVariant, T % element) {

   if (propertyVariant.vt != VariantType<T>::vartype)

      throw gcnew Exception(String::Format("Wrong property VARTYPE (expected {0}, actual {1})", VariantType<T>::vartype, propertyVariant.vt));

   Tif(GetValue(propertyVariant, element));

}

template <typename T>

static void GetValueFromPropertyVariant(const PROPVARIANT & propertyVariant, array<T> ^ % elements) {

   ULONG count = PropVariantGetElementCount(propertyVariant);

   if (count) {

      elements = gcnew array<T>(count);

      for(ULONG i = 0; i < count; i++) {

         PROPVARIANT propertyVariantElement;

         try {

            Tif(PropVariantGetElem(propertyVariant, i, & propertyVariantElement));

            Tif(GetValue(propertyVariantElement, elements[i]));

         } finally {

            PropVariantClear(& propertyVariantElement);

         }

      }

   } else {

      elements = nullptr;

   }

}

// ********************************************************************************

// *

// * SetPropertyVariant functions

// *

// ********************************************************************************

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, Double % value) {

   return InitPropVariantFromDouble(value,propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, const Double * valueArray, ULONG elementCount) {

   return InitPropVariantFromDoubleVector(valueArray, elementCount, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, Byte % value) {

   propertyVariant->vt = VT_UI1;

   propertyVariant->bVal = value;

   return S_OK;

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, bool % value) {

   return InitPropVariantFromBoolean(value,propertyVariant); // the bool will be converted to a BOOL when pushing the value on the stack

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, const bool * valueArray, ULONG elementCount) {

   // Double marshalling from Boolean on the managed heap before entering here, then from

   // bool on the native heap to BOOL on the native heap!

   BOOL * p = new BOOL[elementCount];

   BOOL * to = p;

   const bool * from = valueArray;

   try {

      for(ULONG l = 0; l < elementCount; l++) {

         *to++ = *from++;

      }

      return InitPropVariantFromBooleanVector(p, elementCount, propertyVariant);

   } finally {

      delete [] p;

   }

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, Int16 % value) {

   return InitPropVariantFromInt16(value, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, const Int16 * valueArray, ULONG elementCount) {

   return InitPropVariantFromInt16Vector(valueArray, elementCount, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, Int32 % value) {

   return InitPropVariantFromInt32(value, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, const Int32 * valueArray, ULONG elementCount) {

   return InitPropVariantFromInt32Vector(reinterpret_cast<LONG *>(const_cast<Int32 *>(valueArray)), elementCount, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, UInt16 % value) {

   return InitPropVariantFromUInt16(value, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, const UInt16 * valueArray, ULONG elementCount) {

   return InitPropVariantFromUInt16Vector(valueArray, elementCount, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, UInt32 % value) {

   return InitPropVariantFromUInt32(value, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, const UInt32 * valueArray, ULONG elementCount) {

   return InitPropVariantFromUInt32Vector(reinterpret_cast<ULONG *>(const_cast<UInt32 *>(valueArray)), elementCount, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, UInt64 % value) {

   return InitPropVariantFromUInt64(value, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, const UInt64 * valueArray, ULONG elementCount) {

   return InitPropVariantFromUInt64Vector(valueArray, elementCount, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, ComTypes::FILETIME % value) {

   pin_ptr<ComTypes::FILETIME> filetime = & value;

   return InitPropVariantFromFileTime(reinterpret_cast<::FILETIME *>(filetime),propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, const ::FILETIME * valueArray, ULONG elementCount) {

   return InitPropVariantFromFileTimeVector(valueArray, elementCount, propertyVariant);

}

static HRESULT SetPropertyVariant(PROPVARIANT * propertyVariant, Guid % value) {

   // How to: Convert Between System::Guid and _GUID

   // https://msdn2.microsoft.com/en-us/library/wb8scw8f(VS.80).aspx

   pin_ptr<Byte> p = & value.ToByteArray()[0]; // pin_ptr<Guid> p = & value;

   CLSID & clsid = *reinterpret_cast<CLSID *>(p);

   p = nullptr;

   return InitPropVariantFromCLSID(clsid, propertyVariant);

}

// ********************************************************************************

// *

// * SetPropertyVariantFromValue [template] functions

// *

// ********************************************************************************

static void SetPropertyVariantFromValue(PROPVARIANT * propertyVariant, String ^ % value) {

   pin_ptr<const wchar_t> p = PtrToStringChars(value);

   Tif(InitPropVariantFromString(p,propertyVariant));

   p = nullptr;

   if (propertyVariant->vt != VT_EMPTY && !(propertyVariant->vt & VariantType<String ^>::vartype))

      throw gcnew Exception(String::Format("Wrong property VARTYPE (expected {0}, actual {1})", VariantType<String ^>::vartype, propertyVariant->vt));

}

template <typename T>

static void SetPropertyVariantFromValue(PROPVARIANT * propertyVariant, Nullable<T> % value) {

   Tif(SetPropertyVariant(propertyVariant, value.Value));

   if (propertyVariant->vt != VariantType<T>::vartype)

      throw gcnew Exception(String::Format("Wrong property VARTYPE (expected {0}, actual {1})", VariantType<T>::vartype, propertyVariant->vt));

}

static void SetPropertyVariantFromValue(PROPVARIANT * propertyVariant, array<String ^> ^ % arrayValue) {

   Text::StringBuilder sb;

   for each( String ^ s in arrayValue) {

      sb.Append(s);

      sb.Append(';');

   }

   String ^ s = sb.ToString();

   pin_ptr<const wchar_t> p = PtrToStringChars(s);

   Tif(InitPropVariantFromStringAsVector(p,propertyVariant));

}

static void SetPropertyVariantFromValue(PROPVARIANT * /*propertyVariant*/, array<Byte> ^ % /*arrayValue*/) {

   throw gcnew InvalidOperationException(); // TODO: implement

}

static void SetPropertyVariantFromValue(PROPVARIANT * propertyVariant, array<ComTypes::FILETIME> ^ % arrayValue) {

   ::FILETIME * p = new ::FILETIME[arrayValue->Length];

   try {

      unsigned int u = 0;

      for each(ComTypes::FILETIME value in arrayValue) {

         // Doing that way, we don't have to pin the whole array

         p[u].dwHighDateTime = value.dwHighDateTime;

         p[u++].dwLowDateTime = value.dwLowDateTime;

      }

      Tif(SetPropertyVariant(propertyVariant, p, arrayValue->Length));

   } finally {

      delete [] p;

   }

}

template <typename T>

static void SetPropertyVariantFromValue(PROPVARIANT * propertyVariant, array<T> ^ % arrayValue) {

   T * p = new T[arrayValue->Length];

   try {

      unsigned int u = 0;

      for each(T value in arrayValue) {

         p[u++] = value;

      }

      Tif(SetPropertyVariant(propertyVariant, p, arrayValue->Length));

   } finally {

      delete [] p;

   }

}

// ********************************************************************************

// * PropertyTypeHolder template is a trick to help with the writing of

// * one and only one ItemPropertySet::[Get|Set]Value() template.

// * It works with value type and reference type (including array).

// ********************************************************************************

template <typename PVT, bool IS_REFERENCE_TYPE = __is_ref_array(PVT) || cliext::is_handle<PVT>::value>

ref class PropertyTypeHolder;

template <typename PVT>

ref class PropertyTypeHolder<PVT, true> {

public:

   typedef PVT type;

   static PVT null = nullptr;

   static bool IsNull(type o) {return o == null;}

};

template <typename PVT>

ref class PropertyTypeHolder<PVT, false> {

public:

   typedef Nullable<PVT> type;

   static Nullable<PVT> null;

   static bool IsNull(type % o) {return ! o.HasValue;}

};

namespace Microsoft { namespace SDK { namespace Samples { namespace VistaBridge { namespace Library { namespace Shell {

// ********************************************************************************

// *

// * PropertyKey managed value class

// *

// ********************************************************************************

public value class PropertyKey {

internal:

   PROPERTYKEY Native() {

      pin_ptr<Byte> p = & PropertyGuid.ToByteArray()[0];

      PROPERTYKEY propertyKey = {*reinterpret_cast<CLSID *>(p), PropertyIdentifier};

      return propertyKey;

   }

public:

   PropertyKey(Guid propertyGuid) : PropertyGuid(propertyGuid), PropertyIdentifier(PID_FIRST_USABLE) {

   }

   PropertyKey(Guid propertyGuid, UInt32 propertyIdentifier) : PropertyGuid(propertyGuid), PropertyIdentifier(propertyIdentifier) {

   }

   Guid PropertyGuid;

   UInt32 PropertyIdentifier;

};

// ********************************************************************************

// *

// * ItemPropertySet managed reference class

// *

// ********************************************************************************

public ref class ItemPropertySet {

private:

   IPropertyStore * _propertyStore;

   bool _needCommit;

protected:

   void Init(String ^ path, GetPropertyStoreFlag flag) {

      IShellItem2Ptr shellItem;

      HRESULT hr;

      pin_ptr<const wchar_t> f = PtrToStringChars(path);

      hr = SHCreateItemFromParsingName(f, NULL, IID_PPV_ARGS(& shellItem));

      f = nullptr;

      if FAILED(hr)

         throw gcnew Exception( String::Format("Error creating the Shell Item for \"{0}\".", path));

      void * v;

      Tif(shellItem->GetPropertyStore(reinterpret_cast<GETPROPERTYSTOREFLAGS &>(flag), IID_IPropertyStore, & v));

      _propertyStore = static_cast<IPropertyStore *>(v);

   }

   template <typename T>

   void SetValue(const PROPERTYKEY & propertyKey, typename PropertyTypeHolder<T>::type % value) {

      if(IsNotWritable(propertyKey))

         throw gcnew InvalidOperationException("This property is not writable");

      PROPVARIANT propertyVariant;

      try {

         if (PropertyTypeHolder<T>::IsNull(value))

            propertyVariant.vt = VT_EMPTY;

         else {

            SetPropertyVariantFromValue(& propertyVariant, value);

         }

         Tif(_propertyStore->SetValue(propertyKey, propertyVariant));

         _needCommit = true;

      } finally {

         PropVariantClear(& propertyVariant);

      }

   }

   template <typename T>

   typename PropertyTypeHolder<T>::type GetValue(const PROPERTYKEY & propertyKey) {

      PROPVARIANT propertyVariant;

      Tif(_propertyStore->GetValue(propertyKey, & propertyVariant));

      try {

         if (propertyVariant.vt == VT_EMPTY)

            return PropertyTypeHolder<T>::null;

         typename T propertyValue;

         GetValueFromPropertyVariant(propertyVariant, propertyValue);

         return propertyValue;

      } finally {

         PropVariantClear(& propertyVariant);

      }

   }

   bool IsNotWritable(const PROPERTYKEY & propertyKey) {

      IPropertyStoreCapabilitiesPtr propertyStoreCapabilities;

      return SUCCEEDED(_propertyStore->QueryInterface(IID_PPV_ARGS(& propertyStoreCapabilities)))

             && S_FALSE == propertyStoreCapabilities->IsPropertyWritable(propertyKey);

   }

public:

   ItemPropertySet(String ^ path, GetPropertyStoreFlag flag) {

      Init(path, flag);

   }

   ItemPropertySet(String ^ path) {

      Init(path, GetPropertyStoreFlag::Default);

   }

   ~ItemPropertySet() {

      ItemPropertySet::!ItemPropertySet();

   }

   !ItemPropertySet() {

      if (_needCommit) {

         System::Diagnostics::Debug::WriteLine("ItemPropertySet instance did not call Commit() although it might have needed to.");

         _needCommit = false;

      }

      if (_propertyStore)

         _propertyStore->Release();

   }

   bool IsNotWritable(PropertyKey propertyKey) {

      return IsNotWritable(propertyKey.Native());

   }

   Object ^ GetValue(PropertyKey managedPropertyKey) {

      PROPVARIANT propertyVariant;

      Tif(_propertyStore->GetValue(managedPropertyKey.Native(), & propertyVariant));

      if (propertyVariant.vt == VT_EMPTY)

         return nullptr;

      try {

         if (propertyVariant.vt & VT_VECTOR) {

            #pragma push_macro("HANDLE_CASE")

            #define HANDLE_CASE(T) case VariantType<T>::vartype: {array<T> ^ t; ::GetValueFromPropertyVariant(propertyVariant, t); return t;}

            switch (propertyVariant.vt & ~VT_VECTOR) {

               HANDLE_CASE(Byte)

               HANDLE_CASE(UInt16)

   HANDLE_CASE(UInt32)

               HANDLE_CASE(UInt64)

               HANDLE_CASE(Int16)

               HANDLE_CASE(Int32)

               HANDLE_CASE(Double)

               HANDLE_CASE(bool)

               HANDLE_CASE(ComTypes::FILETIME)

      HANDLE_CASE(Guid)

               HANDLE_CASE(String ^)

            }

            #undef HANDLE_CASE

         } else {

            #define HANDLE_CASE(T) case VariantType<T>::vartype: {T t; Tif(::GetValue(propertyVariant, t)); return t;}

          switch (propertyVariant.vt) {

               HANDLE_CASE(Byte)

               HANDLE_CASE(UInt16)

               HANDLE_CASE(UInt32)

               HANDLE_CASE(UInt64)

               HANDLE_CASE(Int16)

               HANDLE_CASE(Int32)

               HANDLE_CASE(Double)

               HANDLE_CASE(bool)

               HANDLE_CASE(ComTypes::FILETIME)

               HANDLE_CASE(Guid)

               HANDLE_CASE(String ^)

            }

            #undef HANDLE_CASE

            #pragma pop_macro("HANDLE_CASE")

         }

      } finally {

         PropVariantClear(& propertyVariant);

      }

      throw gcnew InvalidOperationException(String::Format("Unsupported type ({0})", propertyVariant.vt));

   }

   Object ^ GetValue(Guid propertyGuid, UInt32 propertyIdentifier) {

      return GetValue(PropertyKey(propertyGuid,propertyIdentifier));

   }

   // https://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/ifaces/ipropertystore/commit.asp

   void Commit() {

      if (_needCommit) {

         Tif(_propertyStore->Commit());

         _needCommit = false;

      } else {

         System::Diagnostics::Debug::WriteLine("ItemPropertySet.Commit() was called although it was not need");

      }

   }

   void SetValue(PropertyKey pk, String ^ value) {SetValue<String ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<Boolean> value) {SetValue<Boolean>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<Byte> value) {SetValue<Byte>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<Int16> value) {SetValue<Int16>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<Int32> value) {SetValue<Int32>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<UInt16> value) {SetValue<UInt16>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<UInt32> value) {SetValue<UInt32>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<UInt64> value) {SetValue<UInt64>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<Double> value) {SetValue<Double>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<ComTypes::FILETIME> value) {SetValue<ComTypes::FILETIME>(pk.Native(), value);}

   void SetValue(PropertyKey pk, Nullable<Guid> value) {SetValue<Guid>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<String ^> ^ value) {SetValue<array<String ^> ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<Boolean> ^value) {SetValue<array<Boolean> ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<Byte> ^ value) {SetValue<array<Byte> ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<Int16> ^ value) {SetValue<array<Int16> ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<Int32> ^ value) {SetValue<array<Int32> ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<UInt16> ^ value) {SetValue<array<UInt16> ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<UInt32> ^ value) {SetValue<array<UInt32> ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<UInt64> ^ value) {SetValue<array<UInt64> ^> (pk.Native(), value);}

   void SetValue(PropertyKey pk, array<Double> ^ value) {SetValue<array<Double> ^>(pk.Native(), value);}

   void SetValue(PropertyKey pk, array<ComTypes::FILETIME> ^ value) {SetValue<array<ComTypes::FILETIME> ^>(pk.Native(), value);}

   //void SetValue(PropertyKey pk, array<Guid> ^ value) {SetValue<array<Guid> ^>(pk.Native(), value);}

   // And now for the properties:

   // https://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/schemas/properties/system\_size.asp

   #define PROPERTY_GETTER(PK, T) PropertyTypeHolder<T>::type get() {return GetValue<T>(PK);}

   #define PROPERTY_SETTER(PK, T) void set(PropertyTypeHolder<T>::type value) {SetValue<T>(PK, value);}

   #define PROPERTY(PK, T, N) property PropertyTypeHolder<T>::type N {PROPERTY_GETTER(PK, T) PROPERTY_SETTER(PK, T)}

   #define PROPERTY_R(PK, T, N) property PropertyTypeHolder<T>::type N {PROPERTY_GETTER(PK, T)}

   // #define PROPERTY_W(PK, T, N) property PropertyTypeHolder<T>::type N {PROPERTY_SETTER(PK, T)}

   // I use the full namespace so that I can have a property of the same type as its name (e.g. NoteColor of type NoteColor)

   #define PROPERTY_WRAPPER_GETTER(FROM_TYPE, TO_TYPE, NAME)\

      Nullable<::Microsoft::SDK::Samples::VistaBridge::Library::Shell::TO_TYPE> get() {\

         Nullable<FROM_TYPE> u = NAME;\

         return u.HasValue ? static_cast<::Microsoft::SDK::Samples::VistaBridge::Library::Shell::TO_TYPE>(u.Value) : Nullable<::Microsoft::SDK::Samples::VistaBridge::Library::Shell::TO_TYPE>();\

    }

   #define PROPERTY_WRAPPER_SETTER(FROM_TYPE, TO_TYPE, NAME)\

      void set(Nullable<::Microsoft::SDK::Samples::VistaBridge::Library::Shell::TO_TYPE> value) {\

         NAME = value.HasValue ? static_cast<FROM_TYPE>(value.Value) : Nullable<FROM_TYPE>();\

      }

   #define PROPERTY_WRAPPER_R(FROM_TYPE, TO_TYPE, NAME) property Nullable<TO_TYPE> NAME {PROPERTY_WRAPPER_GETTER(FROM_TYPE, TO_TYPE, NAME##Raw)}

   //#define PROPERTY_WRAPPER_W(FROM_TYPE, TO_TYPE, NAME) property Nullable<TO_TYPE> NAME {PROPERTY_WRAPPER_SETTER(FROM_TYPE, TO_TYPE, NAME##Raw)}

   #define PROPERTY_WRAPPER(FROM_TYPE, TO_TYPE, NAME) property Nullable<TO_TYPE> NAME {PROPERTY_WRAPPER_GETTER(FROM_TYPE, TO_TYPE, NAME##Raw) PROPERTY_WRAPPER_SETTER(FROM_TYPE, TO_TYPE, NAME##Raw)}

#pragma region Audio properties

   PROPERTY_R(PKEY_Audio_ChannelCount, UInt32, AudioChannelCountRaw)

   PROPERTY_WRAPPER_R(UInt32, ChannelCount, AudioChannelCount)

   PROPERTY_R(PKEY_Audio_Compression, String ^, AudioCompression)

   PROPERTY_R(PKEY_Audio_EncodingBitrate, UInt32, AudioEncodingBitrate)

   PROPERTY_R(PKEY_Audio_Format, String ^, AudioFormat)

   PROPERTY_R(PKEY_Audio_IsVariableBitRate, bool, AudioIsVariableBitRate)

   PROPERTY_R(PKEY_Audio_PeakValue, UInt32, AudioPeakValue)

   PROPERTY_R(PKEY_Audio_SampleRate, UInt32, AudioSampleRate)

   PROPERTY_R(PKEY_Audio_SampleSize, UInt32, AudioSampleSize)

   PROPERTY_R(PKEY_Audio_StreamName, String ^, AudioStreamName)

   PROPERTY_R(PKEY_Audio_StreamNumber, UInt16, AudioStreamNumber)

#pragma endregion

#pragma region Calendar properties

...

ItemPropertySet 10.zip