public static int ChangeTypeForCOM(object source, VarEnum sourceType, VarEnum targetType, out object target)
{
target = source;
// check for trivial case.
if (sourceType == targetType)
{
return ResultIds.S_OK;
}
// check for conversions to date time from string.
string stringValue = source as string;
if (stringValue != null && targetType == VarEnum.VT_DATE)
{
try
{
target = System.Convert.ToDateTime(stringValue);
return ResultIds.S_OK;
}
catch
{
target = null;
return ResultIds.DISP_E_OVERFLOW;
}
}
// check for conversions from date time to boolean.
if (sourceType == VarEnum.VT_DATE && targetType == VarEnum.VT_BOOL)
{
target = !(new DateTime(1899, 12, 30, 0, 0, 0).Equals((DateTime)source));
return ResultIds.S_OK;
}
// check for conversions from float to double.
if (sourceType == VarEnum.VT_R4 && targetType == VarEnum.VT_R8)
{
target = System.Convert.ToDouble((float)source);
return ResultIds.S_OK;
}
// check for array conversion.
Array array = source as Array;
bool targetIsArray = ((targetType & VarEnum.VT_ARRAY) != 0);
if (array != null && targetIsArray)
{
VarEnum elementType = (VarEnum)((short)targetType & ~(short)VarEnum.VT_ARRAY);
Array convertedArray = Array.CreateInstance(GetSystemType((short)elementType), array.Length);
for (int ii = 0; ii < array.Length; ii++)
{
object elementValue = null;
int error = ChangeTypeForCOM(array.GetValue(ii), elementType, out elementValue);
if (error < 0)
{
target = null;
return ResultIds.DISP_E_OVERFLOW;
}
convertedArray.SetValue(elementValue, ii);
}
target = convertedArray;
return ResultIds.S_OK;
}
else if (array == null && !targetIsArray)
{
IntPtr pvargDest = Marshal.AllocCoTaskMem(16);
IntPtr pvarSrc = Marshal.AllocCoTaskMem(16);
VariantInit(pvargDest);
VariantInit(pvarSrc);
Marshal.GetNativeVariantForObject(source, pvarSrc);
try
{
// change type.
int error = VariantChangeTypeEx(
pvargDest,
pvarSrc,
Thread.CurrentThread.CurrentCulture.LCID,
VARIANT_NOVALUEPROP | VARIANT_ALPHABOOL,
(short)targetType);
// check error code.
if (error != 0)
{
target = null;
return error;
}
// unmarshal result.
object result = Marshal.GetObjectForNativeVariant(pvargDest);
// check for invalid unsigned <=> signed conversions.
switch (targetType)
{
case VarEnum.VT_I1:
case VarEnum.VT_I2:
case VarEnum.VT_I4:
case VarEnum.VT_I8:
case VarEnum.VT_UI1:
case VarEnum.VT_UI2:
case VarEnum.VT_UI4:
case VarEnum.VT_UI8:
{
// ignore issue for conversions from boolean.
if (sourceType == VarEnum.VT_BOOL)
{
break;
}
decimal sourceAsDecimal = 0;
decimal resultAsDecimal = System.Convert.ToDecimal(result);
try { sourceAsDecimal = System.Convert.ToDecimal(source); }
catch { sourceAsDecimal = 0; }
if ((sourceAsDecimal < 0 && resultAsDecimal > 0) || (sourceAsDecimal > 0 && resultAsDecimal < 0))
{
target = null;
return ResultIds.E_RANGE;
}
// conversion from datetime should have failed.
if (sourceType == VarEnum.VT_DATE)
{
if (resultAsDecimal == 0)
{
target = null;
return ResultIds.E_RANGE;
}
}
break;
}
case VarEnum.VT_R8:
{
// fix precision problem introduced with conversion from float to double.
if (sourceType == VarEnum.VT_R4)
{
result = System.Convert.ToDouble(source.ToString());
}
break;
}
}
target = result;
return ResultIds.S_OK;
}
finally
{
VariantClear(pvargDest);
VariantClear(pvarSrc);
Marshal.FreeCoTaskMem(pvargDest);
Marshal.FreeCoTaskMem(pvarSrc);
}
}
else if (array != null && targetType == VarEnum.VT_BSTR)
{
int count = ((Array)source).Length;
StringBuilder buffer = new StringBuilder();
buffer.Append("{");
foreach (object element in (Array)source)
{
object elementValue = null;
int error = ChangeTypeForCOM(element, VarEnum.VT_BSTR, out elementValue);
if (error < 0)
{
target = null;
return error;
}
buffer.Append((string)elementValue);
count--;
if (count > 0)
{
buffer.Append(" | ");
}
}
buffer.Append("}");
target = buffer.ToString();
return ResultIds.S_OK;
}
// no conversions between scalar and array types allowed.
target = null;
return ResultIds.E_BADTYPE;
}