Marshalling a VARIANT in a WinRT component

Amyuni's Development Team would like to share with fellow developers general information about Windows and Web development. These are issues encountered by Amyuni's team and were either solved or pending solution.
Post Reply
Devteam
Posts: 119
Joined: Fri Oct 14 2005
Location: Montreal
Contact:

Marshalling a VARIANT in a WinRT component

Post 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;
	}
};
Amyuni Development Team

Efficient and accurate conversion to PDF, XPS, PDF/A and more. Free trials at - https://www.amyuni.com
Post Reply