// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// The Debugger class is a part of the System.Diagnostics package
// and is used for communicating with a debugger.

namespace System.Diagnostics
{
    using System;
    using System.IO;
    using System.Collections;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Security;
    using System.Security.Permissions;
    using System.Runtime.Versioning;

    // No data, does not need to be marked with the serializable attribute
    [System.Runtime.InteropServices.ComVisible(true)]
    public sealed class Debugger
    {
        // This should have been a static class, but wasn't as of v3.5.  Clearly, this is
        // broken.  We'll keep this in V4 for binary compat, but marked obsolete as error
        // so migrated source code gets fixed.
        [Obsolete("Do not create instances of the Debugger class.  Call the static methods directly on this type instead", true)]
        public Debugger()
        {
            // Should not have been instantiable - here for binary compatibility in V4.
        }

        // Break causes a breakpoint to be signalled to an attached debugger.  If no debugger
        // is attached, the user is asked if he wants to attach a debugger. If yes, then the 
        // debugger is launched.
        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        public static void Break()
        {
            if (!IsDebuggerAttached())
            {
                // Try and demand UnmanagedCodePermission.  This is done in a try block because if this
                // fails we want to be able to silently eat the exception and just return so
                // that the call to Break does not possibly cause an unhandled exception.
                // The idea here is that partially trusted code shouldn't be able to launch a debugger 
                // without the user going through Watson.
                try
                {
#pragma warning disable 618
                    new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
#pragma warning restore 618
                }

                // If we enter this block, we do not have permission to break into the debugger
                // and so we just return.
                catch (SecurityException)
                {
                    return;
                }
            }

            // Causing a break is now allowed.
            BreakInternal();
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        static void BreakCanThrow()
        {
            if (!IsDebuggerAttached())
            {
#pragma warning disable 618
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
#pragma warning restore 618
            }

            // Causing a break is now allowed.
            BreakInternal();
        }

        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Process)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void BreakInternal();

        // Launch launches & attaches a debugger to the process. If a debugger is already attached,
        // nothing happens.  
        //
        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public static bool Launch()
        {
            if (IsDebuggerAttached())
                return (true);

            // Try and demand UnmanagedCodePermission.  This is done in a try block because if this
            // fails we want to be able to silently eat the exception and just return so
            // that the call to Break does not possibly cause an unhandled exception.
            // The idea here is that partially trusted code shouldn't be able to launch a debugger 
            // without the user going through Watson.
            try
            {
#pragma warning disable 618
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
#pragma warning restore 618
            }

            // If we enter this block, we do not have permission to break into the debugger
            // and so we just return.
            catch (SecurityException)
            {
                return (false);
            }

            // Causing the debugger to launch is now allowed.
            return (LaunchInternal());
        }

        // This class implements code:ICustomDebuggerNotification and provides a type to be used to notify
        // the debugger that execution is about to enter a path that involves a cross-thread dependency. 
        // See code:NotifyOfCrossThreadDependency for more details. 
        private class CrossThreadDependencyNotification : ICustomDebuggerNotification
        {
            // constructor
            public CrossThreadDependencyNotification()
            {
            }
        }

        // Sends a notification to the debugger to indicate that execution is about to enter a path 
        // involving a cross thread dependency. A debugger that has opted into this type of notification 
        // can take appropriate action on receipt. For example, performing a funceval normally requires 
        // freezing all threads but the one performing the funceval. If the funceval requires execution on 
        // more than one thread, as might occur in remoting scenarios, the funceval will block. This 
        // notification will apprise the debugger that it will need  to slip a thread or abort the funceval 
        // in such a situation. The notification is subject to collection after this function returns. 
        // 
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        [method:System.Runtime.InteropServices.ComVisible(false)]
        public static void NotifyOfCrossThreadDependency()
        {
            if (Debugger.IsAttached)
            {
                CrossThreadDependencyNotification notification = new CrossThreadDependencyNotification();
                CustomNotification(notification);
            }
        }


        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern bool LaunchInternal();

        // Returns whether or not a debugger is attached to the process.
        //
        public static bool IsAttached
        {
            [ResourceExposure(ResourceScope.Process)]
            [ResourceConsumption(ResourceScope.Process)]
            get { return IsDebuggerAttached(); }
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Process)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern bool IsDebuggerAttached();

        // Constants representing the importance level of messages to be logged.
        //
        // An attached debugger can enable or disable which messages will
        // actually be reported to the user through the COM+ debugger
        // services API.  This info is communicated to the runtime so only
        // desired events are actually reported to the debugger.  
        //
        // Constant representing the default category
        public static readonly String DefaultCategory = null;

        // Posts a message for the attached debugger.  If there is no
        // debugger attached, has no effect.  The debugger may or may not
        // report the message depending on its settings. 
        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        public static extern void Log(int level, String category, String message);

        // Checks to see if an attached debugger has logging enabled
        //  
        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        public static extern bool IsLogging();

        // Posts a custom notification for the attached debugger.  If there is no
        // debugger attached, has no effect.  The debugger may or may not
        // report the notification depending on its settings. 
        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void CustomNotification(ICustomDebuggerNotification data);

    }

}
