Page 1 of 1

Marshalling a VARIANT in a WinRT component

Posted: Mon Aug 20 2012
by Devteam
I was porting our component which is written in C++ at its core and which has both an ActiveX and a .Net shell. The component internally uses the VARIANT type in many places. Some public properties (get/set) and methods of this component's arguments are of the VARIANT type in the ActiveX implementation and System::Object in the .Net implementation. Internally in our code we use the VARIANT directly.

When implementing the ActiveX component, I did not need to do any marshaling since VARIANT is an OLE/COM type.

When implementing .Net component, I used similar to this:

VARIANT var;
//...
//Initialize the VARIANT value
//...
System::IntPtr p( &var );
System::Object ^o = System::Runtime::InteropServices::Marshal::GetObjectForNativeVariant( p );
return o;

In WinRT, there does not seem to be any similar Marshal class that will do the job. According to MSDN "The WinRT Platform::Runtime::InteropServices namespace is intended for internal use only, and is not intended to be used for development."

After posting a question on an MSDN (http://social.msdn.microsoft.com/Forums ... 1a654965d4) forum I got one reply suggesting that I try to use Windows::Foundation::IPropertyValue. After some investigation I wrote a simple utility class that makes use of Windows::Foundation::IPropertyValue to do implement marshalling of a VARIANT to Platform::Object ^ and the reverse, Platform::Object ^ to VARIANT.

Below is the code of this class.

Code: Select all

//////////////////////////////////////////////////////////////////////////////////
//                                                                         
// Copyright AMYUNI Technologies, 1998-2012, All Rights Reserved 
//
// Permission to use this work is granted for free and for any purpose
// with no restrictions and without any warranties
// For more information: AMYUNI Technologies, http://www.amyuni.com
//                                              
// acMarshall: WinRT C++ utility class to Marshal VARIANT to Object and vice-versa
//
//////////////////////////////////////////////////////////////////////////////////
#pragma once

#include <windows.h>
#include <stdint.h>
#include <atlconv.h>

using namespace Platform;
using namespace Windows::Foundation;

public ref class acMarshall
{
internal:
			
	static LPSTR MarshalString(String ^s)
	{
		//The caller is responsible for freeing the allocated LPSTR memory

		USES_CONVERSION;

		UINT n = s->Length();
		LPSTR str = (LPSTR) malloc( (n + 1) * sizeof(CHAR) ); 
		str[0] = 0;
		strncpy_s( str, n, W2CA(s->Begin()), n );
		return str;
	}


	static LPWSTR MarshalStringW(String ^s)
	{
		//The caller is responsible for freeing the allocated LPWSTR memory

		UINT n = s->Length();
		LPWSTR str = (LPWSTR) malloc( (n + 1) * sizeof(WCHAR) ); 
		str[0] = 0;
		wcsncpy_s( str, n, s->Begin(), n );
		return str;
	}

	static Object ^ MarshalVariantToObject(const VARIANT &var)
	{
		Object ^ obj = nullptr;
	
		BOOL	byRef = (var.vt & VT_BYREF) > 0;
		BOOL	isArray = (var.vt & VT_ARRAY) > 0;
		VARTYPE vt = var.vt &~ (VT_BYREF | VT_ARRAY);

		if ( isArray )
		{
			//if VARIANT contains an array

			UINT len = 0;
			SAFEARRAY *psa = (byRef ? *var.pparray : var.parray);
			HRESULT hr;
			if ( SUCCEEDED( hr = SafeArrayLock( psa ) ) &&
					SUCCEEDED( hr = SafeArrayGetUBound( psa, 1, (LONG *) &len ) ) )
			{
				len++;

				Object ^ arr = nullptr;

				switch ( vt )
				{
				case VT_I2:
					arr = ref new Array< short >( len );
					break;
				case VT_I4:
				case VT_INT:
					arr = ref new Array< int >( len );
					break;
				case VT_R4:
					arr = ref new Array< float >( len );
					break;
				case VT_R8:
					arr = ref new Array< double >( len );
					break;
				case VT_DATE:
					arr = ref new Array< DateTime >( len );
					break;
				case VT_BSTR:
					arr = ref new Array< String ^>( len );
					break;
				case VT_BOOL:
					arr = ref new Array< bool >( len );
					break;
				case VT_VARIANT:
					arr = ref new Array< Object ^ >( len );
					break;
				case VT_I1:
				case VT_UI1:
					arr = ref new Array< unsigned char >( len );
					break;
				case VT_UI2:
					arr = ref new Array< unsigned short >( len );
					break;
				case VT_UI4:
				case VT_UINT:
					arr = ref new Array< unsigned int >( len );
					break;
				case VT_I8:
					arr = ref new Array< int64_t >( len );
					break;
				case VT_UI8:
					arr = ref new Array< uint64_t >( len );
					break;
				default:
					break;
				}

				// read data into array in this loop
				LPBYTE p = (LPBYTE) psa->pvData;
				for ( UINT i = 0; i < len; i++ )
				{
					if ( arr == nullptr )
						break;

					switch ( vt )
					{
					case VT_I2:
						((Array< short > ^)arr)->set( i, *(short *) p );
						p += sizeof( short );
						break;
					case VT_I4:
					case VT_INT:
						((Array< int > ^)arr)->set( i, *(int *) p );
						p += sizeof( int );
						break;
					case VT_R4:
						((Array< float > ^)arr)->set( i, *(float *) p );
						p += sizeof( float );
						break;
					case VT_R8:
						((Array< double > ^)arr)->set( i, *(double *) p );
						p += sizeof( double );
						break;
					case VT_DATE:
						{
							UDATE udate;
							if SUCCEEDED( VarUdateFromDate( *(DATE *) p, 0, &udate ) )
							{
								Windows::Globalization::Calendar ^ c = ref new Windows::Globalization::Calendar();
								c->Year = udate.st.wYear;
								c->Month = udate.st.wMonth;
								c->Day = udate.st.wDay;
								c->Hour = udate.st.wHour;
								c->Minute = udate.st.wMinute;
								c->Second = udate.st.wSecond;
								c->Nanosecond = udate.st.wMilliseconds * 1000000;
								((Array< DateTime > ^)arr)->set( i, c->GetDateTime() );
							}
						}					
						p += sizeof( DATE );
						break;
					case VT_BSTR:
						((Array< String ^ > ^)arr)->set( i, ref new String( *(BSTR *) p, SysStringLen(*(BSTR *) p) ) );
						p += sizeof( BSTR );
						break;
					case VT_BOOL:
						((Array< bool > ^)arr)->set( i, *(bool *) p );
						p += sizeof( bool );
						break;
					case VT_VARIANT:
						((Array< Object ^ > ^)arr)->set( i, MarshalVariantToObject( *(VARIANT *) p ) );
						p += sizeof( VARIANT * );
						break;
					case VT_I1:
					case VT_UI1:
						((Array< unsigned char > ^)arr)->set( i, *(unsigned char *) p );
						p += sizeof( unsigned char );
						break;
					case VT_UI2:
						((Array< unsigned short > ^)arr)->set( i, *(unsigned short *) p );
						p += sizeof( unsigned short );
						break;
					case VT_UI4:
					case VT_UINT:
						((Array< unsigned int > ^)arr)->set( i, *(unsigned int *) p );
						p += sizeof( unsigned int );
						break;
					case VT_I8:
						((Array< int64_t > ^)arr)->set( i, *(int64_t *) p );
						p += sizeof( int64_t );
						break;
					case VT_UI8:
						((Array< uint64_t > ^)arr)->set( i, *(uint64_t *) p );
						p += sizeof( uint64_t );
						break;
					default:
						arr = nullptr; // abort in case of unsupported VT in array
						break;
					}
				}			

				SafeArrayUnlock( psa );

				if ( arr != nullptr )
				{
					switch ( vt )
					{
					case VT_I2:
						obj = PropertyValue::CreateInt16Array( (Array< short > ^) arr );
						break;
					case VT_I4:
					case VT_INT:
						obj = PropertyValue::CreateInt32Array( (Array< int > ^) arr );
						break;
					case VT_R4:
						obj = PropertyValue::CreateSingleArray( (Array< float > ^) arr );
						break;
					case VT_R8:
						obj = PropertyValue::CreateDoubleArray( (Array< double > ^) arr );
						break;
					case VT_DATE:
						obj = PropertyValue::CreateDateTimeArray( (Array< DateTime > ^) arr );
						break;
					case VT_BSTR:
						obj = PropertyValue::CreateStringArray( (Array< String ^ > ^) arr );
						break;
					case VT_BOOL:
						obj = PropertyValue::CreateBooleanArray( (Array< bool > ^) arr );
						break;
					case VT_VARIANT:
						obj = PropertyValue::CreateInspectableArray( (Array< Object ^ > ^) arr );
						break;
					case VT_I1:
					case VT_UI1:
						obj = PropertyValue::CreateUInt8Array( (Array< unsigned char  > ^) arr );
						break;
					case VT_UI2:
						obj = PropertyValue::CreateUInt16Array( (Array< unsigned short  > ^) arr );
						break;
					case VT_UI4:
					case VT_UINT:
						obj = PropertyValue::CreateUInt32Array( (Array< unsigned int  > ^) arr );
						break;
					case VT_I8:
						obj = PropertyValue::CreateInt64Array( (Array< int64_t > ^) arr );
						break;
					case VT_UI8:
						obj = PropertyValue::CreateUInt64Array( (Array< uint64_t > ^) arr );
						break;
					default:
						break;
					}
				}
			}

			return obj;
		}

		//if VARIANT does not contain an array
		switch ( vt )
		{
		case VT_I2:
			obj = PropertyValue::CreateInt16( byRef ? *var.piVal : var.iVal );
			break;
		case VT_I4:
		case VT_INT:
			obj = PropertyValue::CreateInt32( byRef ? *var.plVal : var.lVal );
			break;
		case VT_R4:
			obj = PropertyValue::CreateSingle( byRef ? *var.pfltVal : var.fltVal );
			break;
		case VT_R8:
			obj = PropertyValue::CreateDouble( byRef ? *var.pdblVal : var.dblVal );
			break;
		case VT_DATE:
			{
				UDATE udate;
				if SUCCEEDED( VarUdateFromDate( byRef ? *var.pdate : var.date, 0, &udate ) )
				{
					Windows::Globalization::Calendar ^ c = ref new Windows::Globalization::Calendar();
					c->Year = udate.st.wYear;
					c->Month = udate.st.wMonth;
					c->Day = udate.st.wDay;
					c->Hour = udate.st.wHour;
					c->Minute = udate.st.wMinute;
					c->Second = udate.st.wSecond;
					c->Nanosecond = udate.st.wMilliseconds * 1000000;
				
					obj = PropertyValue::CreateDateTime( c->GetDateTime() );
				}
			}
			break;
		case VT_BSTR:
			{
				const BSTR *pbstr = (byRef ? var.pbstrVal : &var.bstrVal);
				obj = PropertyValue::CreateString( ref new String( *pbstr, SysStringLen(*pbstr) ) );	
			}
			break;
		case VT_BOOL:
			{
				const VARIANT_BOOL *pVarBool = ( byRef ? var.pboolVal : &var.boolVal );
				obj = PropertyValue::CreateBoolean( *pVarBool == 0xFFFF );
			}
			break;
		case VT_VARIANT:
			obj = MarshalVariantToObject( *var.pvarVal );
			break;
		case VT_I1:
			obj = PropertyValue::CreateUInt8( byRef ? (UCHAR) *var.pcVal: (UCHAR) var.cVal );
			break;
		case VT_UI1:
			obj = PropertyValue::CreateUInt8( byRef ? *var.pbVal: var.bVal );
			break;
		case VT_UI2:
			obj = PropertyValue::CreateUInt16( byRef ? *var.puiVal: var.uiVal );
			break;
		case VT_UI4:
		case VT_UINT:
			obj = PropertyValue::CreateUInt32( byRef ? *var.pulVal: var.ulVal );
			break;
		case VT_I8:
			obj = PropertyValue::CreateInt64( byRef ? *var.pllVal: var.llVal );
			break;
		case VT_UI8:
			obj = PropertyValue::CreateUInt64( byRef ? *var.pullVal: var.ullVal );
			break;
		case VT_EMPTY:
		case VT_NULL:
		case VT_CY:
		case VT_DISPATCH:
		case VT_ERROR:
		case VT_UNKNOWN:
		case VT_DECIMAL:
		case VT_VOID:
		case VT_HRESULT:
		case VT_PTR:
		case VT_CARRAY:
		case VT_USERDEFINED:
		case VT_LPSTR:
		case VT_LPWSTR:
		case VT_RECORD:
		case VT_INT_PTR:
		case VT_UINT_PTR:
		case VT_FILETIME:
		case VT_BLOB:
		case VT_STREAM:
		case VT_STORAGE:
		case VT_STREAMED_OBJECT:
		case VT_STORED_OBJECT:
		case VT_BLOB_OBJECT:
		case VT_CF:
		case VT_CLSID:
		case VT_VERSIONED_STREAM:
		case VT_BSTR_BLOB:
		case VT_VECTOR:
		case VT_RESERVED:
		case VT_ILLEGAL:
		//case VT_ILLEGALMASKED:
		//case VT_TYPEMASK:
			break;
		default:
			break;
		}

		return obj;
	}
			
	static VARIANT MarshalObjectToVariant(Object ^ obj)
	{
		VARIANT var;
		VariantInit( &var );

		IPropertyValue ^ ipv = dynamic_cast<IPropertyValue ^>(obj);
		PropertyType type = ipv->Type;
	
		BOOL isArray = ((UINT) type & 1024) > 0;

		if ( isArray )
		{
			//if Object contains an array
			VARTYPE vt = VT_EMPTY;

			type = (PropertyType) (((UINT)type) &~ 1024);
			var.vt = VT_ARRAY;
			switch (type)
			{
				case PropertyType::Boolean:
					{
						vt = VT_BOOL;
						var.vt |= vt;
					
						Array< bool > ^ arr;
						ipv->GetBooleanArray(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						VARIANT_BOOL *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each (bool value in arr)
						{
							*p = (value ? 0xFFFF : 0);
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::DateTime:
					{
						vt = VT_DATE;
						var.vt |= vt;
					
						Array< DateTime > ^ arr;
						ipv->GetDateTimeArray(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						DATE *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
					
						UDATE udate;
						Windows::Globalization::Calendar ^ c = ref new Windows::Globalization::Calendar();
					
						for each (DateTime value in arr)
						{
							c->SetDateTime( value );
			
							udate.st.wYear = c->Year ;
							udate.st.wMonth = c->Month;
							udate.st.wDay = c->Day;
							udate.st.wHour = c->Hour;
							udate.st.wMinute = c->Minute;
							udate.st.wSecond = c->Second;
							udate.st.wMilliseconds = c->Nanosecond / 1000000;
				
							var.vt = VT_DATE;
							VarDateFromUdate( &udate, 0, p );
						
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::UInt8:
					{
						vt = VT_UI1;
						var.vt |= vt;
					
						Array< unsigned char > ^ arr;
						ipv->GetUInt8Array(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						BYTE *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each (unsigned char value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::Int16:
					{
						vt = VT_I2;
						var.vt |= vt;
					
						Array< short > ^ arr;
						ipv->GetInt16Array(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						SHORT *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each (short value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::UInt16:
					{
						vt = VT_UI2;
						var.vt |= vt;
					
						Array< unsigned short > ^ arr;
						ipv->GetUInt16Array(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						USHORT *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each (unsigned short value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::Int32:
					{
						vt = VT_I4;
						var.vt |= vt;
					
						Array< int > ^ arr;
						ipv->GetInt32Array(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						LONG *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each (int value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::UInt32:
					{
						vt = VT_UI4;
						var.vt |= vt;
					
						Array< unsigned int > ^ arr;
						ipv->GetUInt32Array(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						ULONG *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each (unsigned int value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::Int64:
					{
						vt = VT_I8;
						var.vt |= vt;
					
						Array< int64_t > ^ arr;
						ipv->GetInt64Array(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						LONGLONG *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each ( int64_t value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::UInt64:
					{
						vt = VT_UI8;
						var.vt |= vt;
					
						Array< uint64_t > ^ arr;
						ipv->GetUInt64Array(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						ULONGLONG *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each ( uint64_t value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::Single:
					{
						vt = VT_R4;
						var.vt |= vt;
					
						Array< float > ^ arr;
						ipv->GetSingleArray(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						FLOAT *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each ( float value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::Double:
					{
						vt = VT_R8;
						var.vt |= vt;
					
						Array< double > ^ arr;
						ipv->GetDoubleArray(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						DOUBLE *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each ( double value in arr)
						{
							*p = value;
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				case PropertyType::String:
					{
						vt = VT_BSTR;
						var.vt |= vt;
					
						Array< String ^ > ^ arr;
						ipv->GetStringArray(&arr);
						SAFEARRAYBOUND sab;
						sab.cElements = arr->Length;
						sab.lLbound = 0;
						SAFEARRAY *psa = SafeArrayCreate( vt, 1, &sab );
						BSTR *p = NULL;
						SafeArrayAccessData( psa, (void**) &p );
						for each ( String ^ value in arr)
						{
							*p = SysAllocStringLen( value->Begin(), value->Length() );
							p++;
						}
						SafeArrayUnaccessData( psa );
						var.parray = psa;
					}
					break;
				default:
					var.vt = VT_EMPTY; //coudn't convert
					break;
			}

			return var;
		}

		//if Object does not contain an array
		switch (type)
		{
		case PropertyType::Boolean:
			var.vt = VT_BOOL;
			var.boolVal = (ipv->GetBoolean() ? 0xFFFF : 0);
			break;
		case PropertyType::DateTime:
			{
				Windows::Globalization::Calendar ^ c = ref new Windows::Globalization::Calendar();
				c->SetDateTime( ipv->GetDateTime() );
			
				UDATE udate;
				udate.st.wYear = c->Year ;
				udate.st.wMonth = c->Month;
				udate.st.wDay = c->Day;
				udate.st.wHour = c->Hour;
				udate.st.wMinute = c->Minute;
				udate.st.wSecond = c->Second;
				udate.st.wMilliseconds = c->Nanosecond / 1000000;
				
				var.vt = VT_DATE;
				VarDateFromUdate( &udate, 0, &var.date );
			}
			break;
		case PropertyType::UInt8:
			var.vt = VT_UI1;
			var.bVal = ipv->GetUInt8();
			break;
		case PropertyType::Int16:
			var.vt = VT_I2;
			var.iVal = ipv->GetInt16();
			break;
		case PropertyType::UInt16:
			var.vt = VT_UI2;
			var.uiVal = ipv->GetUInt16();
			break;
		case PropertyType::Int32:
			var.vt = VT_I4;
			var.lVal = ipv->GetInt32();
			break;
		case PropertyType::UInt32:
			var.vt = VT_UI4;
			var.ulVal = ipv->GetUInt32();
			break;
		case PropertyType::Int64:
			var.vt = VT_I8;
			var.llVal = ipv->GetInt64();
			break;
		case PropertyType::UInt64:
			var.vt = VT_UI8;
			var.ullVal = ipv->GetUInt64();
			break;
		case PropertyType::Single:
			var.vt = VT_R4;
			var.fltVal = ipv->GetSingle();
			break;
		case PropertyType::Double:
			var.vt = VT_R8;
			var.dblVal = ipv->GetDouble();
			break;
		case PropertyType::String:
			{
				String ^s = ipv->GetString();
				var.vt = VT_BSTR;
				var.bstrVal = SysAllocStringLen( s->Begin(), s->Length() );
			}
			break;
		default:
			break;
		};

		return var;
	}
};