/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Microsoft Public License. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Microsoft Public License, please send an email to
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Microsoft Public License.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/
#if !SILVERLIGHT // ComObject
using System;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using MarshalSystem.Runtime.InteropServices.Marshal;
using VarEnumSystem.Runtime.InteropServices.VarEnum;
namespace Microsoft.Scripting.ComInterop{
/// <summary>
/// The parameter description of a method defined in a type library
/// </summary>
public class ComParamDesc {
# region private fields
private readonly bool _isOut; // is an output parameter?
private readonly bool _isOpt; // is an optional parameter?
private readonly bool _byRef; // is a reference or pointer parameter?
private readonly bool _isArray;
private readonly VarEnum _vt;
private readonly string _name;
private readonly Type _type;
private readonly object _defaultValue;
# endregion
# region ctor
/// <summary>
/// Creates a representation for the paramter of a COM method
/// </summary>
internal ComParamDesc(ref ELEMDESC elemDesc, string name) {
// Ensure _defaultValue is set to DBNull.Value regardless of whether or not the
// default value is extracted from the parameter description. Failure to do so
// yields a runtime exception in the ToString() function.
_defaultValue = DBNull.Value;
if (!String.IsNullOrEmpty(name)) {
// This is a parameter, not a return value
this._isOut = (elemDesc.desc.paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FOUT) != 0;
this._isOpt = (elemDesc.desc.paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FOPT) != 0;
// TODO: The PARAMDESCEX struct has a memory issue that needs to be resolved. For now, we ignore it.
//_defaultValue = PARAMDESCEX.GetDefaultValue(ref elemDesc.desc.paramdesc);
}
_name = name;
_vt = (VarEnum)elemDesc.tdesc.vt;
TYPEDESC typeDesc = elemDesc.tdesc;
while (true) {
if (_vt == VarEnum.VT_PTR) {
this._byRef = true;
} else if (_vt == VarEnum.VT_ARRAY) {
this._isArray = true;
} else {
break;
}
TYPEDESC childTypeDesc = (TYPEDESC)Marshal.PtrToStructure(typeDesc.lpValue, typeof(TYPEDESC));
_vt = (VarEnum)childTypeDesc.vt;
typeDesc = childTypeDesc;
}
VarEnum vtWithoutByref = _vt;
if ((_vt & VarEnum.VT_BYREF) != 0) {
vtWithoutByref = (_vt & ~VarEnum.VT_BYREF);
_byRef = true;
}
_type = GetTypeForVarEnum(vtWithoutByref);
}
/// <summary>
/// Creates a representation for the return value of a COM method
/// TODO: Return values should be represented by a different type
/// </summary>
internal ComParamDesc(ref ELEMDESC elemDesc)
: this(ref elemDesc, String.Empty) {
}
//internal struct PARAMDESCEX {
// private ulong _cByte;
// private Variant _varDefaultValue;
// internal void Dummy() {
// _cByte = 0;
// throw Error.MethodShouldNotBeCalled();
// }
// internal static object GetDefaultValue(ref PARAMDESC paramdesc) {
// if ((paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FHASDEFAULT) == 0) {
// return DBNull.Value;
// }
// PARAMDESCEX varValue = (PARAMDESCEX)Marshal.PtrToStructure(paramdesc.lpVarValue, typeof(PARAMDESCEX));
// if (varValue._cByte != (ulong)(Marshal.SizeOf((typeof(PARAMDESCEX))))) {
// throw Error.DefaultValueCannotBeRead();
// }
// return varValue._varDefaultValue.ToObject();
// }
//}
private static Type GetTypeForVarEnum(VarEnum vt) {
Type type;
switch (vt) {
// VarEnums which can be used in VARIANTs, but which cannot occur in a TYPEDESC
case VarEnum.VT_EMPTY:
case VarEnum.VT_NULL:
case VarEnum.VT_RECORD:
throw new InvalidOperationException(string.Format("Unexpected VarEnum {0}.", vt));
// VarEnums which are not used in VARIANTs, but which can occur in a TYPEDESC
case VarEnum.VT_VOID:
type = null;
break;
#if DISABLE // TODO: WTypes.h indicates that these cannot be used in VARIANTs, but Type.InvokeMember seems to allow it
case VarEnum.VT_I8:
case VarEnum.UI8:
#endif
case VarEnum.VT_HRESULT:
type = typeof(int);
break;
case ((VarEnum)37): // VT_INT_PTR:
case VarEnum.VT_PTR:
type = typeof(IntPtr);
break;
case ((VarEnum)38): // VT_UINT_PTR:
type = typeof(UIntPtr);
break;
case VarEnum.VT_SAFEARRAY:
case VarEnum.VT_CARRAY:
type = typeof(Array);
break;
case VarEnum.VT_LPSTR:
case VarEnum.VT_LPWSTR:
type = typeof(string);
break;
case VarEnum.VT_USERDEFINED:
type = typeof(object);
break;
// For VarEnums that can be used in VARIANTs and well as TYPEDESCs, just use VarEnumSelector
default:
type = VarEnumSelector.GetManagedMarshalType(vt);
break;
}
return type;
}
public override string ToString() {
StringBuilder result = new StringBuilder();
if (_isOpt) {
result.Append("[Optional] ");
}
if (_isOut) {
result.Append("[out]");
}
result.Append(_type.Name);
if (_isArray) {
result.Append("[]");
}
if (_byRef) {
result.Append("&");
}
result.Append(" ");
result.Append(_name);
if (_defaultValue != DBNull.Value) {
result.Append("=");
result.Append(_defaultValue.ToString());
}
return result.ToString();
}
# endregion
# region properties
public bool IsOut {
get { return _isOut; }
}
public bool IsOptional {
get { return _isOpt; }
}
public bool ByReference {
get { return _byRef; }
}
public bool IsArray {
get { return _isArray; }
}
public Type ParameterType {
get {
return _type;
}
}
/// <summary>
/// DBNull.Value if there is no default value
/// </summary>
internal object DefaultValue {
get {
return _defaultValue;
}
}
# endregion
}
}
#endif
|