andrew4582

DebugMonitor

Aug 21st, 2010
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 14.17 KB | None | 0 0
  1. namespace DbMon.NET {
  2.  
  3.     using System;
  4.     using System.Threading;
  5.     using System.Runtime.InteropServices;
  6.  
  7.     /// <summary>
  8.     /// Delegate used when firing DebugMonitor.OnOutputDebug event
  9.     /// </summary>
  10.     public delegate void OnOutputDebugStringHandler(int pid,string text);
  11.  
  12.     /// <summary>
  13.     /// This class captures all strings passed to <c>OutputDebugString</c> when
  14.     /// the application is not debugged.   
  15.     /// </summary>
  16.     /// <remarks>  
  17.     /// This class is a port of Microsofts Visual Studio's C++ example "dbmon", which
  18.     /// can be found at <c>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample98/html/vcsmpdbmon.asp</c>.
  19.     /// </remarks>
  20.     /// <remarks>
  21.     ///     <code>
  22.     ///         public static void Main(string[] args) {
  23.     ///             DebugMonitor.Start();
  24.     ///             DebugMonitor.OnOutputDebugString += new OnOutputDebugStringHandler(OnOutputDebugString);
  25.     ///             Console.WriteLine("Press 'Enter' to exit.");
  26.     ///             Console.ReadLine();
  27.     ///             DebugMonitor.Stop();
  28.     ///         }
  29.     ///        
  30.     ///         private static void OnOutputDebugString(int pid, string text) {
  31.     ///             Console.WriteLine(DateTime.Now + ": " + text);
  32.     ///         }
  33.     ///     </code>
  34.     /// </remarks>
  35.     public sealed class DebugMonitor {
  36.  
  37.         /// <summary>
  38.         /// Private constructor so no one can create a instance
  39.         /// of this static class
  40.         /// </summary>
  41.         private DebugMonitor() {
  42.             ;
  43.         }
  44.  
  45.         #region Win32 API Imports
  46.  
  47.         [StructLayout(LayoutKind.Sequential)]
  48.         private struct SECURITY_DESCRIPTOR {
  49.             public byte revision;
  50.             public byte size;
  51.             public short control;
  52.             public IntPtr owner;
  53.             public IntPtr group;
  54.             public IntPtr sacl;
  55.             public IntPtr dacl;
  56.         }
  57.  
  58.         [StructLayout(LayoutKind.Sequential)]
  59.         private struct SECURITY_ATTRIBUTES {
  60.             public int nLength;
  61.             public IntPtr lpSecurityDescriptor;
  62.             public int bInheritHandle;
  63.         }
  64.  
  65.         [Flags]
  66.         private enum PageProtection:uint {
  67.             NoAccess = 0x01,
  68.             Readonly = 0x02,
  69.             ReadWrite = 0x04,
  70.             WriteCopy = 0x08,
  71.             Execute = 0x10,
  72.             ExecuteRead = 0x20,
  73.             ExecuteReadWrite = 0x40,
  74.             ExecuteWriteCopy = 0x80,
  75.             Guard = 0x100,
  76.             NoCache = 0x200,
  77.             WriteCombine = 0x400,
  78.         }
  79.  
  80.  
  81.         private const int WAIT_OBJECT_0 = 0;
  82.         private const uint INFINITE = 0xFFFFFFFF;
  83.         private const int ERROR_ALREADY_EXISTS = 183;
  84.  
  85.         private const uint SECURITY_DESCRIPTOR_REVISION = 1;
  86.  
  87.         private const uint SECTION_MAP_READ = 0x0004;
  88.  
  89.         [DllImport("kernel32.dll",SetLastError = true)]
  90.         private static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject,uint
  91.             dwDesiredAccess,uint dwFileOffsetHigh,uint dwFileOffsetLow,
  92.             uint dwNumberOfBytesToMap);
  93.  
  94.         [DllImport("kernel32.dll",SetLastError = true)]
  95.         private static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
  96.  
  97.         [DllImport("advapi32.dll",SetLastError = true)]
  98.         private static extern bool InitializeSecurityDescriptor(ref SECURITY_DESCRIPTOR sd,uint dwRevision);
  99.  
  100.         [DllImport("advapi32.dll",SetLastError = true)]
  101.         private static extern bool SetSecurityDescriptorDacl(ref SECURITY_DESCRIPTOR sd,bool daclPresent,IntPtr dacl,bool daclDefaulted);
  102.  
  103.         [DllImport("kernel32.dll")]
  104.         private static extern IntPtr CreateEvent(ref SECURITY_ATTRIBUTES sa,bool bManualReset,bool bInitialState,string lpName);
  105.  
  106.         [DllImport("kernel32.dll")]
  107.         private static extern bool PulseEvent(IntPtr hEvent);
  108.  
  109.         [DllImport("kernel32.dll")]
  110.         private static extern bool SetEvent(IntPtr hEvent);
  111.  
  112.         [DllImport("kernel32.dll",SetLastError = true)]
  113.         private static extern IntPtr CreateFileMapping(IntPtr hFile,
  114.             ref SECURITY_ATTRIBUTES lpFileMappingAttributes,PageProtection flProtect,uint dwMaximumSizeHigh,
  115.             uint dwMaximumSizeLow,string lpName);
  116.  
  117.         [DllImport("kernel32.dll",SetLastError = true)]
  118.         private static extern bool CloseHandle(IntPtr hHandle);
  119.  
  120.         [DllImport("kernel32",SetLastError = true,ExactSpelling = true)]
  121.         private static extern Int32 WaitForSingleObject(IntPtr handle,uint milliseconds);
  122.         #endregion
  123.  
  124.         /// <summary>
  125.         /// Fired if an application calls <c>OutputDebugString</c>
  126.         /// </summary>
  127.         public static event OnOutputDebugStringHandler OnOutputDebugString;
  128.  
  129.         /// <summary>
  130.         /// Event handle for slot 'DBWIN_BUFFER_READY'
  131.         /// </summary>
  132.         private static IntPtr m_AckEvent = IntPtr.Zero;
  133.  
  134.         /// <summary>
  135.         /// Event handle for slot 'DBWIN_DATA_READY'
  136.         /// </summary>
  137.         private static IntPtr m_ReadyEvent = IntPtr.Zero;
  138.  
  139.         /// <summary>
  140.         /// Handle for our shared file
  141.         /// </summary>
  142.         private static IntPtr m_SharedFile = IntPtr.Zero;
  143.  
  144.         /// <summary>
  145.         /// Handle for our shared memory
  146.         /// </summary>
  147.         private static IntPtr m_SharedMem = IntPtr.Zero;
  148.  
  149.         /// <summary>
  150.         /// Our capturing thread
  151.         /// </summary>
  152.         private static Thread m_Capturer = null;
  153.  
  154.         /// <summary>
  155.         /// Our synchronization root
  156.         /// </summary>
  157.         private static object m_SyncRoot = new object();
  158.  
  159.         /// <summary>
  160.         /// Mutex for singleton check
  161.         /// </summary>
  162.         private static Mutex m_Mutex = null;
  163.         public static bool IsStarted { get; private set; }
  164.  
  165.         /// <summary>
  166.         /// Starts this debug monitor
  167.         /// </summary>
  168.         public static void Start() {
  169.             if(IsStarted)
  170.                 return;
  171.             IsStarted = true;
  172.             lock(m_SyncRoot) {
  173.                 if(m_Capturer != null)
  174.                     throw new ApplicationException("This DebugMonitor is already started.");
  175.  
  176.                 // Check for supported operating system. Mono (at least with *nix) won't support
  177.                 // our P/Invoke calls.
  178.                 if(Environment.OSVersion.ToString().IndexOf("Microsoft") == -1)
  179.                     throw new NotSupportedException("This DebugMonitor is only supported on Microsoft operating systems.");
  180.  
  181.                 // Check for multiple instances. As the README.TXT of the msdn
  182.                 // example notes it is possible to have multiple debug monitors
  183.                 // listen on OutputDebugString, but the message will be randomly
  184.                 // distributed among all running instances so this won't be
  185.                 // such a good idea.               
  186.                 bool createdNew = false;
  187.                 m_Mutex = new Mutex(false,typeof(DebugMonitor).Namespace,out createdNew);
  188.                 //TODO: Temp Change Andrew Hodgson Friday July 31,2009 11:18 AM
  189.                 if(!createdNew)
  190.                     throw new ApplicationException("There is already an instance of 'DbMon.NET' running.");
  191.  
  192.                 SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR();
  193.  
  194.                 // Initialize the security descriptor.
  195.                 if(!InitializeSecurityDescriptor(ref sd,SECURITY_DESCRIPTOR_REVISION)) {
  196.                     throw CreateApplicationException("Failed to initializes the security descriptor.");
  197.                 }
  198.  
  199.                 // Set information in a discretionary access control list
  200.                 if(!SetSecurityDescriptorDacl(ref sd,true,IntPtr.Zero,false)) {
  201.                     throw CreateApplicationException("Failed to initializes the security descriptor");
  202.                 }
  203.  
  204.                 SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
  205.  
  206.                 // Create the event for slot 'DBWIN_BUFFER_READY'
  207.                 m_AckEvent = CreateEvent(ref sa,false,false,"DBWIN_BUFFER_READY");
  208.                 if(m_AckEvent == IntPtr.Zero) {
  209.                     throw CreateApplicationException("Failed to create event 'DBWIN_BUFFER_READY'");
  210.                 }
  211.  
  212.                 // Create the event for slot 'DBWIN_DATA_READY'
  213.                 m_ReadyEvent = CreateEvent(ref sa,false,false,"DBWIN_DATA_READY");
  214.                 if(m_ReadyEvent == IntPtr.Zero) {
  215.                     throw CreateApplicationException("Failed to create event 'DBWIN_DATA_READY'");
  216.                 }
  217.  
  218.                 // Get a handle to the readable shared memory at slot 'DBWIN_BUFFER'.
  219.                 m_SharedFile = CreateFileMapping(new IntPtr(-1),ref sa,PageProtection.ReadWrite,0,4096,"DBWIN_BUFFER");
  220.                 if(m_SharedFile == IntPtr.Zero) {
  221.                     throw CreateApplicationException("Failed to create a file mapping to slot 'DBWIN_BUFFER'");
  222.                 }
  223.  
  224.                 // Create a view for this file mapping so we can access it
  225.                 m_SharedMem = MapViewOfFile(m_SharedFile,SECTION_MAP_READ,0,0,512);
  226.                 if(m_SharedMem == IntPtr.Zero) {
  227.                     throw CreateApplicationException("Failed to create a mapping view for slot 'DBWIN_BUFFER'");
  228.                 }
  229.  
  230.                 // Start a new thread where we can capture the output
  231.                 // of OutputDebugString calls so we don't block here.
  232.                 m_Capturer = new Thread(new ThreadStart(Capture));
  233.                 m_Capturer.Start();
  234.             }
  235.         }
  236.  
  237.         /// <summary>
  238.         /// Captures
  239.         /// </summary>
  240.         private static void Capture() {
  241.             try {
  242.                 // Everything after the first DWORD is our debugging text
  243.                 IntPtr pString = new IntPtr(
  244.                     m_SharedMem.ToInt32() + Marshal.SizeOf(typeof(int))
  245.                 );
  246.  
  247.                 while(true) {
  248.                     SetEvent(m_AckEvent);
  249.  
  250.                     int ret = WaitForSingleObject(m_ReadyEvent,INFINITE);
  251.  
  252.                     // if we have no capture set it means that someone
  253.                     // called 'Stop()' and is now waiting for us to exit
  254.                     // this endless loop.
  255.                     if(m_Capturer == null)
  256.                         break;
  257.  
  258.                     if(ret == WAIT_OBJECT_0) {
  259.                         // The first DWORD of the shared memory buffer contains
  260.                         // the process ID of the client that sent the debug string.
  261.                         FireOnOutputDebugString(
  262.                             Marshal.ReadInt32(m_SharedMem),
  263.                                 Marshal.PtrToStringAnsi(pString));
  264.                     }
  265.                 }
  266.  
  267.             }
  268.             catch {
  269.                 throw;
  270.  
  271.                 // Cleanup
  272.             }
  273.             finally {
  274.                 Dispose();
  275.             }
  276.         }
  277.  
  278.         private static void FireOnOutputDebugString(int pid,string text) {
  279.             // Raise event if we have any listeners
  280.             if(OnOutputDebugString == null)
  281.                 return;
  282.  
  283. #if !DEBUG
  284.             try {
  285. #endif
  286.             OnOutputDebugString(pid,text);
  287. #if !DEBUG
  288.             } catch (Exception ex) {
  289.                 Console.WriteLine("An 'OnOutputDebugString' handler failed to execute: " + ex.ToString());
  290.             }
  291. #endif
  292.         }
  293.  
  294.         /// <summary>
  295.         /// Dispose all resources
  296.         /// </summary>
  297.         private static void Dispose() {
  298.             // Close AckEvent
  299.             if(m_AckEvent != IntPtr.Zero) {
  300.                 if(!CloseHandle(m_AckEvent)) {
  301.                     throw CreateApplicationException("Failed to close handle for 'AckEvent'");
  302.                 }
  303.                 m_AckEvent = IntPtr.Zero;
  304.             }
  305.  
  306.             // Close ReadyEvent
  307.             if(m_ReadyEvent != IntPtr.Zero) {
  308.                 if(!CloseHandle(m_ReadyEvent)) {
  309.                     throw CreateApplicationException("Failed to close handle for 'ReadyEvent'");
  310.                 }
  311.                 m_ReadyEvent = IntPtr.Zero;
  312.             }
  313.  
  314.             // Close SharedFile
  315.             if(m_SharedFile != IntPtr.Zero) {
  316.                 if(!CloseHandle(m_SharedFile)) {
  317.                     throw CreateApplicationException("Failed to close handle for 'SharedFile'");
  318.                 }
  319.                 m_SharedFile = IntPtr.Zero;
  320.             }
  321.  
  322.  
  323.             // Unmap SharedMem
  324.             if(m_SharedMem != IntPtr.Zero) {
  325.                 if(!UnmapViewOfFile(m_SharedMem)) {
  326.                     throw CreateApplicationException("Failed to unmap view for slot 'DBWIN_BUFFER'");
  327.                 }
  328.                 m_SharedMem = IntPtr.Zero;
  329.             }
  330.  
  331.             // Close our mutex
  332.             if(m_Mutex != null) {
  333.                 m_Mutex.Close();
  334.                 m_Mutex = null;
  335.             }
  336.         }
  337.  
  338.         public static bool IsRunning {
  339.             get {
  340.                 if(m_Capturer == null)
  341.                     return false;
  342.                 else
  343.                     return true;
  344.             }
  345.         }
  346.  
  347.  
  348.         /// <summary>
  349.         /// Stops this debug monitor. This call we block the executing thread
  350.         /// until this debug monitor is stopped.
  351.         /// </summary>
  352.         public static void Stop() {
  353.             if(!IsStarted)
  354.                 return;
  355.             if(!IsRunning)
  356.                 return;
  357.             lock(m_SyncRoot) {
  358.                 if(m_Capturer == null)
  359.                     throw new ObjectDisposedException("DebugMonitor","This DebugMonitor is not running.");
  360.                 m_Capturer = null;
  361.                 PulseEvent(m_ReadyEvent);
  362.                 while(m_AckEvent != IntPtr.Zero)
  363.                     ;
  364.             }
  365.         }
  366.  
  367.         /// <summary>
  368.         /// Helper to create a new application exception, which has automaticly the
  369.         /// last win 32 error code appended.
  370.         /// </summary>
  371.         /// <param name="text">text</param>
  372.         private static ApplicationException CreateApplicationException(string text) {
  373.             if(text == null || text.Length < 1)
  374.                 throw new ArgumentNullException("text","'text' may not be empty or null.");
  375.  
  376.             return new ApplicationException(string.Format("{0}. Last Win32 Error was {1}",
  377.                 text,Marshal.GetLastWin32Error()));
  378.         }
  379.  
  380.     }
  381. }
Advertisement
Add Comment
Please, Sign In to add comment