Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
ExtraAdapter.cs386 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.DirectoryServices;7using System.Reflection;8using System.Runtime.InteropServices;9using System.Text;10 11using Microsoft.PowerShell;12 13#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings14 15namespace System.Management.Automation16{17    /// <summary>18    /// Deals with DirectoryEntry objects.19    /// </summary>20    internal class DirectoryEntryAdapter : DotNetAdapter21    {22        #region private data23        // DirectoryEntry(DE) adapter needs dotnet adapter as DE adapter24        // don't know the underlying native adsi object's method metadata.25        // In the MethodInvoke() call, this adapter first calls26        // native adsi object's method, if there is a failure it calls27        // dotnet method (if one available).28        // This ensures dotnet methods are available on the adapted object.29        private static readonly DotNetAdapter s_dotNetAdapter = new DotNetAdapter();30        #endregion31 32        #region member33 34        internal override bool CanSiteBinderOptimize(MemberTypes typeToOperateOn)35        {36            return false;37        }38 39        /// <summary>40        /// Returns null if memberName is not a member in the adapter or41        /// the corresponding PSMemberInfo.42        /// </summary>43        /// <param name="obj">Object to retrieve the PSMemberInfo from.</param>44        /// <param name="memberName">Name of the member to be retrieved.</param>45        /// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>46        protected override T GetMember<T>(object obj, string memberName)47        {48            PSProperty property;49            DirectoryEntry entry = (DirectoryEntry)obj;50 51            // This line must precede InvokeGet. See the comment below.52            PropertyValueCollection collection = entry.Properties[memberName];53            object valueToTake = collection;54 55#pragma warning disable 5650056            // Even for the cases where propertyName does not exist57            // entry.Properties[propertyName] still returns a PropertyValueCollection.58            // The non schema way to check for a non existing property is to call entry.InvokeGet59            // and catch an eventual exception.60            // Specifically for "LDAP://RootDse" there are some cases where calling61            // InvokeGet will throw COMException for existing properties like defaultNamingContext.62            // Having a call to entry.Properties[propertyName] fixes the RootDse problem.63            // Calling entry.RefreshCache() also fixes the RootDse problem.64            try65            {66                object invokeGetValue = entry.InvokeGet(memberName);67                // if entry.Properties[memberName] returns empty value and invokeGet non-empty68                // value..take invokeGet's value. This will fix bug Windows Bug 121188.69                if ((collection == null) || ((collection.Value == null) && (invokeGetValue != null)))70                {71                    valueToTake = invokeGetValue;72                }73 74                property = new PSProperty(collection.PropertyName, this, obj, valueToTake);75            }76            catch (Exception)77            {78                property = null;79            }80#pragma warning restore 5650081 82            if (valueToTake == null)83            {84                property = null;85            }86 87            if (typeof(T).IsAssignableFrom(typeof(PSProperty)) && property != null)88            {89                return property as T;90            }91 92            if (typeof(T).IsAssignableFrom(typeof(PSMethod)))93            {94                if (property == null)95                {96                    #region Readme97                    // we are unable to find a native adsi object property.98                    // The next option is to find method. Unfortunately DirectoryEntry99                    // doesn't provide us a way to access underlying native object's method100                    // metadata.101                    // Adapter engine resolve's members in the following steps:102                    //  1. Extended members -> 2. Adapted members -> 3. Dotnet members103                    // We cannot say from DirectoryEntryAdapter if a method with name "memberName"104                    // is available. So check if a DotNet property with the same name is available105                    // If yes, return null from the adapted view and let adapter engine106                    // take care of DotNet member resolution. If not, assume memberName method107                    // is available on native adsi object.108                    // In case of collisions between Dotnet Property and adsi native object methods,109                    // Dotnet wins. Looking through IADs com interfaces there doesn't appear110                    // to be a collision like this.111                    // Powershell Parser will call only GetMember<PSMemberInfo>, so here112                    // we cannot distinguish if the caller is looking for a property or a113                    // method.114                    #endregion115 116                    if (base.GetDotNetProperty<T>(obj, memberName) == null)117                    {118                        return new PSMethod(memberName, this, obj, null) as T;119                    }120                }121            }122 123            return null;124        }125 126        /// <summary>127        /// Retrieves all the members available in the object.128        /// The adapter implementation is encouraged to cache all properties/methods available129        /// in the first call to GetMember and GetMembers so that subsequent130        /// calls can use the cache.131        /// In the case of the .NET adapter that would be a cache from the .NET type to132        /// the public properties and fields available in that type.133        /// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass134        /// to the properties available in it.135        /// </summary>136        /// <param name="obj">Object to get all the member information from.</param>137        /// <returns>All members in obj.</returns>138        protected override PSMemberInfoInternalCollection<T> GetMembers<T>(object obj)139        {140            DirectoryEntry entry = (DirectoryEntry)obj;141 142            PSMemberInfoInternalCollection<T> members = new PSMemberInfoInternalCollection<T>();143 144            if (entry.Properties == null || entry.Properties.PropertyNames == null)145            {146                return null;147            }148 149            int countOfProperties = 0;150 151#pragma warning disable 56500152            try153            {154                countOfProperties = entry.Properties.PropertyNames.Count;155            }156            catch (Exception) // swallow all non-severe exceptions157            {158            }159#pragma warning restore 56500160 161            if (countOfProperties > 0)162            {163                foreach (PropertyValueCollection property in entry.Properties)164                {165                    members.Add(new PSProperty(property.PropertyName, this, obj, property) as T);166                }167            }168 169            return members;170        }171 172        #endregion member173 174        #region property175 176        /// <summary>177        /// Returns the value from a property coming from a previous call to GetMember.178        /// </summary>179        /// <param name="property">PSProperty coming from a previous call to GetMember.</param>180        /// <returns>The value of the property.</returns>181        protected override object PropertyGet(PSProperty property)182        {183            return property.adapterData;184        }185 186        /// <summary>187        /// Sets the value of a property coming from a previous call to GetMember.188        /// </summary>189        /// <param name="property">PSProperty coming from a previous call to GetMember.</param>190        /// <param name="setValue">Value to set the property with.</param>191        /// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>192        protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)193        {194            PropertyValueCollection values = property.adapterData as PropertyValueCollection;195 196            if (values != null)197            {198                // This means GetMember returned PropertyValueCollection199                try200                {201                    values.Clear();202                }203                catch (System.Runtime.InteropServices.COMException e)204                {205                    if (e.ErrorCode != unchecked((int)0x80004005) || (setValue == null))206                        // When clear is called, DirectoryEntry calls PutEx on AD object with Clear option and Null Value207                        // WinNT provider throws E_FAIL when null value is specified though actually ADS_PROPERTY_CLEAR option is used,208                        // we need to catch this exception here.209                        // But at the same time we don't want to catch the exception if user explicitly sets the value to null.210                        throw;211                }212 213                IEnumerable enumValues = LanguagePrimitives.GetEnumerable(setValue);214 215                if (enumValues == null)216                {217                    values.Add(setValue);218                }219                else220                {221                    foreach (object objValue in enumValues)222                    {223                        values.Add(objValue);224                    }225                }226            }227            else228            {229                // This means GetMember returned the value from InvokeGet..So set the value using InvokeSet.230                DirectoryEntry entry = (DirectoryEntry)property.baseObject;231                Diagnostics.Assert(entry != null, "Object should be of type DirectoryEntry in DirectoryEntry adapter.");232 233                List<object> setValues = new List<object>();234                IEnumerable enumValues = LanguagePrimitives.GetEnumerable(setValue);235 236                if (enumValues == null)237                {238                    setValues.Add(setValue);239                }240                else241                {242                    foreach (object objValue in enumValues)243                    {244                        setValues.Add(objValue);245                    }246                }247 248                entry.InvokeSet(property.name, setValues.ToArray());249            }250 251            return;252        }253 254        /// <summary>255        /// Returns true if the property is settable.256        /// </summary>257        /// <param name="property">Property to check.</param>258        /// <returns>True if the property is settable.</returns>259        protected override bool PropertyIsSettable(PSProperty property)260        {261            return true;262        }263 264        /// <summary>265        /// Returns true if the property is gettable.266        /// </summary>267        /// <param name="property">Property to check.</param>268        /// <returns>True if the property is gettable.</returns>269        protected override bool PropertyIsGettable(PSProperty property)270        {271            return true;272        }273 274        /// <summary>275        /// Returns the name of the type corresponding to the property's value.276        /// </summary>277        /// <param name="property">PSProperty obtained in a previous GetMember.</param>278        /// <param name="forDisplay">True if the result is for display purposes only.</param>279        /// <returns>The name of the type corresponding to the member.</returns>280        protected override string PropertyType(PSProperty property, bool forDisplay)281        {282            object value = null;283            try284            {285                value = BasePropertyGet(property);286            }287            catch (GetValueException)288            {289            }290 291            var type = value == null ? typeof(object) : value.GetType();292            return forDisplay ? ToStringCodeMethods.Type(type) : type.FullName;293        }294 295        #endregion property296 297        #region method298 299        protected override object MethodInvoke(PSMethod method, PSMethodInvocationConstraints invocationConstraints, object[] arguments)300        {301            return this.MethodInvoke(method, arguments);302        }303 304        /// <summary>305        /// Called after a non null return from GetMember to try to call306        /// the method with the arguments.307        /// </summary>308        /// <param name="method">The non empty return from GetMethods.</param>309        /// <param name="arguments">The arguments to use.</param>310        /// <returns>The return value for the method.</returns>311        protected override object MethodInvoke(PSMethod method, object[] arguments)312        {313            ParameterInformation[] parameters = new ParameterInformation[arguments.Length];314 315            for (int i = 0; i < arguments.Length; i++)316            {317                parameters[i] = new ParameterInformation(typeof(object), false, null, false);318            }319 320            MethodInformation[] methodInformation = new MethodInformation[1];321            methodInformation[0] = new MethodInformation(false, false, parameters);322 323            object[] newArguments;324            GetBestMethodAndArguments(method.Name, methodInformation, arguments, out newArguments);325 326            DirectoryEntry entry = (DirectoryEntry)method.baseObject;327 328            // First try to invoke method on the native adsi object. If the method329            // call fails, try to invoke dotnet method with same name, if one available.330            // This will ensure dotnet methods are exposed for DE objects.331            // The problem is in GetMember<T>(), DE adapter cannot check if a requested332            // method is available as it doesn't have access to native adsi object's333            // method metadata. So GetMember<T> returns PSMethod assuming a method334            // is available. This behavior will never give a chance to dotnet adapter335            // to resolve method call. So the DE adapter owns calling dotnet method336            // if one available.337            Exception exception;338            try339            {340                return entry.Invoke(method.Name, newArguments);341            }342            catch (DirectoryServicesCOMException dse)343            {344                exception = dse;345            }346            catch (TargetInvocationException tie)347            {348                exception = tie;349            }350            catch (COMException ce)351            {352                exception = ce;353            }354 355            // this code is reached only on exception356            // check if there is a dotnet method, invoke the dotnet method if available357            PSMethod dotNetmethod = s_dotNetAdapter.GetDotNetMethod<PSMethod>(method.baseObject, method.name);358            if (dotNetmethod != null)359            {360                return dotNetmethod.Invoke(arguments);361            }362            // else363            throw exception;364        }365 366        /// <summary>367        /// Returns the string representation of the method in the object.368        /// </summary>369        /// <returns>The string representation of the method in the object.</returns>370        protected override string MethodToString(PSMethod method)371        {372            StringBuilder returnValue = new StringBuilder();373            foreach (string overload in MethodDefinitions(method))374            {375                returnValue.Append(overload);376                returnValue.Append(", ");377            }378 379            returnValue.Remove(returnValue.Length - 2, 2);380            return returnValue.ToString();381        }382 383        #endregion method384    }385}386