none
Pasar datos a CreateThread desde C# RRS feed

  • Pregunta

  • Buenos días.

    Necesito llamar a la función CreateThread (importada con DllImport) con una instancia de datos, que debe ser recogida dentro del método llamado al crear el hilo, para utilizar los datos y luego poder usarlos también en el hilo creador.

    Tengo el código así definido:

    using System.Runtime.InteropServices; ... [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int CreateThread( IntPtr lpThreadAttributes, UInt32 dwStackSize, IntPtr lpStartAddress, IntPtr param, UInt32 dwCreationFlags, UInt32 lpThreadId ); public class ParamTarea { public bool Activo; public Log log; public string ficheroAAbrir; public bool TodoCreado; } class ClaseParaHilo { public static void LanzarTarea(object objParam) { ParamTarea p = (ParamTarea)objParam; ClaseLENMUL03 instancia = new ClaseLENMUL03(p.Activo, p.log, p.ficheroAAbrir); p.TodoCreado = true; instancia.EjecutarTarea(); } //Constructor ClaseLENMUL03 (bool Activo, Log log, string ficheroAAbrir) { this.Activo = Activo; this.log = log; this.ficheroAAbrir = ficheroAAbrir; } //Método que ejecuta la tarea en sí misma void EjecutarTarea() { //Código a ejecutar... } }

    La función ya la tengo referenciada para pasarla:

        public delegate void FuncionWin32(object o);
    ...
        var delfuncion = new FuncionWin32(ClaseLENMUL03.LanzarTarea);
        IntPtr ptrfuncion = Marshal.GetFunctionPointerForDelegate(delfuncion);

    Todo lo puesto arriba funciona perfectamente siempre que como datos, que es el cuarto parametro, le pase un puntero nulo (IntPtr.Zero), lo que implica que no paso datos al método y por lo tanto la variable objParam aparece como null.

    int intpr = CreateThread(IntPtr.Zero, 0, ptrfuncion, IntPtr.Zero, 0, 0);

    Por más vueltas que le he dado, no he encontrado la forma de poder pasarle nada. He intentado pasar la instancia, también obtener el puntero a ella usando GCHandle, y susmétodos .Alloc(...) y .ToIntPtr(...)

    Ya que la definición de ese parámetro en la función original está definido como LPVOID, es decir, un puntero sin tipo a cualquier cosa, he probado a cambiar el DllImport y poner el parámetro con diferentes tipos:

    • Si lo declaro como IntPtr y le paso el puntero obtenido con GCHandle, me da una InvalidOleVariantTypeException

    ParamTarea pt = new ParamTarea() { ... }; object oPt = (object)pt; GCHandle handlePt = GCHandle.Alloc(oPt); IntPtr ptrPt = GCHandle.ToIntPtr(handlePt); ...

    int intpr = CreateThread(IntPtr.Zero, 0, ptrfuncion, ptrPt , 0, 0);

    • Si lo declaro en el DllImport como ParamTarea y le paso la instancia, me da igualmente una InvalidOleVariantTypeException

    • Si lo declaro como Object y le paso la instancia con un cast a object, me da un error de PInvokeStackImbalance (parece que indica que las firmas no son coincidentes)

    Ya no sé que más probar...

    Tan sólo necesito poder pasar y recuperar un parámetro... tiene que poder hacerse, ya que la firma de la función tiene ese parámetro para ello, o al menos eso parece.

    Muchísimas gracias.

    P.D.: Si alguien está pensando que por qué me complico y no uso los objetos Thread de C#, os comento que todo esto surge porque tengo la necesidad (requerimiento) de poder destruir un hilo (no un proceso ejecutable) desde el hilo que lo lanzó, no sólo con Abort, sino si se ha quedado temporal o completamente colgado (por ejemplo, si ha hecho un broadcast para buscar un servidor en red, que se queda a la espera durante bastantes segundos...) y necesito poder pararlo A LO BRUTO (con todo lo que conlleva) con el TerminateThread, que se carga el hilo a lo bruto y luego yo me dedico a liberar los recursos que pueda.

    Es decir, no me vale son pedir el Abort y que se lance la ThreadAbortException CUANDO TERMINE esa instrucción que puede quedarse parada durante 30s... por ejemplo.







    jueves, 20 de febrero de 2014 13:08