From 15d6ce2a602c1af424f8e289795512838f079085 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Thu, 10 Aug 2017 18:22:38 -0700 Subject: [PATCH 01/11] Make PowerShell Core enumerate COM collections --- .../engine/COM/ComTypeInfo.cs | 28 ++++++-- .../engine/COM/ComUtil.cs | 71 +++++++++++++++++++ .../engine/runtime/Operations/MiscOps.cs | 18 +---- 3 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/System.Management.Automation/engine/COM/ComTypeInfo.cs b/src/System.Management.Automation/engine/COM/ComTypeInfo.cs index 28a881c458e..971d83f89a7 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 @@ internal ComTypeInfo(COM.ITypeInfo info) /// /// Collection of properties in the COM object. /// - public Dictionary Properties + internal Dictionary Properties { get { @@ -55,7 +67,7 @@ public Dictionary Properties /// /// Collection of methods in the COM object. /// - public Dictionary Methods + internal Dictionary Methods { get { @@ -63,12 +75,10 @@ public Dictionary Methods } } - - /// /// Returns the string of the GUID for the type information. /// - public string Clsid + internal string Clsid { get { @@ -76,6 +86,12 @@ public string Clsid } } + /// + /// 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 @@ private void Initialize() 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 9f30fd47ef6..d674fda7295 100644 --- a/src/System.Management.Automation/engine/COM/ComUtil.cs +++ b/src/System.Management.Automation/engine/COM/ComUtil.cs @@ -5,6 +5,7 @@ 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,74 @@ internal static ComMethodInformation[] GetMethodInformationArray(COM.ITypeInfo t return returnValue; } } + + /// + /// 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(); + } + + internal static ComEnumerator Create(object comObject) + { + if (!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) + { + // 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(), byRef: null, + invokeKind: comTypeInfo.NewEnumInvokeKind.Value); + enumVariant = result as COM.IEnumVARIANT; + } + } + catch (Exception) + { + /* Catch all exception. */ + } + } + } + + return enumVariant != null ? new ComEnumerator(enumVariant) : null; + } + } } \ No newline at end of file diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 80e2effc1a6..8bece06ab61 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -3199,23 +3199,7 @@ internal object GetNonEnumerableObject() internal static IEnumerator GetCOMEnumerator(object obj) { object targetValue = PSObject.Base(obj); - try - { - IEnumerable enumerable = targetValue as IEnumerable; - if (enumerable != null) - { - var enumerator = enumerable.GetEnumerator(); - if (enumerator != null) - { - return enumerator; - } - } - } - catch (Exception) - { - } - - return targetValue as IEnumerator ?? NonEnumerableObjectEnumerator.Create(obj); + return ComEnumerator.Create(targetValue) ?? NonEnumerableObjectEnumerator.Create(obj); } internal static IEnumerator GetGenericEnumerator(IEnumerable enumerable) From 3ee11d84da86d1341b618971c031e6d2927a543f Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 11 Aug 2017 09:59:06 -0700 Subject: [PATCH 02/11] Add tests --- .../engine/runtime/Operations/MiscOps.cs | 11 ++++++++ .../powershell/engine/COM/COM.Basic.Tests.ps1 | 26 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 test/powershell/engine/COM/COM.Basic.Tests.ps1 diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 8bece06ab61..d5ab00d9389 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -3199,6 +3199,17 @@ internal object GetNonEnumerableObject() internal static IEnumerator GetCOMEnumerator(object obj) { object targetValue = PSObject.Base(obj); + + // We use ComEnumerator to enumerate COM collections because the following code doesn't work in .NET Core + // IEnumerable enumerable = targetValue as IEnumerable; + // if (enumerable != null) + // { + // var enumerator = enumerable.GetEnumerator(); + // ... + // } + // The call to 'GetEnumerator()' throws exception because COM is not supported in .NET Core. + // See https://github.com/dotnet/corefx/issues/19731 for more information. + // When COM support is back to .NET Core, we need to change back to the original implementation. return ComEnumerator.Create(targetValue) ?? NonEnumerableObjectEnumerator.Create(obj); } diff --git a/test/powershell/engine/COM/COM.Basic.Tests.ps1 b/test/powershell/engine/COM/COM.Basic.Tests.ps1 new file mode 100644 index 00000000000..a5411886c5f --- /dev/null +++ b/test/powershell/engine/COM/COM.Basic.Tests.ps1 @@ -0,0 +1,26 @@ +Describe 'Basic COM Tests' -Tags "CI" { + It "Should enumerate ShellWindows" { + $shell = New-Object -ComObject "Shell.Application" + $windows = $shell.Windows() + + ## $windows is a collection of all of the open windows that belong to the Shell, and it should be enumerated. + ## - If there are any open shell windows, then $element will be the first window from the enumeration; + ## - If there is no open shell window ($windows is an empty collection), then $element will be $null. + ## So in either case, $element should not be the same as $windows + $element = $windows | Select-Object -First 1 + $element | Should Not Be $windows + } + + It "Should enumerate drives" { + $fileSystem = New-Object -ComObject scripting.filesystemobject + $drives = $fileSystem.Drives + + ## $drives is a read-only collection of all available drives, and it should be enumerated. + $drives | Measure-Command | ForEach-Object Count | Should Be $drives.Count + ## $element should be the first drive from the enumeration. It shouldn't be the same as $drives, + ## but it should be the same as '$drives.Item($element.DriveLetter)' + $element = $drives | Select-Object -First 1 + $element | Should Not Be $drives + $element | Should Be $drives.Item($element.DriveLetter) + } +} From d49de32ed5b377555eabb240677da8ee1cff3dbc Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 11 Aug 2017 10:56:11 -0700 Subject: [PATCH 03/11] Skip COM tests on Unix --- .../powershell/engine/COM/COM.Basic.Tests.ps1 | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/test/powershell/engine/COM/COM.Basic.Tests.ps1 b/test/powershell/engine/COM/COM.Basic.Tests.ps1 index a5411886c5f..66beef4c3d7 100644 --- a/test/powershell/engine/COM/COM.Basic.Tests.ps1 +++ b/test/powershell/engine/COM/COM.Basic.Tests.ps1 @@ -1,26 +1,35 @@ -Describe 'Basic COM Tests' -Tags "CI" { - It "Should enumerate ShellWindows" { - $shell = New-Object -ComObject "Shell.Application" - $windows = $shell.Windows() - ## $windows is a collection of all of the open windows that belong to the Shell, and it should be enumerated. - ## - If there are any open shell windows, then $element will be the first window from the enumeration; - ## - If there is no open shell window ($windows is an empty collection), then $element will be $null. - ## So in either case, $element should not be the same as $windows - $element = $windows | Select-Object -First 1 - $element | Should Not Be $windows - } +try { + $defaultParamValues = $PSdefaultParameterValues.Clone() + $PSDefaultParameterValues["it:skip"] = !$IsWindows + + Describe 'Basic COM Tests' -Tags "CI" { + It "Should enumerate ShellWindows" { + $shell = New-Object -ComObject "Shell.Application" + $windows = $shell.Windows() - It "Should enumerate drives" { - $fileSystem = New-Object -ComObject scripting.filesystemobject - $drives = $fileSystem.Drives + ## $windows is a collection of all of the open windows that belong to the Shell, and it should be enumerated. + ## - If there are any open shell windows, then $element will be the first window from the enumeration; + ## - If there is no open shell window ($windows is an empty collection), then $element will be $null. + ## So in either case, $element should not be the same as $windows + $element = $windows | Select-Object -First 1 + $element | Should Not Be $windows + } - ## $drives is a read-only collection of all available drives, and it should be enumerated. - $drives | Measure-Command | ForEach-Object Count | Should Be $drives.Count - ## $element should be the first drive from the enumeration. It shouldn't be the same as $drives, - ## but it should be the same as '$drives.Item($element.DriveLetter)' - $element = $drives | Select-Object -First 1 - $element | Should Not Be $drives - $element | Should Be $drives.Item($element.DriveLetter) + It "Should enumerate drives" { + $fileSystem = New-Object -ComObject scripting.filesystemobject + $drives = $fileSystem.Drives + + ## $drives is a read-only collection of all available drives, and it should be enumerated. + $drives | Measure-Command | ForEach-Object Count | Should Be $drives.Count + ## $element should be the first drive from the enumeration. It shouldn't be the same as $drives, + ## but it should be the same as '$drives.Item($element.DriveLetter)' + $element = $drives | Select-Object -First 1 + $element | Should Not Be $drives + $element | Should Be $drives.Item($element.DriveLetter) + } } + +} finally { + $global:PSdefaultParameterValues = $defaultParamValues } From 4d6e365cfcba250bf3f17c425a440608aca1c385 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 11 Aug 2017 15:28:08 -0700 Subject: [PATCH 04/11] Add a new line to the end of ComUtil.cs --- src/System.Management.Automation/engine/COM/ComUtil.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/COM/ComUtil.cs b/src/System.Management.Automation/engine/COM/ComUtil.cs index d674fda7295..16197367478 100644 --- a/src/System.Management.Automation/engine/COM/ComUtil.cs +++ b/src/System.Management.Automation/engine/COM/ComUtil.cs @@ -426,4 +426,4 @@ internal static ComEnumerator Create(object comObject) return enumVariant != null ? new ComEnumerator(enumVariant) : null; } } -} \ No newline at end of file +} From 3b53c0fef8b63b4a07d6571ba5cf2b2c04235f23 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 11 Aug 2017 15:59:40 -0700 Subject: [PATCH 05/11] Fix failing test --- test/powershell/engine/COM/COM.Basic.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/powershell/engine/COM/COM.Basic.Tests.ps1 b/test/powershell/engine/COM/COM.Basic.Tests.ps1 index 66beef4c3d7..542d03b07da 100644 --- a/test/powershell/engine/COM/COM.Basic.Tests.ps1 +++ b/test/powershell/engine/COM/COM.Basic.Tests.ps1 @@ -13,7 +13,7 @@ try { ## - If there is no open shell window ($windows is an empty collection), then $element will be $null. ## So in either case, $element should not be the same as $windows $element = $windows | Select-Object -First 1 - $element | Should Not Be $windows + [System.Object]::ReferenceEquals($element, $windows) | Should Be $false } It "Should enumerate drives" { @@ -21,11 +21,11 @@ try { $drives = $fileSystem.Drives ## $drives is a read-only collection of all available drives, and it should be enumerated. - $drives | Measure-Command | ForEach-Object Count | Should Be $drives.Count + $drives | Measure-Object | ForEach-Object Count | Should Be $drives.Count ## $element should be the first drive from the enumeration. It shouldn't be the same as $drives, ## but it should be the same as '$drives.Item($element.DriveLetter)' $element = $drives | Select-Object -First 1 - $element | Should Not Be $drives + [System.Object]::ReferenceEquals($element, $drives) | Should Be $false $element | Should Be $drives.Item($element.DriveLetter) } } From 5490f76a8c69d7999d4654c2807d1ddfd4fa89a9 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Mon, 14 Aug 2017 09:58:12 -0700 Subject: [PATCH 06/11] Address 2 more comments --- src/System.Management.Automation/engine/COM/ComUtil.cs | 2 +- test/powershell/engine/COM/COM.Basic.Tests.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/COM/ComUtil.cs b/src/System.Management.Automation/engine/COM/ComUtil.cs index 16197367478..e1f0b2a7ddc 100644 --- a/src/System.Management.Automation/engine/COM/ComUtil.cs +++ b/src/System.Management.Automation/engine/COM/ComUtil.cs @@ -391,7 +391,7 @@ public void Reset() internal static ComEnumerator Create(object comObject) { - if (!comObject.GetType().IsCOMObject) { return null; } + 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. diff --git a/test/powershell/engine/COM/COM.Basic.Tests.ps1 b/test/powershell/engine/COM/COM.Basic.Tests.ps1 index 542d03b07da..4c6e055ba7d 100644 --- a/test/powershell/engine/COM/COM.Basic.Tests.ps1 +++ b/test/powershell/engine/COM/COM.Basic.Tests.ps1 @@ -1,7 +1,7 @@ try { $defaultParamValues = $PSdefaultParameterValues.Clone() - $PSDefaultParameterValues["it:skip"] = !$IsWindows + $PSDefaultParameterValues["it:skip"] = ![System.Management.Automation.Platform]::IsWindowsDesktop Describe 'Basic COM Tests' -Tags "CI" { It "Should enumerate ShellWindows" { From 1d0ce460912d3f70793f3d1431dd255faca6d3f3 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 15 Aug 2017 09:19:39 -0700 Subject: [PATCH 07/11] Add more comment for the catch-all-exception block --- src/System.Management.Automation/engine/COM/ComUtil.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/COM/ComUtil.cs b/src/System.Management.Automation/engine/COM/ComUtil.cs index e1f0b2a7ddc..0ce38bf1fbf 100644 --- a/src/System.Management.Automation/engine/COM/ComUtil.cs +++ b/src/System.Management.Automation/engine/COM/ComUtil.cs @@ -418,7 +418,8 @@ internal static ComEnumerator Create(object comObject) } catch (Exception) { - /* Catch all exception. */ + // Ignore exceptions. In case of exception, no enumerator can be created + // for the passed-in COM object, and we will return null eventually. } } } From 50ae76cb742a91435c23cdb6ca4eb51bf3a38f3b Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 15 Aug 2017 09:27:25 -0700 Subject: [PATCH 08/11] Add comment block for the method --- src/System.Management.Automation/engine/COM/ComUtil.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/System.Management.Automation/engine/COM/ComUtil.cs b/src/System.Management.Automation/engine/COM/ComUtil.cs index 0ce38bf1fbf..1f032c39f29 100644 --- a/src/System.Management.Automation/engine/COM/ComUtil.cs +++ b/src/System.Management.Automation/engine/COM/ComUtil.cs @@ -389,6 +389,12 @@ public void Reset() _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; } From 864a58d55c1111d7b9989fa2197c537a7e05cff8 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 15 Aug 2017 12:25:14 -0700 Subject: [PATCH 09/11] Address another comment and add one more test --- .../engine/COM/ComUtil.cs | 46 +++++++++++-------- .../powershell/engine/COM/COM.Basic.Tests.ps1 | 13 ++++++ 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/System.Management.Automation/engine/COM/ComUtil.cs b/src/System.Management.Automation/engine/COM/ComUtil.cs index 1f032c39f29..d77752abcc2 100644 --- a/src/System.Management.Automation/engine/COM/ComUtil.cs +++ b/src/System.Management.Automation/engine/COM/ComUtil.cs @@ -402,35 +402,41 @@ internal static ComEnumerator Create(object comObject) // 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) + if (enumVariant != null) { - // The passed-in COM object could be a collection. - var enumerable = comObject as IEnumerable; - var target = comObject as IDispatch; - if (enumerable != null && target != 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 { - try + var comTypeInfo = ComTypeInfo.GetDispatchTypeInfo(comObject); + if (comTypeInfo != null && comTypeInfo.NewEnumInvokeKind.HasValue) { - 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(), byRef: null, + invokeKind: comTypeInfo.NewEnumInvokeKind.Value); + enumVariant = result as COM.IEnumVARIANT; + if (enumVariant != null) { - // 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(), byRef: null, - invokeKind: comTypeInfo.NewEnumInvokeKind.Value); - enumVariant = result as COM.IEnumVARIANT; + return new ComEnumerator(enumVariant); } } - catch (Exception) - { - // Ignore exceptions. In case of exception, no enumerator can be created - // for the passed-in COM object, and we will return null eventually. - } + } + catch (Exception) + { + // Ignore exceptions. In case of exception, no enumerator can be created + // for the passed-in COM object, and we will return null eventually. } } - return enumVariant != null ? new ComEnumerator(enumVariant) : null; + return null; } } } diff --git a/test/powershell/engine/COM/COM.Basic.Tests.ps1 b/test/powershell/engine/COM/COM.Basic.Tests.ps1 index 4c6e055ba7d..f7cb434cb35 100644 --- a/test/powershell/engine/COM/COM.Basic.Tests.ps1 +++ b/test/powershell/engine/COM/COM.Basic.Tests.ps1 @@ -16,6 +16,19 @@ try { [System.Object]::ReferenceEquals($element, $windows) | Should Be $false } + It "Should enumerate IEnumVariant interface object without exception" { + $shell = New-Object -ComObject "Shell.Application" + $windows = $shell.Windows() + $enumVariant = $windows._NewEnum() + + ## $enumVariant is an IEnumVariant interface of all of the open windows that belong to the Shell, and it should be enumerated. + ## - If there are any open shell windows, then $element will be the first window from the enumeration; + ## - If there is no open shell window ($enumVariant refers to an empty collection), then $element will be $null. + ## So in either case, $element should not be the same as $enumVariant + $element = $enumVariant | Select-Object -First 1 + [System.Object]::ReferenceEquals($element, $enumVariant) | Should Be $false + } + It "Should enumerate drives" { $fileSystem = New-Object -ComObject scripting.filesystemobject $drives = $fileSystem.Drives From 7a06a827023b149f62a8ffdb1a7ebe04038f7acc Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 16 Aug 2017 12:10:33 -0700 Subject: [PATCH 10/11] Replace shell-windows tests with shell-folder tests --- .../powershell/engine/COM/COM.Basic.Tests.ps1 | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/test/powershell/engine/COM/COM.Basic.Tests.ps1 b/test/powershell/engine/COM/COM.Basic.Tests.ps1 index f7cb434cb35..78c21240788 100644 --- a/test/powershell/engine/COM/COM.Basic.Tests.ps1 +++ b/test/powershell/engine/COM/COM.Basic.Tests.ps1 @@ -4,29 +4,32 @@ try { $PSDefaultParameterValues["it:skip"] = ![System.Management.Automation.Platform]::IsWindowsDesktop Describe 'Basic COM Tests' -Tags "CI" { - It "Should enumerate ShellWindows" { + BeforeAll { + $null = New-Item -Path $TESTDRIVE/file1 -ItemType File + $null = New-Item -Path $TESTDRIVE/file2 -ItemType File + $null = New-Item -Path $TESTDRIVE/file3 -ItemType File + } + + It "Should enumerate files from a folder" { $shell = New-Object -ComObject "Shell.Application" - $windows = $shell.Windows() - - ## $windows is a collection of all of the open windows that belong to the Shell, and it should be enumerated. - ## - If there are any open shell windows, then $element will be the first window from the enumeration; - ## - If there is no open shell window ($windows is an empty collection), then $element will be $null. - ## So in either case, $element should not be the same as $windows - $element = $windows | Select-Object -First 1 - [System.Object]::ReferenceEquals($element, $windows) | Should Be $false + $folder = $shell.Namespace("$TESTDRIVE") + $items = $folder.Items() + + ## $items is a collection of all items belong to the folder, and it should be enumerated. + $items | Measure-Object | ForEach-Object Count | Should Be $items.Count + $names = $items | ForEach-Object { $_.Name } + $names -join "," | Should Be "file1,file2,file3" } It "Should enumerate IEnumVariant interface object without exception" { $shell = New-Object -ComObject "Shell.Application" - $windows = $shell.Windows() - $enumVariant = $windows._NewEnum() - - ## $enumVariant is an IEnumVariant interface of all of the open windows that belong to the Shell, and it should be enumerated. - ## - If there are any open shell windows, then $element will be the first window from the enumeration; - ## - If there is no open shell window ($enumVariant refers to an empty collection), then $element will be $null. - ## So in either case, $element should not be the same as $enumVariant - $element = $enumVariant | Select-Object -First 1 - [System.Object]::ReferenceEquals($element, $enumVariant) | Should Be $false + $folder = $shell.Namespace("$TESTDRIVE") + $items = $folder.Items() + + ## $enumVariant is an IEnumVariant interface of all items belong to the folder, and it should be enumerated. + $enumVariant = $items._NewEnum() + $names = $enumVariant | ForEach-Object { $_.Name } + $names -join "," | Should Be "file1,file2,file3" } It "Should enumerate drives" { From 459518d6e92fd175f5e29980f932705d31b949c8 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 16 Aug 2017 22:30:02 -0700 Subject: [PATCH 11/11] Minor change in test --- test/powershell/engine/COM/COM.Basic.Tests.ps1 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/powershell/engine/COM/COM.Basic.Tests.ps1 b/test/powershell/engine/COM/COM.Basic.Tests.ps1 index 78c21240788..f98139f9b83 100644 --- a/test/powershell/engine/COM/COM.Basic.Tests.ps1 +++ b/test/powershell/engine/COM/COM.Basic.Tests.ps1 @@ -17,8 +17,6 @@ try { ## $items is a collection of all items belong to the folder, and it should be enumerated. $items | Measure-Object | ForEach-Object Count | Should Be $items.Count - $names = $items | ForEach-Object { $_.Name } - $names -join "," | Should Be "file1,file2,file3" } It "Should enumerate IEnumVariant interface object without exception" { @@ -28,8 +26,7 @@ try { ## $enumVariant is an IEnumVariant interface of all items belong to the folder, and it should be enumerated. $enumVariant = $items._NewEnum() - $names = $enumVariant | ForEach-Object { $_.Name } - $names -join "," | Should Be "file1,file2,file3" + $enumVariant | Measure-Object | ForEach-Object Count | Should Be $items.Count } It "Should enumerate drives" {