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;5using System.Diagnostics;6using System.Linq;7using System.Reflection;8using System.Reflection.Emit;9using System.Management.Automation.InteropServices;10using System.Runtime.InteropServices;11using System.Security;12using ComTypes = System.Runtime.InteropServices.ComTypes;13 14namespace System.Management.Automation.ComInterop15{16 internal static class ComRuntimeHelpers17 {18 public static void CheckThrowException(int hresult, ref ExcepInfo excepInfo, ComMethodDesc method, object[] args, uint argErr)19 {20 if (ComHresults.IsSuccess(hresult))21 {22 return;23 }24 Exception parameterException = null;25 26 switch (hresult)27 {28 case ComHresults.DISP_E_BADPARAMCOUNT:29 // The number of elements provided to DISPPARAMS is different from the number of arguments30 // accepted by the method or property.31 parameterException = Error.DispBadParamCount(method.Name, args.Length - 1);32 ThrowWrappedInvocationException(method, parameterException);33 break;34 35 case ComHresults.DISP_E_BADVARTYPE:36 //One of the arguments in rgvarg is not a valid variant type.37 break;38 39 case ComHresults.DISP_E_EXCEPTION:40 // The application needs to raise an exception. In this case, the structure passed in pExcepInfo41 // should be filled in.42 throw excepInfo.GetException();43 44 case ComHresults.DISP_E_MEMBERNOTFOUND:45 // The requested member does not exist, or the call to Invoke tried to set the value of a46 // read-only property.47 throw Error.DispMemberNotFound(method.Name);48 49 case ComHresults.DISP_E_NONAMEDARGS:50 // This implementation of IDispatch does not support named arguments.51 throw Error.DispNoNamedArgs(method.Name);52 53 case ComHresults.DISP_E_OVERFLOW:54 // One of the arguments in rgvarg could not be coerced to the specified type.55 throw Error.DispOverflow(method.Name);56 57 case ComHresults.DISP_E_PARAMNOTFOUND:58 // One of the parameter DISPIDs does not correspond to a parameter on the method. In this case,59 // puArgErr should be set to the first argument that contains the error.60 break;61 62 case ComHresults.DISP_E_TYPEMISMATCH:63 // The index within rgvarg of the first parameter with the incorrect64 // type is returned in the puArgErr parameter.65 //66 // But: Arguments are stored in pDispParams->rgvarg in reverse order, so the first67 // parameter is the one with the highest index in the array68 // https://msdn.microsoft.com/library/aa912367.aspx69 argErr = ((uint)args.Length) - argErr - 2;70 71 // One or more of the arguments could not be coerced.72 73 Type destinationType = null;74 if (argErr >= method.ParameterInformation.Length)75 {76 destinationType = method.InputType;77 }78 else79 {80 destinationType = method.ParameterInformation[argErr].parameterType;81 }82 83 object originalValue = args[argErr + 1];84 85 // If this is a put, use the InputType and the last argument86 if (method.IsPropertyPut || method.IsPropertyPutRef)87 {88 destinationType = method.InputType;89 originalValue = args[args.Length - 1];90 }91 92 string originalValueString = originalValue.ToString();93 string originalTypeName = Microsoft.PowerShell.ToStringCodeMethods.Type(originalValue.GetType(), true);94 95 // ByRef arguments should be displayed in the error message as a PSReference96 if (destinationType == typeof(object) && method.ParameterInformation[argErr].isByRef)97 {98 destinationType = typeof(PSReference);99 }100 101 string destinationTypeName = Microsoft.PowerShell.ToStringCodeMethods.Type(destinationType, true);102 103 parameterException = Error.DispTypeMismatch(method.Name, originalValueString, originalTypeName, destinationTypeName);104 ThrowWrappedInvocationException(method, parameterException);105 break;106 107 case ComHresults.DISP_E_UNKNOWNINTERFACE:108 // The interface identifier passed in riid is not IID_NULL.109 break;110 111 case ComHresults.DISP_E_UNKNOWNLCID:112 // The member being invoked interprets string arguments according to the LCID, and the113 // LCID is not recognized.114 break;115 116 case ComHresults.DISP_E_PARAMNOTOPTIONAL:117 // A required parameter was omitted.118 throw Error.DispParamNotOptional(method.Name);119 }120 121 Marshal.ThrowExceptionForHR(hresult);122 }123 124 private static void ThrowWrappedInvocationException(ComMethodDesc method, Exception parameterException)125 {126 if ((method.InvokeKind & ComTypes.INVOKEKIND.INVOKE_FUNC) ==127 ComTypes.INVOKEKIND.INVOKE_FUNC)128 {129 throw new MethodException(parameterException.Message, parameterException);130 }131 132 if (method.IsPropertyGet)133 {134 throw new GetValueInvocationException(parameterException.Message, parameterException);135 }136 137 if (method.IsPropertyPut || method.IsPropertyPutRef)138 {139 throw new SetValueInvocationException(parameterException.Message, parameterException);140 }141 142 throw parameterException;143 }144 145 internal static void GetInfoFromType(ComTypes.ITypeInfo typeInfo, out string name, out string documentation)146 {147 typeInfo.GetDocumentation(-1, out name, out documentation, out int _, out string _);148 }149 150 internal static string GetNameOfMethod(ComTypes.ITypeInfo typeInfo, int memid)151 {152 string[] rgNames = new string[1];153 typeInfo.GetNames(memid, rgNames, 1, out int _);154 return rgNames[0];155 }156 157 internal static string GetNameOfLib(ComTypes.ITypeLib typeLib)158 {159 typeLib.GetDocumentation(-1, out string name, out string _, out int _, out string _);160 return name;161 }162 163 internal static string GetNameOfType(ComTypes.ITypeInfo typeInfo)164 {165 GetInfoFromType(typeInfo, out string name, out string _);166 167 return name;168 }169 170 /// <summary>171 /// Look for typeinfo using IDispatch.GetTypeInfo.172 /// </summary>173 /// <param name="dispatch">IDispatch object</param>174 /// <remarks>175 /// Some COM objects just dont expose typeinfo. In these cases, this method will return null.176 /// Some COM objects do intend to expose typeinfo, but may not be able to do so if the type-library is not properly177 /// registered. This will be considered as acceptable or as an error condition depending on throwIfMissingExpectedTypeInfo178 /// </remarks>179 /// <returns>Type info</returns>180 internal static ComTypes.ITypeInfo GetITypeInfoFromIDispatch(IDispatch dispatch)181 {182 int hresult = dispatch.TryGetTypeInfoCount(out uint typeCount);183 if (hresult == ComHresults.E_NOTIMPL || hresult == ComHresults.E_NOINTERFACE)184 {185 // Allow the dynamic binding to continue using the original binder.186 return null;187 }188 else189 {190 Marshal.ThrowExceptionForHR(hresult);191 }192 193 Debug.Assert(typeCount <= 1);194 if (typeCount == 0)195 {196 return null;197 }198 199 IntPtr typeInfoPtr;200 hresult = dispatch.TryGetTypeInfo(0, 0, out typeInfoPtr);201 if (!ComHresults.IsSuccess(hresult))202 {203 // Word.Basic always returns this because of an incorrect implementation of IDispatch.GetTypeInfo204 // Any implementation that returns E_NOINTERFACE is likely to do so in all environments205 if (hresult == ComHresults.E_NOINTERFACE)206 {207 return null;208 }209 210 // This assert is potentially over-restrictive since COM components can behave in quite unexpected ways.211 // However, asserting the common expected cases ensures that we find out about the unexpected scenarios, and212 // can investigate the scenarios to ensure that there is no bug in our own code.213 Debug.Assert(hresult == ComHresults.TYPE_E_LIBNOTREGISTERED);214 215 Marshal.ThrowExceptionForHR(hresult);216 }217 218 if (typeInfoPtr == IntPtr.Zero)219 {220 Marshal.ThrowExceptionForHR(ComHresults.E_FAIL);221 }222 223 ComTypes.ITypeInfo typeInfo = null;224 try225 {226 typeInfo = Marshal.GetObjectForIUnknown(typeInfoPtr) as ComTypes.ITypeInfo;227 }228 finally229 {230 Marshal.Release(typeInfoPtr);231 }232 233 return typeInfo;234 }235 236 internal static ComTypes.TYPEATTR GetTypeAttrForTypeInfo(ComTypes.ITypeInfo typeInfo)237 {238 IntPtr pAttrs;239 typeInfo.GetTypeAttr(out pAttrs);240 241 // GetTypeAttr should never return null, this is just to be safe242 if (pAttrs == IntPtr.Zero)243 {244 throw Error.CannotRetrieveTypeInformation();245 }246 247 try248 {249 return (ComTypes.TYPEATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPEATTR));250 }251 finally252 {253 typeInfo.ReleaseTypeAttr(pAttrs);254 }255 }256 257 internal static ComTypes.TYPELIBATTR GetTypeAttrForTypeLib(ComTypes.ITypeLib typeLib)258 {259 IntPtr pAttrs;260 typeLib.GetLibAttr(out pAttrs);261 262 // GetTypeAttr should never return null, this is just to be safe263 if (pAttrs == IntPtr.Zero)264 {265 throw Error.CannotRetrieveTypeInformation();266 }267 268 try269 {270 return (ComTypes.TYPELIBATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPELIBATTR));271 }272 finally273 {274 typeLib.ReleaseTLibAttr(pAttrs);275 }276 }277 278 public static BoundDispEvent CreateComEvent(object rcw, Guid sourceIid, int dispid)279 {280 return new BoundDispEvent(rcw, sourceIid, dispid);281 }282 283 public static DispCallable CreateDispCallable(IDispatchComObject dispatch, ComMethodDesc method)284 {285 return new DispCallable(dispatch, method.Name, method.DispId);286 }287 }288 289 /// <summary>290 /// This class contains methods that either cannot be expressed in C#, or which require writing unsafe code.291 /// Callers of these methods need to use them extremely carefully as incorrect use could cause GC-holes292 /// and other problems.293 /// </summary>294 ///295 internal static class UnsafeMethods296 {297 #region public members298 299 public static unsafe IntPtr ConvertInt32ByrefToPtr(ref int value)300 {301 return (IntPtr)System.Runtime.CompilerServices.Unsafe.AsPointer(ref value);302 }303 304 public static unsafe IntPtr ConvertVariantByrefToPtr(ref Variant value)305 {306 return (IntPtr)System.Runtime.CompilerServices.Unsafe.AsPointer(ref value);307 }308 309 internal static Variant GetVariantForObject(object obj)310 {311 Variant variant = default;312 if (obj == null)313 {314 return variant;315 }316 InitVariantForObject(obj, ref variant);317 return variant;318 }319 320 internal static void InitVariantForObject(object obj, ref Variant variant)321 {322 Debug.Assert(obj != null);323 324 // GetNativeVariantForObject is very expensive for values that marshal as VT_DISPATCH325 // also is extremely common scenario when object at hand is an RCW.326 // Therefore we are going to test for IDispatch before defaulting to GetNativeVariantForObject.327 if (obj is IDispatch)328 {329 variant.AsDispatch = obj;330 return;331 }332 333 Marshal.GetNativeVariantForObject(obj, ConvertVariantByrefToPtr(ref variant));334 }335 336 // This method is intended for use through reflection and should not be used directly337 public static object GetObjectForVariant(Variant variant)338 {339 IntPtr ptr = UnsafeMethods.ConvertVariantByrefToPtr(ref variant);340 return Marshal.GetObjectForNativeVariant(ptr);341 }342 343 // This method is intended for use through reflection and should only be used directly by IUnknownReleaseNotZero344 public static unsafe int IUnknownRelease(IntPtr interfacePointer)345 {346 return ((delegate* unmanaged<IntPtr, int>)(*(*(void***)interfacePointer + 2 /* IUnknown.Release slot */)))(interfacePointer);347 }348 349 // This method is intended for use through reflection and should not be used directly350 public static void IUnknownReleaseNotZero(IntPtr interfacePointer)351 {352 if (interfacePointer != IntPtr.Zero)353 {354 IUnknownRelease(interfacePointer);355 }356 }357 358 // This method is intended for use through reflection and should not be used directly359 public static unsafe int IDispatchInvoke(360 IntPtr dispatchPointer,361 int memberDispId,362 ComTypes.INVOKEKIND flags,363 ref ComTypes.DISPPARAMS dispParams,364 out Variant result,365 out ExcepInfo excepInfo,366 out uint argErr)367 {368 Guid IID_NULL = default;369 370 fixed (ComTypes.DISPPARAMS* pDispParams = &dispParams)371 fixed (Variant* pResult = &result)372 fixed (ExcepInfo* pExcepInfo = &excepInfo)373 fixed (uint* pArgErr = &argErr)374 {375 var pfnIDispatchInvoke = (delegate* unmanaged<IntPtr, int, Guid*, int, ushort, ComTypes.DISPPARAMS*, Variant*, ExcepInfo*, uint*, int>)(*(*(void***)dispatchPointer + 6 /* IDispatch.Invoke slot */));376 377 int hresult = pfnIDispatchInvoke(dispatchPointer,378 memberDispId, &IID_NULL, 0, (ushort)flags, pDispParams, pResult, pExcepInfo, pArgErr);379 380 if (hresult == ComHresults.DISP_E_MEMBERNOTFOUND381 && (flags & ComTypes.INVOKEKIND.INVOKE_FUNC) != 0382 && (flags & (ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT | ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF)) == 0)383 {384 // Re-invoke with no result argument to accommodate Word385 hresult = pfnIDispatchInvoke(dispatchPointer,386 memberDispId, &IID_NULL, 0, (ushort)ComTypes.INVOKEKIND.INVOKE_FUNC, pDispParams, null, pExcepInfo, pArgErr);387 }388 389 return hresult;390 }391 }392 393 // This method is intended for use through reflection and should not be used directly394 public static IntPtr GetIdsOfNamedParameters(IDispatch dispatch, string[] names, int methodDispId, out GCHandle pinningHandle)395 {396 pinningHandle = GCHandle.Alloc(null, GCHandleType.Pinned);397 int[] dispIds = new int[names.Length];398 Guid empty = Guid.Empty;399 int hresult = dispatch.TryGetIDsOfNames(ref empty, names, (uint)names.Length, 0, dispIds);400 if (hresult < 0)401 {402 Marshal.ThrowExceptionForHR(hresult);403 }404 405 if (methodDispId != dispIds[0])406 {407 throw Error.GetIDsOfNamesInvalid(names[0]);408 }409 410 int[] keywordArgDispIds = dispIds.RemoveFirst(); // Remove the dispId of the method name411 412 pinningHandle.Target = keywordArgDispIds;413 return Marshal.UnsafeAddrOfPinnedArrayElement(keywordArgDispIds, 0);414 }415 416 #endregion417 418 #region non-public members419 420 private static readonly object s_lock = new object();421 private static ModuleBuilder s_dynamicModule;422 423 internal static ModuleBuilder DynamicModule424 {425 get426 {427 if (s_dynamicModule != null)428 {429 return s_dynamicModule;430 }431 lock (s_lock)432 {433 if (s_dynamicModule == null)434 {435 string name = typeof(VariantArray).Namespace + ".DynamicAssembly";436 var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(name), AssemblyBuilderAccess.Run);437 s_dynamicModule = assembly.DefineDynamicModule(name);438 }439 return s_dynamicModule;440 }441 }442 }443 444 #endregion445 }446}447 