private static Exception GetErrorToLog(Exception e) { if (e == null) { throw new ArgumentNullException("e"); } var comException = e as COMException; if (comException == null) { return(e); } try { COMSurvivableException.RethrowAsOriginalIfPossible(comException); } catch (Exception comSurvivableException) { return(comSurvivableException); } return(e); }
private static T Invoke <T>(object source, InvokeFlags invokeFlags, int dispId, params object[] args) { if (source == null) { throw new ArgumentNullException("source"); } if (!Enum.IsDefined(typeof(InvokeFlags), invokeFlags)) { throw new ArgumentOutOfRangeException("invokeFlags"); } if (args == null) { throw new ArgumentNullException("args"); } var memoryAllocationsToFree = new List <IntPtr>(); IntPtr rgdispidNamedArgs; int cNamedArgs; if ((invokeFlags == InvokeFlags.DISPATCH_PROPERTYPUT) || (invokeFlags == InvokeFlags.DISPATCH_PROPERTYPUTREF)) { // There must be at least one argument specified; only one if it is a non-indexed property and multiple if there are index values as well as the // value to set to if (args.Length < 1) { throw new ArgumentException("At least one argument must be specified when DISPATCH_PROPERTYPUT is requested"); } var pdPutID = Marshal.AllocCoTaskMem(sizeof(Int64)); Marshal.WriteInt64(pdPutID, DISPID_PROPERTYPUT); memoryAllocationsToFree.Add(pdPutID); rgdispidNamedArgs = pdPutID; cNamedArgs = 1; } else { rgdispidNamedArgs = IntPtr.Zero; cNamedArgs = 0; } var variantsToClear = new List <IntPtr>(); IntPtr rgvarg; if (args.Length == 0) { rgvarg = IntPtr.Zero; } else { // We need to allocate enough memory to store a variant for each argument (and then populate this memory) rgvarg = Marshal.AllocCoTaskMem(SizeOfNativeVariant * args.Length); memoryAllocationsToFree.Add(rgvarg); for (var index = 0; index < args.Length; index++) { // Note: The "IDispatch::Invoke method (Automation)" page (http://msdn.microsoft.com/en-us/library/windows/desktop/ms221479(v=vs.85).aspx) // states that "Arguments are stored in pDispParams->rgvarg in reverse order" so we'll reverse them here var arg = args[(args.Length - 1) - index]; // According to http://stackoverflow.com/a/1866268 it seems like using ToInt64 here will be valid for both 32 and 64 bit machines. While // this may apparently not be the most performant approach, it should do the job. // Don't think we have to worry about pinning any references when we do this manipulation here since we are allocating the array in // unmanaged memory and so the garbage collector won't be moving anything around (and GetNativeVariantForObject copies the reference // and automatic pinning will prevent the GC from interfering while this is happening). var pVariant = new IntPtr( rgvarg.ToInt64() + (SizeOfNativeVariant * index) ); Marshal.GetNativeVariantForObject(arg, pVariant); variantsToClear.Add(pVariant); } } var dispParams = new ComTypes.DISPPARAMS() { cArgs = args.Length, rgvarg = rgvarg, cNamedArgs = cNamedArgs, rgdispidNamedArgs = rgdispidNamedArgs }; try { var IID_NULL = new Guid("00000000-0000-0000-0000-000000000000"); UInt32 pArgErr = 0; object varResult; var excepInfo = new ComTypes.EXCEPINFO(); var hrRet = ((IDispatch)source).Invoke ( dispId, ref IID_NULL, LOCALE_SYSTEM_DEFAULT, (ushort)invokeFlags, ref dispParams, out varResult, ref excepInfo, out pArgErr ); if (hrRet != 0) { if (excepInfo.bstrDescription == null) { Console.WriteLine($"Exception thrown while accessing {TypeDescriptor.GetClassName(source)} has null bstrDescription"); } else { Console.WriteLine($"Exception thrown while accessing {TypeDescriptor.GetClassName(source)} has bstrDescription: \"{excepInfo.bstrDescription}\""); } // Try to translate the exception back into a COMSurvivableException - if this is not possible then fall through to the code below COMSurvivableException.RethrowAsOriginalIfPossible( new COMException(excepInfo.bstrDescription, excepInfo.scode) ); var message = "Failing attempting to invoke method with DispId " + dispId + ": "; var errorType = GetErrorMessageForHResult(hrRet); if (errorType == CommonErrors.DISP_E_MEMBERNOTFOUND) { message += "Member not found"; } else if (!string.IsNullOrWhiteSpace(excepInfo.bstrDescription)) { message += excepInfo.bstrDescription; } else { message += "Unspecified error"; if (errorType != CommonErrors.Unknown) { message += " [" + errorType.ToString() + "]"; } } throw new ArgumentException(message); } return((T)varResult); } finally { foreach (var variantToClear in variantsToClear) { VariantClear(variantToClear); } foreach (var memoryAllocationToFree in memoryAllocationsToFree) { Marshal.FreeCoTaskMem(memoryAllocationToFree); } } }