Foro de elhacker.net

Programación => .NET (C#, VB.NET, ASP) => Mensaje iniciado por: El Benjo en 18 Febrero 2016, 07:36 am



Título: Obtener el nombre de la función que ejecuta un hilo.
Publicado por: El Benjo en 18 Febrero 2016, 07:36 am
Hola. Buen día.

No encuentro la manera de obtener el nombre del método que está ejecutando un hilo en particular.

Ya busqué dentro de la clase ProcessThread pero creo que esa clase no me da esa información.

Muchas gracias por su ayuda.


Título: Re: Obtener el nombre de la función que ejecuta un hilo.
Publicado por: fary en 18 Febrero 2016, 09:27 am
Quieres saber el nombre que tu le diste a la funcion al programarla?
Para que quieres saber eso?

Saludos.


Título: Re: Obtener el nombre de la función que ejecuta un hilo.
Publicado por: Eleкtro en 18 Febrero 2016, 09:54 am
Creo que tu pregunta se puede interpretar de varias maneras, y de si intentas obtenerlo el nombre del método desde el thread adicional o el thread de la UI, por favor, muestra el código relevante.

De todas formas, creo que este ejemplo basado en Reflection solventará tus dudas:

Vb.Net:
Código
  1. Imports System.Reflection
  2. Imports System.Threading.Tasks
  3.  
  4. Public NotInheritable Class Main : Inherits Form
  5.  
  6.    Private Sub Test() Handles MyBase.Shown
  7.  
  8.        Dim t As New Thread(AddressOf Method1)
  9.        t.Start()
  10.        Debug.WriteLine("Main: " & MethodBase.GetCurrentMethod().Name) ' Test
  11.  
  12.        Task.Factory.StartNew(
  13.            Sub()
  14.                Debug.WriteLine("Lambda: " & MethodBase.GetCurrentMethod().Name) ' _Lambda$__n
  15.            End Sub)
  16.  
  17.    End Sub
  18.  
  19.    Private Sub Method1()
  20.  
  21.        Dim stackTrace As New StackTrace
  22.        Debug.WriteLine("Thread: " & MethodBase.GetCurrentMethod().Name) ' Method1
  23.  
  24.    End Sub
  25.  
  26. End Class

Traducción online a C#:
Código
  1. using System.Diagnostics;
  2. using System.Reflection;
  3. using System.Threading.Tasks;
  4.  
  5. public sealed class Main : Form {
  6.  
  7.    public Form1() {
  8.        Shown += Test;
  9.    }
  10.  
  11.    private void Test() {
  12.        Thread t = new Thread(Method1);
  13.        t.Start();
  14.        Debug.WriteLine("Main: " + MethodBase.GetCurrentMethod().Name); // Test
  15.  
  16.        Task.Factory.StartNew(() => { Debug.WriteLine("Lambda: " + MethodBase.GetCurrentMethod().Name); }); // _Lambda$__n
  17.    }
  18.  
  19.    private void Method1() {
  20.        StackTrace stackTrace = new StackTrace();
  21.        Debug.WriteLine("Thread: " + MethodBase.GetCurrentMethod().Name); // Method1
  22.    }
  23.  
  24. }
  25.  
  26. //=======================================================
  27. //Service provided by Telerik (www.telerik.com)
  28. //=======================================================

Resultado de ejecución:
Citar
Main: Test
Thread: Method1
Lambda: _Lambda$__2



Si lo prefieres también puedes utilizar una propiedad como esta de aquí abajo para tenerla siempre a mano.

El código fuente lo he extraido de mi API ElektroKit:

(http://i.imgur.com/5nJK3J2.png) (https://github.com/ElektroStudios/ElektroKit/blob/6fbce42a8c59b0a06f679a369d19b7a1282023d0/Solution/v1.2/Elektro.Diagnostics/Tools/DebugUtil_Properties.vb)

Vb.Net:
Código
  1. ''' ----------------------------------------------------------------------------------------------------
  2. ''' <summary>
  3. ''' Gets the current executing member in the stack trace of the application.
  4. ''' </summary>
  5. ''' ----------------------------------------------------------------------------------------------------
  6. ''' <example> This is a code example.
  7. ''' <code>
  8. ''' Private Sub TestMethod()
  9. '''
  10. '''     MsgBox(CurrentMember.Name)
  11. '''
  12. ''' End Sub
  13. ''' </code>
  14. ''' </example>
  15. ''' ----------------------------------------------------------------------------------------------------
  16. ''' <value>
  17. ''' The current executing member.
  18. ''' </value>
  19. ''' ----------------------------------------------------------------------------------------------------
  20. Public Shared ReadOnly Property CurrentMember As MethodBase
  21.    <DebuggerStepThrough>
  22.    Get
  23.        Dim stackTrace As New StackTrace
  24.        Return stackTrace.GetFrame(1).GetMethod()
  25.    End Get
  26. End Property

Traducción online a C#:
Código
  1. /// ----------------------------------------------------------------------------------------------------
  2. /// <summary>
  3. /// Gets the current executing member in the stack trace of the application.
  4. /// </summary>
  5. /// ----------------------------------------------------------------------------------------------------
  6. /// <value>
  7. /// The current executing member.
  8. /// </value>
  9. /// ----------------------------------------------------------------------------------------------------
  10. public static MethodBase CurrentMember {
  11. get {
  12. StackTrace stackTrace = new StackTrace();
  13. return stackTrace.GetFrame(1).GetMethod();
  14. }
  15. }
  16.  
  17. //=======================================================
  18. //Service provided by Telerik (www.telerik.com)
  19. //=======================================================

Modo de empleo:

Vb.Net:
Código
  1. Imports System.Reflection
  2. Imports System.Threading.Tasks
  3.  
  4. Public NotInheritable Class Main : Inherits Form
  5.  
  6.    Private Sub Test() Handles MyBase.Shown
  7.  
  8.        Dim t As New Thread(AddressOf Method1)
  9.        t.Start()
  10.        Debug.WriteLine("Main: " & CurrentMember.Name) ' Test
  11.  
  12.        Task.Factory.StartNew(
  13.            Sub()
  14.                Debug.WriteLine("Lambda: " & CurrentMember.Name) ' _Lambda$__n
  15.            End Sub)
  16.  
  17.    End Sub
  18.  
  19.    Private Sub Method1()
  20.  
  21.        Dim stackTrace As New StackTrace
  22.        Debug.WriteLine("Thread: " & CurrentMember.Name) ' Method1
  23.  
  24.    End Sub
  25.  
  26.    Public Shared ReadOnly Property CurrentMember As MethodBase
  27.        <DebuggerStepThrough>
  28.        Get
  29.            Dim stackTrace As New StackTrace
  30.            Return stackTrace.GetFrame(1).GetMethod()
  31.        End Get
  32.    End Property
  33.  
  34. End Class

C#:
Código
  1. using System.Diagnostics;
  2. using System.Reflection;
  3. using System.Threading.Tasks;
  4.  
  5. public sealed class Main : Form {
  6.  
  7.    public Form1() {
  8.        Shown += Test;
  9.    }
  10.  
  11.    private void Test() {
  12.        Thread t = new Thread(Method1);
  13.        t.Start();
  14.        Debug.WriteLine("Main: " + CurrentMember.Name); // Test
  15.  
  16.        Task.Factory.StartNew(() => { Debug.WriteLine("Lambda: " + CurrentMember.Name); }); // _Lambda$__n
  17.    }
  18.  
  19.    private void Method1() {
  20.        StackTrace stackTrace = new StackTrace();
  21.        Debug.WriteLine("Thread: " + CurrentMember.Name); // Method1
  22.    }
  23.  
  24.    public static MethodBase CurrentMember {
  25.        [DebuggerStepThrough()]
  26.        get {
  27.            StackTrace stackTrace = new StackTrace();
  28.            return stackTrace.GetFrame(1).GetMethod();
  29.        }
  30.    }
  31.  
  32. }
  33.  
  34. //=======================================================
  35. //Service provided by Telerik (www.telerik.com)
  36. //=======================================================

Resultado de ejecución:
Citar
Main: Test
Thread: Method1
Lambda: _Lambda$__2

Saludos!


Título: Re: Obtener el nombre de la función que ejecuta un hilo.
Publicado por: El Benjo en 18 Febrero 2016, 19:53 pm
Muy bien, tienen razón, quizá no he sabido como expresar mi necesidad.

Mi aplicación hace lo siguiente:

Carga librerías dinámicas que encuentre en un directorio en específico y corre (de encontrase) el método 'start()' en cada una de ellas.

El código de las librerías puede correr varios hilos sobre distintos métodos y estos, al ser de terceros, desconozco su nombre.

Ahora la aplicación muestra los hilos que se encuentran dentro del dominio de la aplicación. Lo que necesito pero no he encontrado información para hacerlo es saber el método que está ejecutando cada uno de esos hilos y, de ser posible el ensamblado al que pertenece dicho método.

La única forma que se me ha ocurrido para hacerlo es con la propiedad ProcessThreads al obtener un objeto del proceso actual y sacar la dirección en memoria en cada uno de estos elementos y compararla con la dirección en memoria de cada uno de los métodos contenidos en cada ensamblado. Pero esto se me hace un desperdicio de ciclos de procesador, además de que tendría que explorar de manera recursiva cada clase dentro del ensamblado para obtener la dirección de su método y compararla. Y si el método está declarado en una clase fuera del ensamblado entonces ya no podré obtenerla por este método.

Elektro, el código relevante no puedo mostrarlo porque realmente no existe. Espero que exista una manera de hacer esto que pido: Conocer el nombre de los métodos que ejecuta cada hilo dentro del proceso actual, la clase o ensamblado al que pertenece y saberlo desde un hilo distinto al que lo está ejecutando.

Muchas gracias a todos quienes se tomen la molestia en responder.


Título: Re: Obtener el nombre de la función que ejecuta un hilo.
Publicado por: Eleкtro en 18 Febrero 2016, 20:22 pm
En resumidas cuentas tendrías que obtener el stack-trace de ese hilo o hilos, y analizar sus stack-frames para obtener el método. Pero claro, para eso debes administrar/crear tu los hilos... si los hilos son creados por algún método de dicha librería entonces sinceramente no se me ocurre una solución.

De todas formas te muestro un ejemplo por si te sirve de algo...

Observa la propiedad que publiqué, la metodología sería parecida solo que utilizando el constructor que acepta un thread:
Código
  1. Thread t = Thread.CurrentThread;
  2. StackTrace st = new StackTrace(t, needFileInfo: false);
( esta metodología está considerada obsoleta por Microsoft, sin embargo, tampoco proporcionan ninguna alternativa: https://msdn.microsoft.com/en-us/library/t2k35tat%28v=vs.110%29.aspx (https://msdn.microsoft.com/en-us/library/t2k35tat%28v=vs.110%29.aspx))

Un ejemplo algo más extenso, en Vb.Net:
Código
  1. Public NotInheritable Class Form1 : Inherits Form
  2.  
  3.    Private Sub Test() Handles MyBase.Shown
  4.  
  5.        Dim t As Thread = New Thread(AddressOf Method1)
  6.        t.Start()
  7.        Thread.Sleep(100)
  8.        t.Suspend()
  9.  
  10.        Dim st As New StackTrace(t, needFileInfo:=False)
  11.        Console.WriteLine(st.GetFrame(2).GetMethod.Name) ' Resultado: Method2
  12.  
  13.    End Sub
  14.  
  15.    Public Sub Method1()
  16.        Method2()
  17.    End Sub
  18.  
  19.    Public Sub Method2()
  20.        Thread.Sleep(Integer.MaxValue)
  21.    End Sub
  22.  
  23. End Class

PD: ElBenjo, ¿es necesario determinar esa información en tiempo de ejecución por algún motivo?, ¿no te basta con cojer el .Net Reflector y examinar el código fuente de la librería a ver que métodos se llaman en sus threads?.

Saludos!


Título: Re: Obtener el nombre de la función que ejecuta un hilo.
Publicado por: El Benjo en 19 Febrero 2016, 00:28 am
Elektro, gracias por responder.

No, lo que pasa es que no soy yo el que necesita la información sino mi aplicación. Es decir, que toda la información que necesito es información en tiempo de ejecución y yo no sé (como ya comenté antes) que librerías son las que van a estar ejecutando código ni cual es el código. La aplicación se basa en módulos y los módulos pueden ser escritos por cualquiera, sin embargo la aplicación deber ser capaz de reconocer no sólo la cantidad de hilos que se ejecutan en el proceso sino también el módulo que los creo.

Seguiré buscando a ver si encuentro algo. Muchas gracias, Elektro. Ah, y de paso felicitarte por el trabajo de tu librería. La verdad se nota que le invertiste mucho tiempo y esfuerzo.


Título: Re: Obtener el nombre de la función que ejecuta un hilo.
Publicado por: Eleкtro en 19 Febrero 2016, 00:53 am
la aplicación deber ser capaz de reconocer no sólo la cantidad de hilos que se ejecutan en el proceso sino también el módulo que los creo.

Al parecer eso que ahora mencionas es imposible de monitorizar al no poder ser notificado por la propia aplicación, según un respuesta comentario del gran mago y gurú de .Net, Hans Passant:
  • detect when a new managed thread has been created - StackOverflow (http://stackoverflow.com/questions/6761696/detect-when-a-new-managed-thread-has-been-created)
( desafortunadamente para ti, te recomiendo que no le des más vueltas, si lo dice Hans Passant no hay más que decir, es un genio del conocimiento qué nunca se equivoca. )

Sin embargo, al parecer existe algún proyecto como este de aquí abajo, el cual, supuestamente, desde un proceso externo a tu app, es capaz de monitorizar la creación de threads de tu app.
  • Managed Stack Explorer (MSE) - CodePlex (http://mse.codeplex.com/)

Cita de: MSE
MSE works by quickly attaching to a process when a stack trace is requested and the detaching the second the stack trace has been retrieved.
This way the interference in the normal operation of the process is minimized.

This means it has to done by an external process.
Parece interesante, no lo he testeado ...pero imagino que te podría servir. Y si no te sirve del todo pues... lo bueno es que puedes coger el source del MSE y adaptarlo a tus necesidades.

Otro proyecto parecido sería este:
  • StackDump - CodePlex (http://stackdump.codeplex.com/)
Tampoco lo he testeado ni se si tiene las mismas funcionalidades que el otro.

PD: Gracias por el comentario del ElektroKit !!

Saludos!


Título: Re: Obtener el nombre de la función que ejecuta un hilo.
Publicado por: El Benjo en 19 Febrero 2016, 06:22 am
Muchíssimas gracias por la ayuda, Elektro y por tomarte el tiempo para investigar. La verdad es que llevo casi una semana investigado y no he podido encontrar nada. Veré si eso me sirve, y si no, pues adaptaré el código que mencionas. La otra opción es modificar mi aplicación. Sin embargo, somos programadores y como tal, no debería haber un límite en lo que podemos crear, por eso me rehusaré lo más que pueda a modificar los objetivos de mi aplicación.

Un gran saludo, Elektro.