MegaBites-AI/Windows-powershell
0372
1// Licensed to the .NET Foundation under one or more agreements.2// The .NET Foundation licenses this file to you under the MIT license.3 4using System.Collections.Generic;5using System.Dynamic;6using System.Linq;7using System.Linq.Expressions;8using System.Management.Automation.Language;9using System.Runtime.InteropServices;10 11namespace System.Management.Automation.ComInterop12{13 /// <summary>14 /// Provides helper methods to bind COM objects dynamically.15 /// </summary>16 internal static class ComBinder17 {18 /// <summary>19 /// Determines if an object is a COM object.20 /// </summary>21 /// <param name="value">The object to test.</param>22 /// <returns>True if the object is a COM object, false otherwise.</returns>23 public static bool IsComObject(object value)24 {25 return value != null && Marshal.IsComObject(value);26 }27 28 /// <summary>29 /// Tries to perform binding of the dynamic get member operation.30 /// </summary>31 /// <param name="binder">An instance of the <see cref="GetMemberBinder"/> that represents the details of the dynamic operation.</param>32 /// <param name="instance">The target of the dynamic operation. </param>33 /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>34 /// <param name="delayInvocation">True if member evaluation may be delayed.</param>35 /// <returns>True if operation was bound successfully; otherwise, false.</returns>36 public static bool TryBindGetMember(GetMemberBinder binder, DynamicMetaObject instance, out DynamicMetaObject result, bool delayInvocation)37 {38 Requires.NotNull(binder);39 Requires.NotNull(instance);40 41 if (TryGetMetaObject(ref instance))42 {43 var comGetMember = new ComGetMemberBinder(binder, delayInvocation);44 result = instance.BindGetMember(comGetMember);45 if (result.Expression.Type.IsValueType)46 {47 result = new DynamicMetaObject(48 Expression.Convert(result.Expression, typeof(object)),49 result.Restrictions50 );51 }52 return true;53 }54 55 result = null;56 return false;57 }58 59 /// <summary>60 /// Tries to perform binding of the dynamic set member operation.61 /// </summary>62 /// <param name="binder">An instance of the <see cref="SetMemberBinder"/> that represents the details of the dynamic operation.</param>63 /// <param name="instance">The target of the dynamic operation.</param>64 /// <param name="value">The <see cref="DynamicMetaObject"/> representing the value for the set member operation.</param>65 /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>66 /// <returns>True if operation was bound successfully; otherwise, false.</returns>67 public static bool TryBindSetMember(SetMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject value, out DynamicMetaObject result)68 {69 Requires.NotNull(binder);70 Requires.NotNull(instance);71 Requires.NotNull(value);72 73 if (TryGetMetaObject(ref instance))74 {75 result = instance.BindSetMember(binder, value);76 result = new DynamicMetaObject(result.Expression, result.Restrictions.Merge(value.PSGetMethodArgumentRestriction()));77 return true;78 }79 80 result = null;81 return false;82 }83 84 /// <summary>85 /// Tries to perform binding of the dynamic invoke operation.86 /// </summary>87 /// <param name="binder">An instance of the <see cref="InvokeBinder"/> that represents the details of the dynamic operation.</param>88 /// <param name="instance">The target of the dynamic operation. </param>89 /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>90 /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>91 /// <returns>True if operation was bound successfully; otherwise, false.</returns>92 public static bool TryBindInvoke(InvokeBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result)93 {94 Requires.NotNull(binder);95 Requires.NotNull(instance);96 Requires.NotNull(args);97 98 if (TryGetMetaObjectInvoke(ref instance))99 {100 result = instance.BindInvoke(binder, args);101 return true;102 }103 104 result = null;105 return false;106 }107 108 /// <summary>109 /// Tries to perform binding of the dynamic invoke member operation.110 /// </summary>111 /// <param name="binder">An instance of the <see cref="InvokeMemberBinder"/> that represents the details of the dynamic operation.</param>112 /// <param name="isSetProperty">True if this is for setting a property, false otherwise.</param>113 /// <param name="instance">The target of the dynamic operation. </param>114 /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>115 /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>116 /// <returns>True if operation was bound successfully; otherwise, false.</returns>117 public static bool TryBindInvokeMember(InvokeMemberBinder binder, bool isSetProperty, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result)118 {119 Requires.NotNull(binder);120 Requires.NotNull(instance);121 Requires.NotNull(args);122 123 if (TryGetMetaObject(ref instance))124 {125 var comInvokeMember = new ComInvokeMemberBinder(binder, isSetProperty);126 result = instance.BindInvokeMember(comInvokeMember, args);127 128 BindingRestrictions argRestrictions = args.Aggregate(129 BindingRestrictions.Empty, (current, arg) => current.Merge(arg.PSGetMethodArgumentRestriction()));130 var newRestrictions = result.Restrictions.Merge(argRestrictions);131 132 if (result.Expression.Type.IsValueType)133 {134 result = new DynamicMetaObject(135 Expression.Convert(result.Expression, typeof(object)),136 newRestrictions137 );138 }139 else140 {141 result = new DynamicMetaObject(result.Expression, newRestrictions);142 }143 144 return true;145 }146 147 result = null;148 return false;149 }150 151 /// <summary>152 /// Tries to perform binding of the dynamic get index operation.153 /// </summary>154 /// <param name="binder">An instance of the <see cref="GetIndexBinder"/> that represents the details of the dynamic operation.</param>155 /// <param name="instance">The target of the dynamic operation. </param>156 /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>157 /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>158 /// <returns>True if operation was bound successfully; otherwise, false.</returns>159 public static bool TryBindGetIndex(GetIndexBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result)160 {161 Requires.NotNull(binder);162 Requires.NotNull(instance);163 Requires.NotNull(args);164 165 if (TryGetMetaObjectInvoke(ref instance))166 {167 result = instance.BindGetIndex(binder, args);168 return true;169 }170 171 result = null;172 return false;173 }174 175 /// <summary>176 /// Tries to perform binding of the dynamic set index operation.177 /// </summary>178 /// <param name="binder">An instance of the <see cref="SetIndexBinder"/> that represents the details of the dynamic operation.</param>179 /// <param name="instance">The target of the dynamic operation. </param>180 /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>181 /// <param name="value">The <see cref="DynamicMetaObject"/> representing the value for the set index operation.</param>182 /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>183 /// <returns>True if operation was bound successfully; otherwise, false.</returns>184 public static bool TryBindSetIndex(SetIndexBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, DynamicMetaObject value, out DynamicMetaObject result)185 {186 Requires.NotNull(binder);187 Requires.NotNull(instance);188 Requires.NotNull(args);189 Requires.NotNull(value);190 191 if (TryGetMetaObjectInvoke(ref instance))192 {193 result = instance.BindSetIndex(binder, args, value);194 result = new DynamicMetaObject(result.Expression, result.Restrictions.Merge(value.PSGetMethodArgumentRestriction()));195 return true;196 }197 198 result = null;199 return false;200 }201 202 /// <summary>203 /// Tries to perform binding of the dynamic Convert operation.204 /// </summary>205 /// <param name="binder">An instance of the <see cref="ConvertBinder"/> that represents the details of the dynamic operation.</param>206 /// <param name="instance">The target of the dynamic operation.</param>207 /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>208 /// <returns>True if operation was bound successfully; otherwise, false.</returns>209 public static bool TryConvert(ConvertBinder binder, DynamicMetaObject instance, out DynamicMetaObject result)210 {211 Requires.NotNull(binder);212 Requires.NotNull(instance);213 214 if (IsComObject(instance.Value))215 {216 // Converting a COM object to any interface is always considered possible - it will result in217 // a QueryInterface at runtime218 if (binder.Type.IsInterface)219 {220 result = new DynamicMetaObject(221 Expression.Convert(222 instance.Expression,223 binder.Type224 ),225 BindingRestrictions.GetExpressionRestriction(226 Expression.Call(227 typeof(ComBinder).GetMethod(nameof(ComBinder.IsComObject), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public),228 Helpers.Convert(instance.Expression, typeof(object))229 )230 )231 );232 return true;233 }234 }235 236 result = null;237 return false;238 }239 240 /// <summary>241 /// Gets the member names of the data-like members associated with the object.242 /// This function can operate only with objects for which <see cref="IsComObject"/> returns true.243 /// </summary>244 /// <param name="value">The object for which member names are requested.</param>245 /// <returns>The collection of member names.</returns>246 internal static IList<string> GetDynamicDataMemberNames(object value)247 {248 Requires.NotNull(value);249 Requires.Condition(IsComObject(value), nameof(value));250 251 return ComObject.ObjectToComObject(value).GetMemberNames(true);252 }253 254 /// <summary>255 /// Gets the data-like members and associated data for an object.256 /// This function can operate only with objects for which <see cref="IsComObject"/> returns true.257 /// </summary>258 /// <param name="value">The object for which data members are requested.</param>259 /// <param name="names">The enumeration of names of data members for which to retrieve values.</param>260 /// <returns>The collection of pairs that represent data member's names and their data.</returns>261 internal static IList<KeyValuePair<string, object>> GetDynamicDataMembers(object value, IEnumerable<string> names)262 {263 Requires.NotNull(value);264 Requires.Condition(IsComObject(value), nameof(value));265 266 return ComObject.ObjectToComObject(value).GetMembers(names);267 }268 269 private static bool TryGetMetaObject(ref DynamicMetaObject instance)270 {271 // If we're already a COM MO don't make a new one272 // (we do this to prevent recursion if we call Fallback from COM)273 if (instance is ComUnwrappedMetaObject)274 {275 return false;276 }277 278 if (IsComObject(instance.Value))279 {280 instance = new ComMetaObject(instance.Expression, instance.Restrictions, instance.Value);281 return true;282 }283 284 return false;285 }286 287 private static bool TryGetMetaObjectInvoke(ref DynamicMetaObject instance)288 {289 // If we're already a COM MO don't make a new one290 // (we do this to prevent recursion if we call Fallback from COM)291 if (TryGetMetaObject(ref instance))292 {293 return true;294 }295 296 if (instance.Value is IPseudoComObject o)297 {298 instance = o.GetMetaObject(instance.Expression);299 return true;300 }301 302 return false;303 }304 305 /// <summary>306 /// Special binder that indicates special semantics for COM GetMember operation.307 /// </summary>308 internal class ComGetMemberBinder : GetMemberBinder309 {310 private readonly GetMemberBinder _originalBinder;311 internal bool _canReturnCallables;312 313 internal ComGetMemberBinder(GetMemberBinder originalBinder, bool canReturnCallables)314 : base(originalBinder.Name, originalBinder.IgnoreCase)315 {316 _originalBinder = originalBinder;317 _canReturnCallables = canReturnCallables;318 }319 320 public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)321 {322 return _originalBinder.FallbackGetMember(target, errorSuggestion);323 }324 325 public override int GetHashCode()326 {327 return _originalBinder.GetHashCode() ^ (_canReturnCallables ? 1 : 0);328 }329 330 public override bool Equals(object obj)331 {332 return obj is ComGetMemberBinder other333 && _canReturnCallables == other._canReturnCallables334 && _originalBinder.Equals(other._originalBinder);335 }336 }337 338 /// <summary>339 /// Special binder that indicates special semantics for COM InvokeMember operation.340 /// </summary>341 internal class ComInvokeMemberBinder : InvokeMemberBinder342 {343 private readonly InvokeMemberBinder _originalBinder;344 internal bool IsPropertySet;345 346 internal ComInvokeMemberBinder(InvokeMemberBinder originalBinder, bool isPropertySet)347 : base(originalBinder.Name, originalBinder.IgnoreCase, originalBinder.CallInfo)348 {349 _originalBinder = originalBinder;350 this.IsPropertySet = isPropertySet;351 }352 353 public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)354 {355 return _originalBinder.FallbackInvoke(target, args, errorSuggestion);356 }357 358 public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)359 {360 return _originalBinder.FallbackInvokeMember(target, args, errorSuggestion);361 }362 363 public override int GetHashCode()364 {365 return _originalBinder.GetHashCode() ^ (IsPropertySet ? 1 : 0);366 }367 368 public override bool Equals(object obj)369 {370 ComInvokeMemberBinder other = obj as ComInvokeMemberBinder;371 return other != null &&372 IsPropertySet == other.IsPropertySet &&373 _originalBinder.Equals(other._originalBinder);374 }375 }376 }377}378 