Set ErrorInfo in Marshal.GetHRForException
authorEberhard Beilharz <eb1@sil.org>
Thu, 13 Mar 2014 17:55:39 +0000 (18:55 +0100)
committerEberhard Beilharz <eb1@sil.org>
Thu, 13 Mar 2014 17:55:39 +0000 (18:55 +0100)
Marshal.GetHRForException had a MonoTODO because it needs to set
the errorinfo object so that exceptions get properly passed to
unmanaged code. Since not all managed exceptions have a corresponding
HR value but we might need to get to the original thrown exception
we implement a ManagedErrorInfo helper class that implements the
IErrorInfo interface but also stores the exception.

In GetExceptionForHR we check if the errorinfo we get is a
ManagedErrorInfo. If it is we return the exception stored in there
(if it converts to the same error code), otherwise we construct a new
exception.

mcs/class/corlib/System.Runtime.InteropServices/ManagedErrorInfo.cs [new file with mode: 0644]
mcs/class/corlib/System.Runtime.InteropServices/Marshal.cs
mcs/class/corlib/corlib.dll.sources

diff --git a/mcs/class/corlib/System.Runtime.InteropServices/ManagedErrorInfo.cs b/mcs/class/corlib/System.Runtime.InteropServices/ManagedErrorInfo.cs
new file mode 100644 (file)
index 0000000..c77857c
--- /dev/null
@@ -0,0 +1,63 @@
+// ManagedErrorInfo class
+//
+// Eberhard Beilharz (eb1@sil.org)
+//
+// Copyright (C) 2012 SIL International
+using System;
+using System.Runtime.CompilerServices;
+using System.Security;
+
+namespace System.Runtime.InteropServices
+{
+       /// <summary>
+       /// Helper class that allows to pass an exception as an IErrorInfo object. This is useful
+       /// when we get an exception in managed code that is called from unmanaged code that is called
+       /// from managed code and we want to get to the exception in the outer managed code.
+       /// </summary>
+       internal class ManagedErrorInfo: IErrorInfo
+       {
+               private Exception m_Exception;
+               public ManagedErrorInfo (Exception e)
+               {
+                       m_Exception = e;
+               }
+
+               public Exception Exception {
+                       get { return m_Exception; }
+               }
+
+               #region IErrorInfo
+               public int GetGUID (out Guid guid)
+               {
+                       // not supported
+                       guid = Guid.Empty;
+                       return 0;
+               }
+
+               public int GetSource (out string source)
+               {
+                       source = m_Exception.Source;
+                       return 0;
+               }
+
+               public int GetDescription (out string description)
+               {
+                       description = m_Exception.Message;
+                       return 0;
+               }
+
+               public int GetHelpFile (out string helpFile)
+               {
+                       helpFile = m_Exception.HelpLink;
+                       return 0;
+               }
+
+               public int GetHelpContext(out uint helpContext)
+               {
+                       // not supported
+                       helpContext = 0;
+                       return 0;
+               }
+               #endregion
+       }
+}
index bddd71f12b5d3de3e79a71e91463c91783f73a88..95df86a7c8f0bf6a13854cc50d73a28be57654b9 100644 (file)
@@ -414,9 +414,11 @@ namespace System.Runtime.InteropServices
 #endif // !FULL_AOT_RUNTIME
 
 #if !FULL_AOT_RUNTIME
-               [MonoTODO ("SetErrorInfo")]
                public static int GetHRForException (Exception e)
                {
+                       var errorInfo = new ManagedErrorInfo(e);
+                       SetErrorInfo (0, errorInfo);
+
                        return e.hresult;
                }
 
@@ -1520,12 +1522,36 @@ namespace System.Runtime.InteropServices
                        return null;
                }
 
+               [DllImport ("oleaut32.dll", CharSet=CharSet.Unicode, EntryPoint = "SetErrorInfo")]
+               static extern int _SetErrorInfo (int dwReserved,
+                       [MarshalAs(UnmanagedType.Interface)] IErrorInfo pIErrorInfo);
+
                [DllImport ("oleaut32.dll", CharSet=CharSet.Unicode, EntryPoint = "GetErrorInfo")]
                static extern int _GetErrorInfo (int dwReserved,
                        [MarshalAs(UnmanagedType.Interface)] out IErrorInfo ppIErrorInfo);
 
+               static bool SetErrorInfoNotAvailable;
                static bool GetErrorInfoNotAvailable;
 
+               internal static int SetErrorInfo (int dwReserved, IErrorInfo errorInfo)
+               {
+                       int retVal = 0;
+                       errorInfo = null;
+
+                       if (SetErrorInfoNotAvailable)
+                               return -1;
+
+                       try {
+                               retVal = _SetErrorInfo (dwReserved, errorInfo);
+                       }
+                       catch (Exception) {
+                               // ignore any exception - probably there's no suitable SetErrorInfo
+                               // method available.
+                               SetErrorInfoNotAvailable = true;
+                       }
+                       return retVal;
+               }
+
                internal static int GetErrorInfo (int dwReserved, out IErrorInfo errorInfo)
                {
                        int retVal = 0;
@@ -1552,10 +1578,8 @@ namespace System.Runtime.InteropServices
 
                public static Exception GetExceptionForHR (int errorCode, IntPtr errorInfoPtr)
                {
-                       Exception e = ConvertHrToException (errorCode);
-
-                       if (errorInfoPtr != (IntPtr)(-1) && e != null) {
-                               IErrorInfo errorInfo = null;
+                       IErrorInfo errorInfo = null;
+                       if (errorInfoPtr != (IntPtr)(-1)) {
                                if (errorInfoPtr == IntPtr.Zero) {
                                        if (GetErrorInfo (0, out errorInfo) != 0) {
                                                errorInfo = null;
@@ -1563,22 +1587,27 @@ namespace System.Runtime.InteropServices
                                } else {
                                        errorInfo = Marshal.GetObjectForIUnknown (errorInfoPtr) as IErrorInfo;
                                }
+                       }
 
-                               if (errorInfo != null) {
-                                       uint helpContext;
-                                       errorInfo.GetHelpContext (out helpContext);
-                                       string str;
-                                       errorInfo.GetSource (out str);
-                                       e.Source = str;
-                                       errorInfo.GetDescription (out str);
-                                       e.SetMessage (str);
-                                       errorInfo.GetHelpFile (out str);
-
-                                       if (helpContext == 0) {
-                                               e.HelpLink = str;
-                                       } else {
-                                               e.HelpLink = string.Format ("{0}#{1}", str, helpContext);
-                                       }
+                       if (errorInfo is ManagedErrorInfo && ((ManagedErrorInfo)errorInfo).Exception.hresult == errorCode) {
+                               return ((ManagedErrorInfo)errorInfo).Exception;
+                       }
+
+                       Exception e = ConvertHrToException (errorCode);
+                       if (errorInfo != null && e != null) {
+                               uint helpContext;
+                               errorInfo.GetHelpContext (out helpContext);
+                               string str;
+                               errorInfo.GetSource (out str);
+                               e.Source = str;
+                               errorInfo.GetDescription (out str);
+                               e.SetMessage (str);
+                               errorInfo.GetHelpFile (out str);
+
+                               if (helpContext == 0) {
+                                       e.HelpLink = str;
+                               } else {
+                                       e.HelpLink = string.Format ("{0}#{1}", str, helpContext);
                                }
                        }
                        return e;
index 1a874b863d1295246b9a8047f8e90c932655e0f4..0bf28c2287d5b300f0730d76cb03920086fdf0c1 100644 (file)
@@ -818,6 +818,7 @@ System.Runtime.InteropServices/InvalidOleVariantTypeException.cs
 System.Runtime.InteropServices/LCIDConversionAttribute.cs
 System.Runtime.InteropServices/LIBFLAGS.cs
 System.Runtime.InteropServices/LayoutKind.cs
+System.Runtime.InteropServices/ManagedErrorInfo.cs
 System.Runtime.InteropServices/Marshal.cs
 System.Runtime.InteropServices/MarshalAsAttribute.cs
 System.Runtime.InteropServices/MarshalDirectiveException.cs