Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Problemas con los hilos en Builder (https://www.clubdelphi.com/foros/showthread.php?t=85496)

aguml 25-03-2014 10:17:22

Problemas con los hilos en Builder
 
Hola amigos, aqui otra vez con problemas :(.
El problema que tengo ahora es que estoy probando el manejo de un hilo y si uso Terminate() y FreeOnTerminate = true le doy y se termina el hilo y se destruye pero, si uso Suspend() el hilo no se detiene y sigue trabajando. Probé a poner FreeOnTerminate = false y usar WaitFor() despues de Suspend(), Resume(), o Terminate() pero me encuentro con que ejecuta cualquiera de esas lineas y llega al WaitFor() y se queda congelado todo, tanto el interfaz como el hilo.
No se por qué pasa esto y ademas tuve que poner un Application->ProcessMessage() para que procese los mensajes y me deje trabajar sobre el form para poder pulsar los botones ya que si no es así no me deja hacer nada y el form se congela mientras corre el hilo ¿no se supone que los hilos son entre otras cosas para evitar eso?

Es muy cortito asi que lo pongo aquí:

THilo.cpp:
Código:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "THilo.h"
#include "Unit1.h"
#pragma package(smart_init)

__fastcall THilo::THilo(bool CreateSuspended)
        : TThread(CreateSuspended)
{
        Priority = tpNormal;
        FreeOnTerminate = false;
}
//---------------------------------------------------------------------------
void __fastcall THilo::Execute()
{
        Synchronize(Bucle);
}
//---------------------------------------------------------------------------

void __fastcall THilo::Bucle()
{
        while(!Terminated)
        {
                Form1->Caption = Form1->Caption.ToDouble() + 1;
                Application->ProcessMessages();
                Sleep(1000);
        }
}

THilo.h:
Código:

//---------------------------------------------------------------------------

#ifndef THiloH
#define THiloH
//---------------------------------------------------------------------------
#include <Classes.hpp>
//---------------------------------------------------------------------------
class THilo : public TThread
{
private:
protected:
        void __fastcall Execute();

        void __fastcall THilo::Bucle();
public:
        __fastcall THilo(bool CreateSuspended);
};
//---------------------------------------------------------------------------
#endif

Unit1.cpp:
Código:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include "THilo.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

THilo *Hilo;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ButtonCrearClick(TObject *Sender)
{
        Hilo = new THilo(false);
        ButtonCrear->Enabled = false;
        ButtonPausa->Enabled = true;
        ButtonAbortar->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ButtonContinuarClick(TObject *Sender)
{
        Hilo->Resume();
        Hilo->WaitFor();
        ButtonContinuar->Enabled = false;
        ButtonPausa->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonPausaClick(TObject *Sender)
{
        Hilo->Suspend();
        Hilo->WaitFor();
        ButtonContinuar->Enabled = true;
        ButtonPausa->Enabled = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonAbortarClick(TObject *Sender)
{
        Hilo->Terminate();
        Hilo->WaitFor();
        delete Hilo;
        ButtonCrear->Enabled = true;
        ButtonPausa->Enabled = false;
        ButtonContinuar->Enabled = false;
        ButtonAbortar->Enabled = false;
}
//---------------------------------------------------------------------------

Ya digo que es una prueba y que luego ya colocaria bien cosas que tengo como global y cosas asi. El caption del Form1 es 0.
Estas cosas no me pasan usando las Apis de windows ResumeThread(), SuspendThread(), CreateThread(), y TerminateThread() ¿por que pasa con el que nos da Borland?

aguml 25-03-2014 10:48:26

Acabo de ver que es un problema con Synchronize(), o sea, si uso Synchronize no me funciona ni Resume(), ni Suspend(), WaitFor(). Pero creo que voy a necesitar del uso de Synchronize ¿como lo hago?

aguml 26-03-2014 18:45:44

bueno ya lo conseguí. Me explicaron que es porque Synchronize es para sincronizar entre hilos y que no habia que usarlo para todo y despues de algunos cambios ya fue. Ahora me encuentro con otro problema, necesito pasar variables desde donde creo el hilo hasta el execute y que dichas variables esten accesibles en ambos sitios. No puedo usar variables globales. ¿Como le puedo pasar variables al hilo? Con THilo hilo = new *THilo() solo puedo pasarle el estado al crearse. Probé a pasarle mas variables modificandola en la clase y me daba un error sobre que estaba haciendo una funcion abstracta o algo asi.

aguml 27-03-2014 10:42:41

Ahora me encuentro con otro problema, tengo usa serie de funciones en unit1 y quiero ejecutarlas desde el hilo pero por supuesto el hilo no sabe ni que existen ¿como hago para poder ejecutarlas desde el hilo?

escafandra 27-03-2014 14:17:24

Cita:

Empezado por aguml (Mensaje 474393)
Ahora me encuentro con otro problema, tengo usa serie de funciones en unit1 y quiero ejecutarlas desde el hilo pero por supuesto el hilo no sabe ni que existen ¿como hago para poder ejecutarlas desde el hilo?

Incluye unit1 (#include unit1.h) en la unit donde esté tu TThread y usa Synchronize para ejecutarlas.

Saludos.

aguml 27-03-2014 14:47:30

No me funciona o no se como hacerlo. Tengo esto en el Execute del hilo:

Código:

case EXCEPTION_SINGLE_STEP:
      if(FuncSingleStep != NULL)
      {
            try{
                  FuncSingleStep();
            }
            catch(...){
            }
      }

      dwContinueStatus = OnSingleStep(&DebugEv);
      break;

case EXCEPTION_GUARD_PAGE_VIOLATION:
    dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
    break;


y en el .h donde están las funciones tengo esto:

Código:

#include <vcl.h>
#include "THiloDebugger.h"

#ifndef DEBUGGERH
#define DEBUGGERH
#define EXCEPTION_GUARD_PAGE_VIOLATION 0x80000001L

//tipos de funciones eventos
typedef  void __fastcall(__closure* SYSTEMBREAKPOINT)(void);
typedef  void __fastcall(__closure* BREAKPOINT)(DWORD);
typedef  void __fastcall(__closure* EXCEPTION)(int,DWORD);
typedef  void __fastcall(__closure* CREATEPROCESS)(CREATE_PROCESS_DEBUG_INFO *);
typedef  void __fastcall(__closure* EXITPROCESS)(EXIT_PROCESS_DEBUG_INFO *);
typedef  void __fastcall(__closure* CREATETHREAD)(CREATE_THREAD_DEBUG_INFO *);
typedef  void __fastcall(__closure* EXITTHREAD)(EXIT_THREAD_DEBUG_INFO *);
typedef  void __fastcall(__closure* LOADDLL)(LOAD_DLL_DEBUG_INFO *);
typedef  void __fastcall(__closure* UNLOADDLL)(UNLOAD_DLL_DEBUG_INFO *);
typedef  void __fastcall(__closure* OUTPUTSTRING)(OUTPUT_DEBUG_STRING_INFO *);
typedef  void __fastcall(__closure* RIP)(RIP_INFO *);
typedef  void __fastcall(__closure* SINGLESTEP)(void);

typedef struct{
        SYSTEMBREAKPOINT FuncSystemBreakPoint;
        BREAKPOINT    FuncBPs;
        EXCEPTION    FuncException;
        CREATEPROCESS    FuncCreateProcess;
        EXITPROCESS    FuncExitProcess;
        CREATETHREAD    FuncCreateThread;
        EXITTHREAD    FuncExitThread;
        LOADDLL        FuncLoadDLL;
        UNLOADDLL    FuncUnLoadDLL;
        OUTPUTSTRING    FuncOutPutString;
        RIP        FuncRIP;
                SINGLESTEP      FuncSingleStep;
}TEventDebugCallBack;

  class TDebugger
  {

        //estructura debug_event requerida para obtener los eventos del proceso y poder gestionarlos

        public:
                //variable necesaria para salir cuando lo deseemos del bucle
                bool Terminated;
                                AnsiString PathFile;
                                bool DebugExists;

                TDebugger();
                ~TDebugger();
                bool InitDebug(void); //Carga la víctima
                void LoopDebug(void);  // inicio de la depuración

                // Solo tienen sentido si el depurador está parado
                AnsiString ReadString(DWORD, int);
                bool SetBP(DWORD);
                bool RemoveBP(DWORD);
                bool SetHBP(DWORD, int, int, int);
                bool RemoveHBP(int);
                bool RemoveAllHBP(void);

                DWORD GetEAX(void);
                DWORD GetECX(void);
                DWORD GetEDX(void);
                DWORD GetEBX(void);
                DWORD GetESP(void);
                DWORD GetEBP(void);
                DWORD GetESI(void);
                DWORD GetEDI(void);
                DWORD GetEIP(void);

                void SetSingleStep(void);
                DWORD GetLastAddress(void);

                void SetEAX(DWORD Valor);
                void SetECX(DWORD Valor);
                void SetEDX(DWORD Valor);
                void SetEBX(DWORD Valor);
                void SetESP(DWORD Valor);
                void SetEBP(DWORD Valor);
                void SetESI(DWORD Valor);
                void SetEDI(DWORD Valor);
                void SetEIP(DWORD Valor);

                // Establecer los CallBacks

                void SetOnSystemBreakPoint(SYSTEMBREAKPOINT);
                void SetOnBPs(BREAKPOINT);
                void SetOnException(EXCEPTION);
                void SetOnCreateProcess(CREATEPROCESS);
                void SetOnExitProcess(EXITPROCESS);
                void SetOnCreateThread(CREATETHREAD);
                void SetOnExitThread(EXITTHREAD);
                void SetOnLoadDll(LOADDLL);
                void SetOnUnLoadDll(UNLOADDLL);
                void SetOnOutPutString(OUTPUTSTRING);
                void SetOnRIP(RIP);
                                void SetOnSingleStep(SINGLESTEP);

                void SetCallBack(TEventDebugCallBack *);

                //**************************************


      private:
                                BOOL SearchBPOnList(DWORD, BYTE *, int *, bool *);

                // Eventos del hilo de depuración
                DWORD OnCreateThreadDebugEvent(LPDEBUG_EVENT);
                DWORD OnCreateProcessDebugEvent(LPDEBUG_EVENT);
                DWORD OnExitThreadDebugEvent(LPDEBUG_EVENT);
                DWORD OnExitProcessDebugEvent(LPDEBUG_EVENT);
                DWORD OnLoadDllDebugEvent(LPDEBUG_EVENT);
                DWORD OnUnloadDllDebugEvent(LPDEBUG_EVENT);
                DWORD OnOutputDebugStringEvent(LPDEBUG_EVENT);
                DWORD OnRipEvent(LPDEBUG_EVENT);

                // tratamiento de excepciones
                DWORD OnSingleStep(LPDEBUG_EVENT);
                DWORD OnBreakpointDebugEvent(LPDEBUG_EVENT);

                        DWORD LastExceptionAddress;

      protected:

                //Para la gestion de los BPs
                TList *ListaBPs; //Lista para la gestion de las estructuras BP
                int pos; //Para recuperar la posicion que ocupa un BP en la lista
                bool estadoBP; //Lo uso para saber si el usuario quiso eliminar el BP de la lista
                DWORD FAddrOnBPSS; // Dirección del BP a restaurar en EXCEPTION_SINGLE_STEP
                BYTE bOriginal; //Lo uso para obtener el byte original de la posición del BP

                //Estructura para cada BP
                typedef struct TBP
                {
                        DWORD dir;
                        BYTE byteOriginal;
                        bool estado; //true activado, false desactivado
                }*pBP;

        //Estructura startup requerida para createprocess
        STARTUPINFO si;

        //estructura process information requerida para el pid
        PROCESS_INFORMATION pi;
        //estructura context contiene los valores de los registros

        #pragma align 8  // no recuerdo bien si esto se hace con un #pragma
        CONTEXT con;

        // Funciones de callback
        SYSTEMBREAKPOINT FuncSystemBreakPoint;
        BREAKPOINT    FuncBPs;
                SINGLESTEP      FuncSingleStep;
        EXCEPTION    FuncException;
        CREATEPROCESS    FuncCreateProcess;
        EXITPROCESS    FuncExitProcess;
        CREATETHREAD    FuncCreateThread;
        EXITTHREAD    FuncExitThread;
        LOADDLL        FuncLoadDLL;
        UNLOADDLL    FuncUnLoadDLL;
        OUTPUTSTRING    FuncOutPutString;
        RIP        FuncRIP;
  };

#endif

No consigo hacer que reconozca las funciones en el execute del hilo aunque tenga el .h en el archivo de cabecera del hilo y use synchronize ¿como tengo que hacerlo para que funcione bien y compile?
En lo que pongo arriba no me reconocería ni FuncSingleStep, ni FuncSingleStep(), ni OnSingleStep(&DebugEv) pero me pasa igual con todas las demás.
He probado cosas como:

Synchronize(dwContinueStatus = TDebugger::OnExitProcessDebugEvent(&DebugEv));

pero creo que esto no explota de milagro jajaja. Ya no se que probar.

aguml 27-03-2014 17:58:47

Se me olvidó decir que el error que da es algo asi:
[C++ Error] THiloDebugger.cpp(67): E2247 'TDebugger::OnExitProcessDebugEvent(_DEBUG_EVENT *)' is not accessible
[C++ Error] THiloDebugger.cpp(67): E2283 Use . or -> to call 'TDebugger::OnExitProcessDebugEvent(_DEBUG_EVENT *)'

y si pongo en el TDebugger en el private esto:
friend class THiloDebugger;

desaparece el primer error pero el segundo no y tampoco se si lo que hago de poner friend es algo bueno o no porque lo acabo de descubrir buscando informacion al respecto y ademas me sigue quedando el segundo error que no se como solucionarlo.

escafandra 27-03-2014 23:48:39

Con lo que pones es difícil entender lo que te pasa. Ten en cuenta que Synchronize admite como parámetro un puntero a una función miembro de tu TThread:
Código:

void __fastcall Synchronize(TThreadMethod &Method);
Eso quiere decir que tienes que escribir un método miembro de tu clase TThread y que sea este el que invoque a las funciones que precises.

Saludos.

aguml 28-03-2014 09:54:38

1 Archivos Adjunto(s)
Adjunto el proyecto y decir que el problema lo tengo en THiloDebugger y que en TDebugger puse en el private "friend class THiloDebugger" para poder acceder a las funciones private del TDebugger desde el THiloDebugger. Ya digo que estoy dando palos de ciego porque no encuentro info al respecto y lo que encuentro no estoy seguro que sea lo que necesito. Si teneis algun pdf donde se hable del manejo de hilo avanzado en C++builder para aprender y me lo pasaseis me hariais un gran favor porque en este tema estoy muy muy verde :o

escafandra 28-03-2014 14:27:30

Sin profuncizar mucho en tu código veo errores de bulto. Por ejemplo:

Código:

void __fastcall THiloDebugger::OnCreateThreadDebugEventThread()
{
        dwContinueStatus = TDebugger::OnCreateThreadDebugEvent(&DebugEv);
}

En caso de que OnCreateThreadDebugEvent fuese una función miembro estática de la clase TDebugger, podrías invocarla así, pero como no lo es, deber hacerlo a través de un objeto de esa clase.

El mismo error se repite varias veces. Piensa que necesitas hacer para diseñar las clases. Un TThread no es mas que otra clase y se rige por los mismos principios. Si vas a acceder a objetos o variables desde distintos threads al mismo tiempo ten en cuenta que escrituras simultáneas pueden corromper los hilos, de aquí que exista Synchronize. Las API de Windows
suelen ser thread-safe pero la VCL no.

Espero haberte orientado.


Saludos.

aguml 28-03-2014 15:01:38

pero en el loop uso Synchronize(OnCreateThreadDebugEventThread); para ejecutar el metodo que has puesto y en la clase TDebugger tengo puesto friend class THiloDebugger que se supone eso me daría acceso a las funciones privadas de la clase TDebugger desde la clase THiloDebugger ¿no? ¿entonces como lo hago?

escafandra 28-03-2014 16:52:48

No puedes invocar una función no estática con el nombre de la clase, sino a través de un objeto, por muy amiga que sea otra clase.

Soluciona ese punto antes de buscar más errores.


Saludos.

aguml 28-03-2014 17:25:53

y entonces ¿Tengo que crear un objeto de tipo TDebugger dentro del THiloDebugger? ¿O como se hace eso?

escafandra 28-03-2014 17:38:28

Exacto.

Saludos.

aguml 28-03-2014 18:47:00

¿Y da igual que el TDebugger que cree en THiloDebugger no sea el que realmente está en Unit1? ¿Solo lo creo al principio del evento execute y lo elimino al final y simplemente lo uso para ejecutar sus funciones? El siguiente problema seria con los punteros a funciones que están en protected y que no son accesibles desde el objeto como por ejemplo funcexceptionbreakpoint (o algo asi era que lo digo de cabeza desde el movil) ¿Como accedo a eso desde fuera de la clase TDebugger?

escafandra 28-03-2014 19:21:24

Cita:

Empezado por aguml (Mensaje 474487)
¿Y da igual que el TDebugger que cree en THiloDebugger no sea el que realmente está en Unit1?

Puedes crear un objeto de cualquier clase en cualquier unit siempre que tengas incluido su archivo cabecera y el cpp esté en el proyecto.

Cita:

Empezado por aguml (Mensaje 474487)
¿Solo lo creo al principio del evento execute y lo elimino al final y simplemente lo uso para ejecutar sus funciones?

Si es lo que necesitas, pues si.

Cita:

Empezado por aguml (Mensaje 474487)
El siguiente problema seria con los punteros a funciones que están en protected y que no son accesibles desde el objeto como por ejemplo funcexceptionbreakpoint (o algo asi era que lo digo de cabeza desde el movil) ¿Como accedo a eso desde fuera de la clase TDebugger?

Puedes acceder a protected pero no a private a no ser que la clase sea amiga. Accedes a través de un objeto de la clase TDebugger.


Saludos.

aguml 28-03-2014 19:42:27

ok amigo mil gracias, hare lo que me indicas y ya te cuento como me va.

aguml 28-03-2014 21:20:09

Pues acabo de probar lo siguiente.

En el constructor del hilo he puesto esto:
Código:

__fastcall THiloDebugger(bool CreateSuspended, STARTUPINFO *startupInfo, TList *ListBPs, AnsiString Path, PROCESS_INFORMATION *processInfo, CONTEXT *context, DWORD *LastExcepAddress, TDebugger *Debugger);
El ultimo parametro es un puntero al objeto de tipo TDebugger que se crea.

En el protected del THiloDebugger he añadido esto:
Código:

TDebugger *Dbg;
Y en el constructor añado esto:
Código:

Dbg = Debugger;
En el TDebugger hago esto:
Código:

HiloDbg = new THiloDebugger(true, &si, ListaBPs, PathFile, &pi, &con, &LastExceptionAddress, this);
Y el miembro de THiloDebugger queda asi:
Código:

void __fastcall THiloDebugger::OnCreateThreadDebugEventThread()
{
        dwContinueStatus = Dbg->OnExitDebugEvent(&DebugEv);
}

Al intentar compilar me dice esto:
Código:

[C++ Error] THiloDebugger.cpp(217): E2316 'OnExitDebugEvent' is not a member of 'TDebugger'
OnExitDebugEvent está dentro del private de TDebugger. y por lo tanto no me deja acceder desde el objeto tipo TDebugger pero si me deja desde los metodos de TDebugger. ¿como lo hago?

aguml 29-03-2014 00:06:13

Vale, esto ya compila y corre y lanza el depurador y solo me falta un detalle. He creado esto:

En el protected del THiloDebugger:
TEventDebugCallBack FuncEventos;

En el Execute:
Synchronize(GetFuncEvents);

Este es el metodo:
Código:

void __fastcall THiloDebugger::GetFuncEvents()
{
        Dbg->GetCallBack(&FuncEventos);
}

Y aqui en el TDebugger:
// Obtener los CallBacks

void TDebugger::GetOnSystemBreakPoint(SYSTEMBREAKPOINT func){
    func = FuncSystemBreakPoint;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnBPs(BREAKPOINT func){
    func = FuncBPs;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnSingleStep(SINGLESTEP func){
    func = FuncSingleStep;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnException(EXCEPTION func){
    func = FuncException;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnCreateProcess(CREATEPROCESS func){
    func = FuncCreateProcess;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnExitProcess(EXITPROCESS func){
    func = FuncExitProcess;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnCreateThread(CREATETHREAD func){
    func = FuncCreateThread;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnExitThread(EXITTHREAD func){
    func = FuncExitThread;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnLoadDll(LOADDLL func){
    func = FuncLoadDLL;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnUnLoadDll(UNLOADDLL func){
    func = FuncUnLoadDLL;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnOutPutString(OUTPUTSTRING func){
    func = FuncOutPutString;
}
//---------------------------------------------------------------------------

void TDebugger::GetOnRIP(RIP func){
    func = FuncRIP;
}
//---------------------------------------------------------------------------

void TDebugger::GetCallBack(TEventDebugCallBack *tEstruc){
    GetOnSystemBreakPoint(tEstruc->FuncSystemBreakPoint);
    GetOnBPs(tEstruc->FuncBPs);
    GetOnException(tEstruc->FuncException);
    GetOnCreateProcess(tEstruc->FuncCreateProcess);
    GetOnExitProcess(tEstruc->FuncExitProcess);
    GetOnCreateThread(tEstruc->FuncCreateThread);
    GetOnExitThread(tEstruc->FuncExitThread);
    GetOnLoadDll(tEstruc->FuncLoadDLL);
    GetOnUnLoadDll(tEstruc->FuncUnLoadDLL);
    GetOnOutPutString(tEstruc->FuncOutPutString);
    GetOnRIP(tEstruc->FuncRIP);
    GetOnSingleStep(tEstruc->FuncSingleStep);
}

El problema es el siguiente, no me guarda los punteros a las funciones en la estructura y eso que si pongo un breakpoint por ejemplo en GetOnSystemBreakPoint veo como le asigna correctamente {TFormPrincipal::OnSystemBreakPoint,:00A16EF0} al miembro de la estructura pero al salir de ese metodo me aparece ese miembro de la estructura como NULL,NULL y no se como solucionarlo ya que es lo ultimo que tengo que solucionar para que esto corra como quiero por ahora.

ecfisa 29-03-2014 00:14:32

Hola aguml.

Por favor, no olvides usar las etiquetas [code] tu_codigo [/code] cuando pongas código C++ en el mensaje.

Saludos :)


La franja horaria es GMT +2. Ahora son las 09:07:15.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi