Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 15 additions & 32 deletions src/System.Management.Automation/engine/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1400,43 +1400,26 @@ internal static class Separators
internal static readonly char[] PathSearchTrimEnd = { (char)0x9, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20, (char)0x85, (char)0xA0 };
}

#if !UNIX
// This is to reduce the runtime overhead of the feature query
private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject");
#endif

internal static bool IsComObject(PSObject psObject)
{
#if UNIX
return false;
#else
if (psObject == null) { return false; }

object obj = PSObject.Base(psObject);
return IsComObject(obj);
#endif
}

/// <summary>
/// A COM object could be directly of the type 'System.__ComObject', or it could be a strongly typed RWC,
/// whose specific type derives from 'System.__ComObject'.
/// A strongly typed RWC can be created via the 'new' operation with a Primary Interop Assembly (PIA).
/// For example, with the PIA 'Microsoft.Office.Interop.Excel', you can write the following code:
/// var excelApp = new Microsoft.Office.Interop.Excel.Application();
/// Type type = excelApp.GetType();
/// Type comObjectType = typeof(object).Assembly.GetType("System.__ComObject");
/// Console.WriteLine("excelApp type: {0}", type.FullName);
/// Console.WriteLine("Is __ComObject assignable from? {0}", comObjectType.IsAssignableFrom(type));
/// and the results are:
/// excelApp type: Microsoft.Office.Interop.Excel.ApplicationClass
/// Is __ComObject assignable from? True
/// </summary>
internal static bool IsComObject(object obj)
{
#if UNIX
return false;
#else
// We can't use System.Runtime.InteropServices.Marshal.IsComObject(obj) since it doesn't work in partial trust.
//
// There could be strongly typed RWCs whose type is not 'System.__ComObject', but the more specific type should
// derive from 'System.__ComObject'. The strongly typed RWCs can be created with 'new' operation via the Primay
// Interop Assembly (PIA).
// For example, with the PIA 'Microsoft.Office.Interop.Excel', you can write the following code:
// var excelApp = new Microsoft.Office.Interop.Excel.Application();
// Type type = excelApp.GetType();
// Type comObjectType = typeof(object).Assembly.GetType("System.__ComObject");
// Console.WriteLine("excelApp type: {0}", type.FullName);
// Console.WriteLine("Is __ComObject assignable from? {0}", comObjectType.IsAssignableFrom(type));
// and the results are:
// excelApp type: Microsoft.Office.Interop.Excel.ApplicationClass
// Is __ComObject assignable from? True
return obj != null && ComObjectType.IsAssignableFrom(obj.GetType());
return obj != null && Marshal.IsComObject(obj);
#endif
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ internal static class CachedReflectionInfo
typeof(VariableOps).GetMethod(nameof(VariableOps.SetVariableValue), staticFlags);

internal static readonly MethodInfo Utils_IsComObject =
typeof(Utils).GetMethod(nameof(Utils.IsComObject), staticFlags, binder: null, types: new Type[] {typeof(object)}, modifiers: null);
typeof(Utils).GetMethod(nameof(Utils.IsComObject), staticFlags);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems it is single place where we use the Utils.IsComObject method. Could you please clarify why we can not use Marshal.IsComObject here? Null check?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marshal.IsComObject cannot accept null input -- it throws ArgumentNullException if the argument is null. Utils.IsComObject handles null input properly. For the rest call sites where I replaced with Marshal.IsComObject, the input object is guaranteed to be non-null.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!


internal static readonly MethodInfo ClassOps_ValidateSetProperty =
typeof(ClassOps).GetMethod(nameof(ClassOps.ValidateSetProperty), staticPublicFlags);
Expand Down
16 changes: 11 additions & 5 deletions src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using System.Management.Automation.Runspaces;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
Expand Down Expand Up @@ -619,7 +620,7 @@ public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, Dyna
GetRestrictions(target))).WriteToDebugLog(this);
}

if (Utils.IsComObject(targetValue))
if (Marshal.IsComObject(targetValue))
{
// Pretend that all com objects are enumerable, even if they aren't. We do this because it's technically impossible
// to know if a com object is enumerable without just trying to cast it to IEnumerable. We could generate a rule like:
Expand Down Expand Up @@ -715,7 +716,12 @@ private static IEnumerator AutomationNullRule(CallSite site, object obj)

private static IEnumerator NotEnumerableRule(CallSite site, object obj)
{
if (!(obj is PSObject) && !(obj is IEnumerable) && !(obj is IEnumerator) && !(obj is DataTable) && !Utils.IsComObject(obj))
if (obj == null)
{
return null;
}

if (!(obj is PSObject) && !(obj is IEnumerable) && !(obj is IEnumerator) && !(obj is DataTable) && !Marshal.IsComObject(obj))
{
return null;
}
Expand Down Expand Up @@ -4988,7 +4994,7 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy
if (target.Value is PSObject && (PSObject.Base(target.Value) != target.Value))
{
Object baseObject = PSObject.Base(target.Value);
if (baseObject != null && Utils.IsComObject(baseObject))
if (baseObject != null && Marshal.IsComObject(baseObject))
{
// We unwrap only if the 'base' is a COM object. It's unnecessary to unwrap in other cases,
// especially in the case of strings, we would lose instance members on the PSObject.
Expand Down Expand Up @@ -5859,7 +5865,7 @@ public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, Dy
(value.Value is PSObject && (PSObject.Base(value.Value) != value.Value)))
{
Object baseObject = PSObject.Base(target.Value);
if (baseObject != null && Utils.IsComObject(baseObject))
if (baseObject != null && Marshal.IsComObject(baseObject))
{
// We unwrap only if the 'base' of 'target' is a COM object. It's unnecessary to unwrap in other cases,
// especially in the case that 'target' is a string, we would lose instance members on the PSObject.
Expand Down Expand Up @@ -6380,7 +6386,7 @@ public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target,
args.Any(mo => mo.Value is PSObject && (PSObject.Base(mo.Value) != mo.Value)))
{
Object baseObject = PSObject.Base(target.Value);
if (baseObject != null && Utils.IsComObject(baseObject))
if (baseObject != null && Marshal.IsComObject(baseObject))
{
// We unwrap only if the 'base' of 'target' is a COM object. It's unnecessary to unwrap in other cases,
// especially in the case that 'target' is a string, we would lose instance members on the PSObject.
Expand Down