namespace System.Web.Services.Protocols { using System; using System.Web.Services; using System.Diagnostics; using System.Text; using System.Runtime.InteropServices; using System.Web.Services.Interop; using System.Reflection; using System.Threading; using System.Security.Permissions; using System.Net; using System.ComponentModel; // for CompModSwitches using System.Web.Services.Diagnostics; internal class RemoteDebugger : INotifySource2 { private static INotifyConnection2 connection; private static bool getConnection = true; private INotifySink2 notifySink; private NotifyFilter notifyFilter; private UserThread userThread; private const int INPROC_SERVER = 1; private static Guid IID_NotifyConnectionClassGuid = new Guid("12A5B9F0-7A1C-4fcb-8163-160A30F519B5"); private static Guid IID_NotifyConnection2Guid = new Guid("1AF04045-6659-4aaa-9F4B-2741AC56224B"); private static string debuggerHeader = "VsDebuggerCausalityData"; private static Object s_InternalSyncObject; private static Object InternalSyncObject { get { if (s_InternalSyncObject == null) { Object o = new Object(); Interlocked.CompareExchange(ref s_InternalSyncObject, o, null); } return s_InternalSyncObject; } } [DebuggerStepThrough] [DebuggerHidden] internal RemoteDebugger() { } ~RemoteDebugger() { Close(); } internal static bool IsClientCallOutEnabled() { bool enabled = false; try { enabled = !CompModSwitches.DisableRemoteDebugging.Enabled && Debugger.IsAttached && Connection != null; } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, typeof(RemoteDebugger), "IsClientCallOutEnabled", e); } return enabled; } internal static bool IsServerCallInEnabled(ServerProtocol protocol, out string stringBuffer) { stringBuffer = null; bool enabled = false; try { if (CompModSwitches.DisableRemoteDebugging.Enabled) return false; enabled = protocol.Context.IsDebuggingEnabled && Connection != null; if (enabled) { stringBuffer = protocol.Request.Headers[debuggerHeader]; enabled = (stringBuffer != null && stringBuffer.Length > 0); } } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, typeof(RemoteDebugger), "IsServerCallInEnabled", e); enabled = false; } return enabled; } private static INotifyConnection2 Connection { get { if (connection == null && getConnection) { lock (InternalSyncObject) { if (connection == null) { AppDomain.CurrentDomain.DomainUnload += new EventHandler(OnAppDomainUnload); AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit); TraceMethod method = Tracing.On ? new TraceMethod(typeof(RemoteDebugger), "get_Connection") : null; if (Tracing.On) Tracing.Enter("RemoteDebugger", method); object unk; int result = UnsafeNativeMethods.CoCreateInstance(ref IID_NotifyConnectionClassGuid, null, INPROC_SERVER, ref IID_NotifyConnection2Guid, out unk); if (Tracing.On) Tracing.Exit("RemoteDebugger", method); if (result >= 0) // success connection = (INotifyConnection2)unk; else connection = null; } getConnection = false; } } return connection; } } private INotifySink2 NotifySink { get { if (this.notifySink == null && Connection != null) { TraceMethod method = Tracing.On ? new TraceMethod(this, "get_NotifySink") : null; if (Tracing.On) Tracing.Enter("RemoteDebugger", method); this.notifySink = UnsafeNativeMethods.RegisterNotifySource(Connection, this); if (Tracing.On) Tracing.Exit("RemoteDebugger", method); } return this.notifySink; } } private static void CloseSharedResources() { if (connection != null) { lock (InternalSyncObject) { if (connection != null) { TraceMethod method = Tracing.On ? new TraceMethod(typeof(RemoteDebugger), "CloseSharedResources") : null; if (Tracing.On) Tracing.Enter("RemoteDebugger", method); try { Marshal.ReleaseComObject(connection); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, typeof(RemoteDebugger), "CloseSharedResources", e); } if (Tracing.On) Tracing.Exit("RemoteDebugger", method); connection = null; } } } } private void Close() { if (this.notifySink != null && connection != null) { lock (InternalSyncObject) { if (this.notifySink != null && connection != null) { TraceMethod method = Tracing.On ? new TraceMethod(this, "Close") : null; if (Tracing.On) Tracing.Enter("RemoteDebugger", method); try { UnsafeNativeMethods.UnregisterNotifySource(connection, this); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, method, e); } if (Tracing.On) Tracing.Exit("RemoteDebugger", method); this.notifySink = null; } } } } [DebuggerStepThrough] [DebuggerHidden] internal void NotifyClientCallOut(WebRequest request) { try { if (NotifySink == null) return; IntPtr bufferPtr; int bufferSize = 0; CallId callId = new CallId(null, 0, (IntPtr)0, 0, null, request.RequestUri.Host); TraceMethod method = Tracing.On ? new TraceMethod(this, "NotifyClientCallOut") : null; if (Tracing.On) Tracing.Enter("RemoteDebugger", method); UnsafeNativeMethods.OnSyncCallOut(NotifySink, callId, out bufferPtr, ref bufferSize); if (Tracing.On) Tracing.Exit("RemoteDebugger", method); if (bufferPtr == IntPtr.Zero) return; byte[] buffer = null; try { buffer = new byte[bufferSize]; Marshal.Copy(bufferPtr, buffer, 0, bufferSize); } finally { Marshal.FreeCoTaskMem(bufferPtr); } string bufferString = Convert.ToBase64String(buffer); request.Headers.Add(debuggerHeader, bufferString); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, typeof(RemoteDebugger), "NotifyClientCallOut", e); } } [DebuggerStepThrough] [DebuggerHidden] internal void NotifyClientCallReturn(WebResponse response) { try { if (NotifySink == null) return; byte[] buffer = new byte[0]; if (response != null) { string bufferString = response.Headers[debuggerHeader]; if (bufferString != null && bufferString.Length != 0) buffer = Convert.FromBase64String(bufferString); } CallId callId = new CallId(null, 0, (IntPtr)0, 0, null, null); TraceMethod method = Tracing.On ? new TraceMethod(this, "NotifyClientCallReturn") : null; if (Tracing.On) Tracing.Enter("RemoteDebugger", method); UnsafeNativeMethods.OnSyncCallReturn(NotifySink, callId, buffer, buffer.Length); if (Tracing.On) Tracing.Exit("RemoteDebugger", method); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, typeof(RemoteDebugger), "NotifyClientCallReturn", e); } this.Close(); } [DebuggerStepThrough] [DebuggerHidden] internal void NotifyServerCallEnter(ServerProtocol protocol, string stringBuffer) { try { if (NotifySink == null) return; StringBuilder methodBuilder = new StringBuilder(); methodBuilder.Append(protocol.Type.FullName); methodBuilder.Append('.'); methodBuilder.Append(protocol.MethodInfo.Name); methodBuilder.Append('('); ParameterInfo[] parameterInfos = protocol.MethodInfo.Parameters; for (int i = 0; i < parameterInfos.Length; ++i) { if (i != 0) methodBuilder.Append(','); methodBuilder.Append(parameterInfos[i].ParameterType.FullName); } methodBuilder.Append(')'); byte[] buffer = Convert.FromBase64String(stringBuffer); CallId callId = new CallId(null, 0, (IntPtr)0, 0, methodBuilder.ToString(), null); TraceMethod method = Tracing.On ? new TraceMethod(this, "NotifyServerCallEnter") : null; if (Tracing.On) Tracing.Enter("RemoteDebugger", method); UnsafeNativeMethods.OnSyncCallEnter(NotifySink, callId, buffer, buffer.Length); if (Tracing.On) Tracing.Exit("RemoteDebugger", method); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, typeof(RemoteDebugger), "NotifyServerCallEnter", e); } } [DebuggerStepThrough] [DebuggerHidden] internal void NotifyServerCallExit(HttpResponse response) { try { if (NotifySink == null) return; IntPtr bufferPtr; int bufferSize = 0; CallId callId = new CallId(null, 0, (IntPtr)0, 0, null, null); TraceMethod method = Tracing.On ? new TraceMethod(this, "NotifyServerCallExit") : null; if (Tracing.On) Tracing.Enter("RemoteDebugger", method); UnsafeNativeMethods.OnSyncCallExit(NotifySink, callId, out bufferPtr, ref bufferSize); if (Tracing.On) Tracing.Exit("RemoteDebugger", method); if (bufferPtr == IntPtr.Zero) return; byte[] buffer = null; try { buffer = new byte[bufferSize]; Marshal.Copy(bufferPtr, buffer, 0, bufferSize); } finally { Marshal.FreeCoTaskMem(bufferPtr); } string stringBuffer = Convert.ToBase64String(buffer); response.AddHeader(debuggerHeader, stringBuffer); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, typeof(RemoteDebugger), "NotifyServerCallExit", e); } this.Close(); } private static void OnAppDomainUnload(object sender, EventArgs args) { CloseSharedResources(); } private static void OnProcessExit(object sender, EventArgs args) { CloseSharedResources(); } void INotifySource2.SetNotifyFilter(NotifyFilter in_NotifyFilter, UserThread in_pUserThreadFilter) { notifyFilter = in_NotifyFilter; userThread = in_pUserThreadFilter; } } }