Adjuntar depurador en C # a otro proceso

Me gustaría poder adjuntar automáticamente un depurador, algo así como: System.Diagnostics.Debugger.Launch() , excepto en lugar del proceso actual a otro proceso con nombre. Tengo un nombre de proceso y un PID para identificar el otro proceso.

es posible?

Editar: GSerjo ofreció la solución correcta. Me gustaría compartir algunas ideas sobre cómo mejorarlo (y una explicación). Espero que mi respuesta mejorada sea útil para otros que experimenten el mismo problema.


Adjuntar el depurador VS a un proceso

A mano

  1. Abra el Administrador de tareas de Windows ( Ctrl + Shift + Esc ).
  2. Ir a los Processes Tabulación.
  3. Haga clic derecho en el proceso.
  4. Seleccione Debug .

O bien, en Visual Studio, seleccione Debug > Attach to Process...

Los resultados variarán dependiendo de si tiene acceso al código fuente.

Automáticamente con C #

Nota de precaución: el siguiente código es frágil en el sentido de que ciertos valores, como el número de versión de Visual Studio, están codificados. Tenga esto en cuenta en el futuro si planea distribuir su progtwig.

Antes que nada, agregue una referencia a EnvDTE a su proyecto (haga clic con el botón derecho en la carpeta de referencias en el explorador de soluciones, agregue referencia). En el siguiente código, solo mostraré las inusuales directivas de uso; los normales, como el using System se omiten.

Como está interactuando con COM , debe asegurarse de decorar su método Main (el punto de entrada de su aplicación) con STAThreadAttribute .

Luego, debe definir la interfaz IOleMessageFilter que le permitirá interactuar con los métodos COM definidos (tenga en cuenta el ComImportAttribute ). Necesitamos acceder al filtro de mensajes para poder volver a intentar si el componente COM de Visual Studio bloquea una de nuestras llamadas.

 using System.Runtime.InteropServices; [ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IOleMessageFilter { [PreserveSig] int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); [PreserveSig] int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType); [PreserveSig] int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType); } 

Ahora, necesitamos implementar esta interfaz para manejar los mensajes entrantes:

 public class MessageFilter : IOleMessageFilter { private const int Handled = 0, RetryAllowed = 2, Retry = 99, Cancel = -1, WaitAndDispatch = 2; int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo) { return Handled; } int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType) { return dwRejectType == RetryAllowed ? Retry : Cancel; } int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType) { return WaitAndDispatch; } public static void Register() { CoRegisterMessageFilter(new MessageFilter()); } public static void Revoke() { CoRegisterMessageFilter(null); } private static void CoRegisterMessageFilter(IOleMessageFilter newFilter) { IOleMessageFilter oldFilter; CoRegisterMessageFilter(newFilter, out oldFilter); } [DllImport("Ole32.dll")] private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter); } 

Definí los valores de retorno como constantes para una mejor legibilidad y refactoré todo un poco para eliminar parte de la duplicación del ejemplo de MSDN, así que espero que lo encuentres por sí mismo. extern int CoRegisterMessageFilter es nuestra conexión con el código de filtro de mensajes no administrados; puede leer la palabra clave extern en MSDN .

Ahora todo lo que queda es un código que ilustra el uso:

 using System.Runtime.InteropServices; using EnvDTE; [STAThread] public static void Main() { MessageFilter.Register(); var process = GetProcess(7532); if (process != null) { process.Attach(); Console.WriteLine("Attached to {0}", process.Name); } MessageFilter.Revoke(); Console.ReadLine(); } private static Process GetProcess(int processID) { var dte = (DTE)Marshal.GetActiveObject("VisualStudio.DTE.10.0"); var processes = dte.Debugger.LocalProcesses.OfType(); return processes.SingleOrDefault(x => x.ProcessID == processID); } 

Mira esto

 using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using EnvDTE; using NUnit.Framework; namespace UnitTests { [TestFixture] public class ForTest { [STAThread] [Test] public void Test() { var dte = (DTE) Marshal.GetActiveObject("VisualStudio.DTE.10.0"); MessageFilter.Register(); IEnumerable processes = dte.Debugger.LocalProcesses.OfType(); var process = processes.SingleOrDefault(x => x.ProcessID == 6152); if (process != null) { process.Attach(); } } } public class MessageFilter : IOleMessageFilter { // // Class containing the IOleMessageFilter // thread error-handling functions. // Start the filter. // // IOleMessageFilter functions. // Handle incoming thread requests. #region IOleMessageFilter Members int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo) { //Return the flag SERVERCALL_ISHANDLED. return 0; } // Thread call was rejected, so try again. int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType) { if (dwRejectType == 2) // flag = SERVERCALL_RETRYLATER. { // Retry the thread call immediately if return >=0 & // <100. return 99; } // Too busy; cancel call. return -1; } int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType) { //Return the flag PENDINGMSG_WAITDEFPROCESS. return 2; } #endregion public static void Register() { IOleMessageFilter newFilter = new MessageFilter(); IOleMessageFilter oldFilter = null; CoRegisterMessageFilter(newFilter, out oldFilter); } // Done with the filter, close it. public static void Revoke() { IOleMessageFilter oldFilter = null; CoRegisterMessageFilter(null, out oldFilter); } // Implement the IOleMessageFilter interface. [DllImport("Ole32.dll")] private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter); } [ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IOleMessageFilter { [PreserveSig] int HandleInComingCall( int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); [PreserveSig] int RetryRejectedCall( IntPtr hTaskCallee, int dwTickCount, int dwRejectType); [PreserveSig] int MessagePending( IntPtr hTaskCallee, int dwTickCount, int dwPendingType); } } 
  • Cómo: Crear y adjuntar a otra instancia de Visual Studio
  • Cómo solucionar: "La aplicación está ocupada" y los errores de "Llamada rechazada por Callee"

Forma más simple de hacerlo.

 public static void Attach(DTE2 dte) { var processes = dte.Debugger.LocalProcesses; foreach (var proc in processes.Cast().Where(proc => proc.Name.IndexOf("YourProcess.exe") != -1)) proc.Attach(); } internal static DTE2 GetCurrent() { var dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.12.0"); // For VisualStudio 2013 return dte2; } 

Uso:

 Attach(GetCurrent()); 

Una opción es correr; vsjitdebugger.exe -p ProcessId

Es posible usar Process.Start para hacer esto dentro de la aplicación ac #.