internal static string GetDetailedExceptionMessage(this Exception ex, bool forUser)
        {
            // Must handle EVException separately otherwise its ToString would call us again and cause infinite recursion
            if (ex is EVException)
            {
                return(String.Empty);
            }

            if (forUser)
            {
                // Suppress default message CLR produces if no message is specified in exception
                string realMessage = MessageFieldInfo.GetValue(ex) as string;
                if (String.IsNullOrWhiteSpace(realMessage))
                {
                    return(String.Empty);
                }
            }

            // Debugging
            //FieldInfo[] exceptionFields = typeof(Exception).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);

            // Save InnerException so we can restore it back
            Exception innerException = ex.InnerException;

            // Reset InnerException so that ToString() would not go recursive
            InnerExceptionFieldInfo.SetValue(ex, null);

            // Save StackTraceString so we can restore it back
            object strStackTraceString = StackTraceStringFieldInfo.GetValue(ex);

            // Reset strStackTraceString to empty string so that it would not show up in ToString() output
            StackTraceStringFieldInfo.SetValue(ex, String.Empty);

            // Getting single level (non-recursive) ToString() without StackTrace
            string detailedExceptionMessage = ex.ToString();

            if (forUser)
            {
                // End users are not interested to see the type of exception being thrown. Unfortunately most system exceptions tend to
                // prepend it in their implementation of ToString() overload.
                string typePrefix = ex.GetType().FullName + ": ";
                detailedExceptionMessage = Utils.RemovePrefix(detailedExceptionMessage, typePrefix);
            }

            detailedExceptionMessage = Utils.RemoveSuffix(detailedExceptionMessage, "\r\n");

            IOException ioEx = ex as IOException;

            if (ioEx != null && !ex.GetOrCreatePayload().ContainsProperty(FileNameLabel))
            {
                object objFileName = MaybeFullPathFieldInfo.GetValue(ioEx);
                string fileName    = objFileName == null ? "Unknown" : objFileName.ToString();
                ex.AddNamedProperty(FileNameLabel, fileName);
            }

            // Restoring InnerException and StackTrace back to their original values
            InnerExceptionFieldInfo.SetValue(ex, innerException);
            StackTraceStringFieldInfo.SetValue(ex, strStackTraceString);

            return(detailedExceptionMessage);
        }