diff --git a/src/System.Management.Automation/engine/COM/ComTypeInfo.cs b/src/System.Management.Automation/engine/COM/ComTypeInfo.cs
index 28a881c458..971d83f89a 100644
--- a/src/System.Management.Automation/engine/COM/ComTypeInfo.cs
+++ b/src/System.Management.Automation/engine/COM/ComTypeInfo.cs
@@ -15,6 +15,18 @@ namespace System.Management.Automation
///
internal class ComTypeInfo
{
+ ///
+ /// A member with a DISPID equal to –4 is found on a collection interface.
+ /// This special member, often called '_NewEnum', returns an interface that enables clients to enumerate objects in a collection.
+ ///
+ internal const int DISPID_NEWENUM = -4;
+
+ ///
+ /// A member with a DISPID equal to 0 is considered a default member.
+ /// Default members in COM can be transformed to default members in .NET (indexers in C#).
+ ///
+ internal const int DISPID_DEFAULTMEMBER = 0;
+
///
/// Member variables.
///
@@ -44,7 +56,7 @@ namespace System.Management.Automation
///
/// Collection of properties in the COM object.
///
- public Dictionary Properties
+ internal Dictionary Properties
{
get
{
@@ -55,7 +67,7 @@ namespace System.Management.Automation
///
/// Collection of methods in the COM object.
///
- public Dictionary Methods
+ internal Dictionary Methods
{
get
{
@@ -63,12 +75,10 @@ namespace System.Management.Automation
}
}
-
-
///
/// Returns the string of the GUID for the type information.
///
- public string Clsid
+ internal string Clsid
{
get
{
@@ -76,6 +86,12 @@ namespace System.Management.Automation
}
}
+ ///
+ /// If 'DISPID_NEWENUM' member is present, return the InvokeKind;
+ /// otherwise, return null.
+ ///
+ internal COM.INVOKEKIND? NewEnumInvokeKind { get; private set; }
+
///
/// Initializes the typeinfo object
///
@@ -91,6 +107,8 @@ namespace System.Management.Automation
for (int i = 0; i < typeattr.cFuncs; i++)
{
COM.FUNCDESC funcdesc = GetFuncDesc(_typeinfo, i);
+ if (funcdesc.memid == DISPID_NEWENUM) { NewEnumInvokeKind = funcdesc.invkind; }
+
if ((funcdesc.wFuncFlags & 0x1) == 0x1)
{
// http://msdn.microsoft.com/en-us/library/ee488948.aspx
diff --git a/src/System.Management.Automation/engine/COM/ComUtil.cs b/src/System.Management.Automation/engine/COM/ComUtil.cs
index 9f30fd47ef..d77752abcc 100644
--- a/src/System.Management.Automation/engine/COM/ComUtil.cs
+++ b/src/System.Management.Automation/engine/COM/ComUtil.cs
@@ -5,6 +5,7 @@ Copyright (c) Microsoft Corporation. All rights reserved.
using System.Runtime.InteropServices;
using System.Management.Automation.ComInterop;
using System.Text;
+using System.Collections;
using System.Collections.ObjectModel;
using COM = System.Runtime.InteropServices.ComTypes;
@@ -355,4 +356,87 @@ namespace System.Management.Automation
return returnValue;
}
}
-}
\ No newline at end of file
+
+ ///
+ /// Defines an enumerator that represent a COM collection object.
+ ///
+ internal class ComEnumerator : IEnumerator
+ {
+ private COM.IEnumVARIANT _enumVariant;
+ private object[] _element;
+
+ private ComEnumerator(COM.IEnumVARIANT enumVariant)
+ {
+ _enumVariant = enumVariant;
+ _element = new object[1];
+ }
+
+ public object Current
+ {
+ get { return _element[0]; }
+ }
+
+ public bool MoveNext()
+ {
+ _element[0] = null;
+ int result = _enumVariant.Next(1, _element, IntPtr.Zero);
+ return result == 0;
+ }
+
+ public void Reset()
+ {
+ _element[0] = null;
+ _enumVariant.Reset();
+ }
+
+ ///
+ /// Try to create an enumerator for a COM object.
+ ///
+ ///
+ /// A 'ComEnumerator' instance, or null if we cannot create an enumerator for the COM object.
+ ///
+ internal static ComEnumerator Create(object comObject)
+ {
+ if (comObject == null || !comObject.GetType().IsCOMObject) { return null; }
+
+ // The passed-in COM object could already be a IEnumVARIANT interface.
+ // e.g. user call '_NewEnum()' on a COM collection interface.
+ var enumVariant = comObject as COM.IEnumVARIANT;
+ if (enumVariant != null)
+ {
+ return new ComEnumerator(enumVariant);
+ }
+
+ // The passed-in COM object could be a collection.
+ var enumerable = comObject as IEnumerable;
+ var target = comObject as IDispatch;
+ if (enumerable != null && target != null)
+ {
+ try
+ {
+ var comTypeInfo = ComTypeInfo.GetDispatchTypeInfo(comObject);
+ if (comTypeInfo != null && comTypeInfo.NewEnumInvokeKind.HasValue)
+ {
+ // The COM object is a collection and also a IDispatch interface, so we try to get a
+ // IEnumVARIANT interface out of it by invoking its '_NewEnum (DispId: -4)' function.
+ var result = ComInvoker.Invoke(target, ComTypeInfo.DISPID_NEWENUM,
+ args: Utils.EmptyArray