STRIGANOV SERGEY: Software development.

Разработка программного обеспечения на: C++, T-SQL, VBS, JavaScript, PHP

TDebugInterceptor ( C++Builder, to do: Delphi )

Мне достался в наследство проект 
с количеством форм > 100,
строк исходного кода > 500 000 
Все это безумие нужно было не только поддерживать и развивать,
но еще и пересадить на другой SQL сервер.

Когда пользователь звонит весь в панике - почему не работает то-то и то-то
Что я могу ответить про незнакомую мне программу ?

Только посмотрев логи - можно понять какие формы он открывал, в какие поля что вводил, на какие кнопки в какой последовательности нажимал, какой SQL трафик был при этом порожден, как долго и с каким результатом выполнялись запросы - может быть видно, например, что пользователь забыл ввести в такое-то поле такие-то данные или произошла ошибка связи с сервером и тд. и т.п.

Для ведения достаточно подробных логов я добавил в программу класс-перехватчик, который пробегал по всем формам и всем обьектам, которые могут иметь события с обработчиками и подменял эти обработчики на свои. Когда обработчик перехватчика отработал - в конце он вызывал настоящий обработчик (который он заменил на свой). На основе собранных данных создавался csv файл, который загружался на SQL сервер, а далее шла аналитика - кто на какой форме на какую кнопку нажал, какая функция и с каким результатом отработала и т.п.

Вся прелесть в том, что достаточно добавить в проект класс и создать экземпляр перехватчика. Далее он работает сам.
Ничего переделывать программе не надо.

Информативность этого метода - почти как запустить программу в пошаговом режиме под отладчиком, но только на рабочем месте сотрудника. Некоторые баги в принципе не воспроизводятся на рабочем месте программиста.(поведение программы может сильно отличаться в зависимости от версии ОС, от настроек прав учетной записи пользователя, от весии драйверов, могут вмешиваться антивирусы и т.д. и .т.п.)

Кроме того, используя перехватчик можно менять поведение программы не внося изменений в исходники, если
в паре с классом перехватчика использовать класс плагинов, который мог внедрять свои обработчики до - после- или вместо
"штатных обработчиков".

Сейчас есть реализация  класса-перехватчика для C++ Builder, может быть кто нибудь заинтересуется и поможет перевести его на Delphi.

Для использования класса в любых проектах
удобно разместить файл так:  ...Include\Vcl\TDebugInterceptor.h
Далее в файл VCL0.H добавить строки: 

#ifndef __DBG_INTERCEPTOR__
#define __DBG_INTERCEPTOR__
#include <DebugInterceptor.h>
#endif

 

TDebugInterceptor.h ( это только пример для общего ознакомления, в проект подключать файл, скачанный и распакованный из архива )

'-------------------------------------------------------------------------'
#define debug_mode

#define di_file                 0
#define di_line                 1
#define di_func                 2
#define di_owner_class          3
#define di_owner_name           4
#define di_obj_class            5
#define di_obj_name             6
#define di_obj_field_type       7
#define di_obj_field_value      8
#define di_func_name            9
#define di_event_name           10
#define di_event_counter        11
#define di_info                 12
#define di_thread               13

#define dbg_fields_dbg String Log_fields[14];\
Log_fields[di_file]=String(__FILE__);\
Log_fields[di_func]=String(__FUNC__);\
Log_fields[di_line]=IntToStr(__LINE__);\
Log_fields[di_obj_name]="";\
Log_fields[di_obj_class]="";\
Log_fields[di_owner_name]="";\
Log_fields[di_owner_class]="";\
Log_fields[di_obj_field_type]="";\
Log_fields[di_obj_field_value]="";\
Log_fields[di_event_counter]="";\
Log_fields[di_event_name]="";\
Log_fields[di_func_name]="";\
Log_fields[di_info]="";\
Log_fields[di_thread]=IntToStr(_threadid);\

#define dbg_fields TDebugInterceptor_log_fields[di_file]=String(__FILE__);\
TDebugInterceptor_log_fields[di_func]=String(__FUNC__);\
TDebugInterceptor_log_fields[di_line]=IntToStr(__LINE__);\
TDebugInterceptor_log_fields[di_obj_name]="";\
TDebugInterceptor_log_fields[di_obj_class]="";\
TDebugInterceptor_log_fields[di_owner_name]="";\
TDebugInterceptor_log_fields[di_owner_class]="";\
TDebugInterceptor_log_fields[di_obj_field_type]="";\
TDebugInterceptor_log_fields[di_obj_field_value]="";\
TDebugInterceptor_log_fields[di_event_counter]="";\
TDebugInterceptor_log_fields[di_event_name]="";\
TDebugInterceptor_log_fields[di_func_name]="";\
TDebugInterceptor_log_fields[di_info]="";\
TDebugInterceptor_log_fields[di_thread]=IntToStr(_threadid);\

#define dbg_write  TDebugInterceptor_SafeWriteLog(TDebugInterceptor_log_fields); \

#define dbg_line_sql dbg_fields\
   \
TDebugInterceptor_log_fields[di_info]="dbg_line_sql";\
   \
   dbg_write\
   \

#define dbg_line_begin_trans dbg_fields\
   \
TDebugInterceptor_log_fields[di_info]="dbg_line_begin_trans";\
   \
   dbg_write\
   \

#define dbg_line_commit_trans dbg_fields\
   \
TDebugInterceptor_log_fields[di_info]="dbg_line_commit_trans";\
   \
   dbg_write\
   \

#define dbg_line_rollback_trans dbg_fields\
   \
TDebugInterceptor_log_fields[di_info]="dbg_line_rollback_trans";\
   \
   dbg_write\
   \

#define dbg_line_test dbg_fields\
   \
TDebugInterceptor_log_fields[di_info]="dbg_line_test";\
   \
   dbg_write\
   \

#define dbg_line_no_def TDebugInterceptor_log_fields[di_file]=String(__FILE__);\
TDebugInterceptor_log_fields[di_func]=String(__FUNC__);\
TDebugInterceptor_log_fields[di_line]=IntToStr(__LINE__);\
TDebugInterceptor_log_fields[di_obj_name]="";\
TDebugInterceptor_log_fields[di_obj_class]="";\
TDebugInterceptor_log_fields[di_owner_name]="";\
TDebugInterceptor_log_fields[di_owner_class]="";\
TDebugInterceptor_log_fields[di_obj_field_type]="";\
TDebugInterceptor_log_fields[di_obj_field_value]="";\
TDebugInterceptor_log_fields[di_event_counter]="";\
TDebugInterceptor_log_fields[di_event_name]="";\
TDebugInterceptor_log_fields[di_func_name]="";\
TDebugInterceptor_log_fields[di_info]="";\
TDebugInterceptor_log_fields[di_thread]=IntToStr(_threadid);\
TDebugInterceptor_SafeWriteLog(TDebugInterceptor_log_fields);\
//-------------------------------------------------------------------
#include <vcl.h>
#include <FileCtrl.hpp>
#include <stdio.h>
#include <ComCtrls.HPP>
#include <Buttons.HPP>
#include <Dialogs.HPP>
#include <ADODB.hpp>
#include <dbctrls.hpp>
#include <WINNT.H>

 void __fastcall TDebugInterceptor_RegisterObject(TObject* NewObj);
 void __fastcall TDebugInterceptor_SafeWriteLog(String TDebugInterceptor_log_fields[14]);

//------------------------------------------------------------------------------
class TDebugInterceptor : public TObject
{
private:
       bool Fwait_write;
protected:

public:
             __property bool wait_write = {read = Fwait_write} ;

             __fastcall int debug_file;
             __fastcall String CurrLogFileName;
             __fastcall TDebugInterceptor();
        void __fastcall CloseDebugInterceptor();
        void __fastcall CreateLog(String LogFileName);
        void __fastcall WriteLog(String TDebugInterceptor_log_fields[14]);
        void __fastcall CloseLog();
        void __fastcall RegisterNewForms();
      String __fastcall SingleLine(String InStr);
             __fastcall TStringList * MethodNamesList;
        void __fastcall HookFormClose(TObject *Sender,TCloseAction &Action);
        void __fastcall HookOnClick(TObject *Sender) ;
        void __fastcall HookOnExit(TObject *Sender) ;
        void __fastcall HookADOConnectionWillExecute(TADOConnection *c,WideString &q,TCursorType &,TADOLockType &,TCommandType &,TExecuteOptions &,TEventStatus &,const _di__Command Command,const _di__Recordset);
        void __fastcall HookADODataSetAfterOpen (TDataSet *DataSet);
        void __fastcall HookADOTableAfterOpen (TDataSet *DataSet);
        void __fastcall HookADOQueryAfterOpen (TDataSet *DataSet);

        void __fastcall HookOnMessage(tagMSG &Msg, bool &Handled);
        bool __fastcall HookMainWindow(TMessage &Message);
        void __fastcall HookOnActiveFormChange(TObject *Sender);

      String __fastcall HostForLog;

};

//------------------------------------------------------------------------------
 extern __declspec(selectany)TDebugInterceptor * STR_DEBUG=NULL;
                      String TDebugInterceptor_log_fields[14];
//------------------------------------------------------------------------------
__fastcall TDebugInterceptor::TDebugInterceptor()
{

String ComputerNameStr;
String UserNameStr;
String LogDir="";
MethodNamesList=new TStringList;
char TMPC[255];
unsigned long TMPCSZ=sizeof(TMPC);
//---UserNameStr-----------------------------------------
GetUserName(TMPC,&TMPCSZ)  ;
UserNameStr=String(TMPC);

char CNS[100];
unsigned long CN=sizeof(CNS);
//---ComputerNameStr-----------------------------------------
GetComputerName(CNS,&CN)  ;
ComputerNameStr=String(CNS);

LogDir=ExtractFileDir(Application->ExeName)+"\\DebugInterceptor\\"+DateToStr(Now());

 if (!DirectoryExists(LogDir))
  {
    if (!CreateDir(LogDir))
      {
      throw Exception(String(__FUNC__)+": Cannot create "+LogDir);
      }
  }

HostForLog=AnsiQuotedStr(ExtractFileName(Application->ExeName),'"')+";"+AnsiQuotedStr(UserNameStr,'"')+";"+AnsiQuotedStr(ComputerNameStr,'"');
CurrLogFileName=LogDir+"\\"+ExtractFileName(Application->ExeName)+"_"+UserNameStr+"@"+ComputerNameStr+"_"+DateToStr(Now())+"_"+IntToStr(_threadid)+".txt";

Fwait_write=true;
CreateLog(CurrLogFileName);

dbg_fields_dbg
WriteLog(Log_fields);

Screen->OnActiveFormChange=HookOnActiveFormChange;
Application->HookMainWindow(STR_DEBUG->HookMainWindow);
Application->OnMessage=STR_DEBUG->HookOnMessage;
}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::HookOnActiveFormChange(TObject *Sender)
{
// заготовки на будущее развите класса
}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::HookOnMessage(tagMSG &Msg, bool &Handled)
{
   // заготовки на будущее развите класса

Handled = false;

}
//------------------------------------------------------------------------------
bool __fastcall TDebugInterceptor::HookMainWindow(TMessage &Message)
{
// заготовки на будущее развите класса
return false;
}
//------------------------------------------------------------------------------
void __fastcall  TDebugInterceptor::HookFormClose(TObject *Sender,
      TCloseAction &Action)
{
 static         HookFormCloseCounter = 0;
                HookFormCloseCounter++;
 TCloseEvent    ffc;
 TMethod        Method;
 TForm*         FormTmp              = ((TForm*)Sender);
 String         FullObjectName=String(Sender->ClassName())+"::"+((TComponent*)Sender)->Name;
 String         FuncName=MethodNamesList->Values[FullObjectName];

dbg_fields_dbg
Log_fields[di_obj_name]=((TComponent*)Sender)->Name;
Log_fields[di_obj_class]=String(Sender->ClassName());
Log_fields[di_obj_field_type]="Caption";
Log_fields[di_obj_field_value]=FormTmp->Caption;
Log_fields[di_event_counter]=IntToStr(HookFormCloseCounter);
Log_fields[di_event_name]="Start";
Log_fields[di_func_name]=FuncName;
WriteLog(Log_fields);

 void* AddrProc = FormTmp->MethodAddress(FuncName);

     if (AddrProc!=NULL)
     {
     Method.Code=AddrProc;
     Method.Data=FormTmp;
     ffc = *(TCloseEvent*)&Method;
        try{
           ffc(Sender,Action);
           }catch(Exception &exception)
           {

                Log_fields[di_event_name]="End_ERROR";
                Log_fields[di_info]=exception.Message;
                WriteLog(Log_fields);

                throw Exception(exception) ;

           }
    }else
        {

                Log_fields[di_event_name]="End_NOT_FOUND_METHOD";
                WriteLog(Log_fields);
       
         return;
        }

                Log_fields[di_event_name]="End_OK";
                WriteLog(Log_fields);
     
}
//------------------------------------------------------------------------------
void __fastcall  TDebugInterceptor::HookOnExit(TObject *Sender)
{
TCursor last_cursor=Screen->Cursor;
Screen->Cursor=crHourGlass;

 static         HookOnExitCounter = 0;
                HookOnExitCounter++;
 TNotifyEvent   clc;
 TMethod        Method;
 String         SenderInfo="";
 String         FullObjectName=String(((TComponent*)Sender)->Owner->ClassName())+"::"+((TComponent*)Sender)->Owner->Name+"::"+String(Sender->ClassName())+"::"+((TComponent*)Sender)->Name;
 String         FuncName=MethodNamesList->Values[FullObjectName];              
 String         FieldType="";

 if(IsPublishedProp(Sender, "Caption"))
                        {
                        SenderInfo=GetStrProp(Sender, "Caption");
                        FieldType="Caption";
                        }
 if(IsPublishedProp(Sender, "Text"))
                        {
                        SenderInfo=SenderInfo+" "+GetStrProp(Sender, "Text");
                        FieldType="Text";
                        }

dbg_fields_dbg
Log_fields[di_obj_name]=((TComponent*)Sender)->Name;
Log_fields[di_obj_class]=String(Sender->ClassName());
Log_fields[di_owner_name]=((TComponent*)Sender)->Owner->Name;
Log_fields[di_owner_class]=((TComponent*)Sender)->Owner->ClassName();
Log_fields[di_obj_field_type]= FieldType;
Log_fields[di_obj_field_value]=SenderInfo;
Log_fields[di_event_counter]=IntToStr(HookOnExitCounter);
Log_fields[di_event_name]="Start";
Log_fields[di_func_name]=FuncName;
WriteLog(Log_fields);


   void* AddrProc = ((TComponent*)Sender)->Owner->MethodAddress(FuncName);
   if (AddrProc !=NULL)
        {
                        Method.Code=AddrProc;
                        Method.Data=((TForm*)((TComponent*)Sender)->Owner);
                        clc= *(TNotifyEvent*)&Method;
                try{
                   clc(Sender);
                   }catch(Exception &exception)
                   {
                Screen->Cursor=last_cursor;
                Log_fields[di_event_name]="End_ERROR";
                Log_fields[di_info]=exception.Message;
                WriteLog(Log_fields);
                throw Exception(exception) ;

                   }
        }
        else
        {
                Screen->Cursor=last_cursor;
                Log_fields[di_event_name]="End_NOT_FOUND_METHOD";
                WriteLog(Log_fields);

                return;
        }
                Log_fields[di_event_name]="End_OK";
                WriteLog(Log_fields);

                Screen->Cursor=last_cursor;
}
//------------------------------------------------------------------------------
void __fastcall  TDebugInterceptor::HookOnClick(TObject *Sender)
{
TCursor last_cursor=Screen->Cursor;
Screen->Cursor=crHourGlass;
bool LastEnableButton;

 static         HookOnClickCounter = 0;
                HookOnClickCounter++;
 TNotifyEvent   clc;
 TMethod        Method;
  String        SenderInfo="";
  String        FullObjectName=String(((TComponent*)Sender)->Owner->ClassName())+"::"+((TComponent*)Sender)->Owner->Name+"::"+String(Sender->ClassName())+"::"+((TComponent*)Sender)->Name;
  String        FuncName=MethodNamesList->Values[FullObjectName];
  String        FieldType="";

 if(IsPublishedProp(Sender, "Caption"))
                        {
                        SenderInfo=GetStrProp(Sender, "Caption");
                        FieldType="Caption";
                        }
 if(IsPublishedProp(Sender, "Text"))
                        {
                        SenderInfo=SenderInfo+" "+GetStrProp(Sender, "Text");
                        FieldType="Text";
                        }
if(IsPublishedProp(Sender, "Enabled") && String(Sender->ClassName()).Pos("Button")>0)
                        {
                        LastEnableButton=((TWinControl*)Sender)->Enabled;
                        ((TWinControl*)Sender)->Enabled=false;
                        }


void * AddrProc  = ((TComponent*)Sender)->Owner->MethodAddress(FuncName);


dbg_fields_dbg
Log_fields[di_obj_name]=((TComponent*)Sender)->Name;
Log_fields[di_obj_class]=String(Sender->ClassName());
Log_fields[di_owner_name]=((TComponent*)Sender)->Owner->Name;
Log_fields[di_owner_class]=((TComponent*)Sender)->Owner->ClassName();
Log_fields[di_obj_field_type]= FieldType;
Log_fields[di_obj_field_value]=SenderInfo;
Log_fields[di_event_counter]=IntToStr(HookOnClickCounter);
Log_fields[di_event_name]="Start";
Log_fields[di_func_name]=FuncName;
WriteLog(Log_fields);

   if (AddrProc !=NULL)
        {
                        Method.Code=AddrProc;
                        Method.Data=((TForm*)((TComponent*)Sender)->Owner);
                        clc= *(TNotifyEvent*)&Method;
                try{
                   clc(Sender);
                   }catch(Exception &exception)
                   {
                   Screen->Cursor=last_cursor;
                   if(IsPublishedProp(Sender, "Enabled")&& String(Sender->ClassName()).Pos("Button")>0) { ((TWinControl*)Sender)->Enabled=LastEnableButton; }

                   Log_fields[di_event_name]="End_ERROR";
                   Log_fields[di_info]=exception.Message;
                   WriteLog(Log_fields);
                   throw Exception(exception) ;
                   }
        }
        else
        {
        Screen->Cursor=last_cursor;
        if(IsPublishedProp(Sender, "Enabled")&& String(Sender->ClassName()).Pos("Button")>0) { ((TWinControl*)Sender)->Enabled=LastEnableButton; }

        Log_fields[di_event_name]="End_NOT_FOUND_METHOD";
        WriteLog(Log_fields);

        return;
        }

        Screen->Cursor=last_cursor;
        if(IsPublishedProp(Sender, "Enabled")&& String(Sender->ClassName()).Pos("Button")>0) { ((TWinControl*)Sender)->Enabled=LastEnableButton; }

        Log_fields[di_event_name]="End_OK";
        WriteLog(Log_fields);
}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::HookADOConnectionWillExecute(TADOConnection *c,WideString &q,TCursorType &,TADOLockType &,TCommandType &,TExecuteOptions &,TEventStatus &,const _di__Command Command,const _di__Recordset)
{
 static         HookADOConnectionWillExecuteCounter = 0;
                HookADOConnectionWillExecuteCounter ++;
 String         TmpQ=String(q);

dbg_fields_dbg
Log_fields[di_obj_name]=c->Name;
Log_fields[di_obj_class]=String(c->ClassName());
Log_fields[di_owner_name]=c->Owner->Name;
Log_fields[di_owner_class]=c->Owner->ClassName();
Log_fields[di_obj_field_type]= "SQL";
Log_fields[di_obj_field_value]=TmpQ;
Log_fields[di_event_counter]=IntToStr(HookADOConnectionWillExecuteCounter);
Log_fields[di_event_name]="WillExecute";
WriteLog(Log_fields);

}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::HookADODataSetAfterOpen (TDataSet *DataSet)
{
static         HookADODataSetAfterOpenCounter = 0;
               HookADODataSetAfterOpenCounter ++;

dbg_fields_dbg
Log_fields[di_obj_name]=DataSet->Name;
Log_fields[di_obj_class]=String(DataSet->ClassName());
Log_fields[di_owner_name]=DataSet->Owner->Name;
Log_fields[di_owner_class]=DataSet->Owner->ClassName();
Log_fields[di_obj_field_type]= "RecordCount";
Log_fields[di_obj_field_value]=IntToStr(DataSet->RecordCount);
Log_fields[di_event_counter]=IntToStr(HookADODataSetAfterOpenCounter);
Log_fields[di_event_name]="ADODataSetAfterOpen";
WriteLog(Log_fields);

}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::HookADOTableAfterOpen (TDataSet *DataSet)
{
static         HookADOTableAfterOpenCounter = 0;
               HookADOTableAfterOpenCounter ++;

dbg_fields_dbg
Log_fields[di_obj_name]=DataSet->Name;
Log_fields[di_obj_class]=String(DataSet->ClassName());
Log_fields[di_owner_name]=DataSet->Owner->Name;
Log_fields[di_owner_class]=DataSet->Owner->ClassName();
Log_fields[di_obj_field_type]= "RecordCount";
Log_fields[di_obj_field_value]=IntToStr(DataSet->RecordCount);
Log_fields[di_event_counter]=IntToStr(HookADOTableAfterOpenCounter);
Log_fields[di_event_name]="ADOTableAfterOpen";
WriteLog(Log_fields);

}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::HookADOQueryAfterOpen (TDataSet *DataSet)
{
static         HookADOQueryAfterOpenCounter = 0;
               HookADOQueryAfterOpenCounter ++;

dbg_fields_dbg
Log_fields[di_obj_name]=DataSet->Name;
Log_fields[di_obj_class]=String(DataSet->ClassName());
Log_fields[di_owner_name]=DataSet->Owner->Name;
Log_fields[di_owner_class]=DataSet->Owner->ClassName();
Log_fields[di_obj_field_type]= "RecordCount";
Log_fields[di_obj_field_value]=IntToStr(DataSet->RecordCount);
Log_fields[di_event_counter]=IntToStr(HookADOQueryAfterOpenCounter);
Log_fields[di_event_name]="ADOQueryAfterOpen";
WriteLog(Log_fields);

}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::RegisterNewForms()
{
String ObjClassName="";
String ObjName="";
for( int i = 0; i < Application->ComponentCount; i++ )
      {
      ObjClassName=Application->Components[i]->ClassParent()->ClassName();
     if (ObjClassName=="TForm")
        {
        ObjName=((TForm*)Application->Components[i])->Name;
        ObjClassName=Application->Components[i]->ClassName();
        }
        else
                {
                  if (ObjClassName=="TDataModule")
                        {
                        ObjName=((TDataModule*)Application->Components[i])->Name;
                        ObjClassName=Application->Components[i]->ClassName();
                        }
                }
        if (MethodNamesList->IndexOfName(ObjClassName+"::"+ObjName)<0)
                {
                TDebugInterceptor_RegisterObject(((TObject*) Application->Components[i]));
                }

      }
}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor_RegisterObject(TObject* NewObj)
{
static bool err_info =false;
static         RegisterObjectCounter = 0;
 if (STR_DEBUG==NULL)
 {
 if (!err_info)
        {
         #ifdef debug_mode_off_info
         ShowMessage("Класс TDebugInterceptor не инициализирован, обьект STR_DEBUG==NULL.");
         #endif 
        }
 return;
 }

if (NewObj==NULL)
{
 STR_DEBUG->RegisterNewForms();
 return;
}


dbg_fields_dbg
String ObjClassName=NewObj->ClassParent()->ClassName();
String  ComponentInfo="";
String  FieldType="";
void* AddrProc;
String FullObjName="";
TMethod M ;

bool registration=false;
if (ObjClassName=="TForm")
        {
                TForm * NewForm =(TForm*)NewObj;
                M = *(TMethod*)&(NewForm->OnClose);
                STR_DEBUG->MethodNamesList->Add(String(NewForm->ClassName())+"::"+NewForm->Name+"="+String(NewForm->MethodName(M.Code)));

                NewForm->OnClose=STR_DEBUG->HookFormClose;

RegisterObjectCounter++;

Log_fields[di_obj_name]=NewForm->Name;
Log_fields[di_obj_class]=ObjClassName;
Log_fields[di_owner_name]="";
Log_fields[di_owner_class]="";
Log_fields[di_obj_field_type]= "Caption";
Log_fields[di_obj_field_value]=NewForm->Caption;
Log_fields[di_event_counter]=IntToStr(RegisterObjectCounter);
Log_fields[di_event_name]="";
Log_fields[di_func_name]="";
Log_fields[di_info]="";

TDebugInterceptor_SafeWriteLog(Log_fields);

        for (int c=0; c<NewForm->ComponentCount; c++)
                  {

ComponentInfo="";
registration=false;

if(IsPublishedProp(NewForm->Components[c], "Caption"))
        {
        ComponentInfo=GetStrProp(NewForm->Components[c], "Caption");
        FieldType="Caption";
        }
if(IsPublishedProp(NewForm->Components[c], "Text"))
        {
        ComponentInfo=ComponentInfo+" "+GetStrProp(NewForm->Components[c], "Text");
        FieldType="Text";
        }

FullObjName=String(NewObj->ClassName())+"::"+((TComponent*)NewObj)->Name+"::"+String(NewForm->Components[c]->ClassName())+"::"+NewForm->Components[c]->Name;


                         if (String(NewForm->Components[c]->ClassName())==String("TButton"))
                                {
                                M = *(TMethod*)&(((TButton*)NewForm->Components[c])->OnClick);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TButton*)NewForm->Components[c])->OnClick=STR_DEBUG->HookOnClick;
                                registration=true;
                                }

                         if (String(NewForm->Components[c]->ClassName())==String("TMenuItem"))
                                {
                                M = *(TMethod*)&(((TMenuItem*)NewForm->Components[c])->OnClick);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TMenuItem*)NewForm->Components[c])->OnClick=STR_DEBUG->HookOnClick;
                                registration=true;
                                }

                         if (String(NewForm->Components[c]->ClassName())==String("TSpeedButton"))
                                {
                                M = *(TMethod*)&(((TSpeedButton*)NewForm->Components[c])->OnClick);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TSpeedButton*)NewForm->Components[c])->OnClick=STR_DEBUG->HookOnClick;
                                registration=true;
                                }

                         if (String(NewForm->Components[c]->ClassName())==String("TToolButton"))
                                {
                                M = *(TMethod*)&(((TToolButton*)NewForm->Components[c])->OnClick);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TToolButton *)NewForm->Components[c])->OnClick=STR_DEBUG->HookOnClick;
                                registration=true;
                                }

                         if (String(NewForm->Components[c]->ClassName())==String("TBitBtn"))
                                {
                                M = *(TMethod*)&(((TBitBtn*)NewForm->Components[c])->OnClick);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TBitBtn*)NewForm->Components[c])->OnClick=STR_DEBUG->HookOnClick;
                                registration=true;
                                }

                         if (String(NewForm->Components[c]->ClassName())==String("TCheckBox"))
                                {
                                M = *(TMethod*)&(((TCheckBox*)NewForm->Components[c])->OnClick);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TCheckBox*)NewForm->Components[c])->OnClick=STR_DEBUG->HookOnClick;
                                registration=true;
                                }

                         if (String(NewForm->Components[c]->ClassName())==String("TStaticText"))
                                {
                                M = *(TMethod*)&(((TStaticText*)NewForm->Components[c])->OnClick);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TStaticText*)NewForm->Components[c])->OnClick=STR_DEBUG->HookOnClick;
                                registration=true;
                                }

                          if (String(NewForm->Components[c]->ClassName())==String("TEdit"))
                                {
                                M = *(TMethod*)&(((TEdit*)NewForm->Components[c])->OnExit);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TEdit*)NewForm->Components[c])->OnExit=STR_DEBUG->HookOnExit;
                                registration=true;
                                }

                          if (String(NewForm->Components[c]->ClassName())==String("TDBLookupComboBox"))
                                {
                                M = *(TMethod*)&(((TDBLookupComboBox*)NewForm->Components[c])->OnExit);
                                STR_DEBUG->MethodNamesList->Add(FullObjName+"="+String(NewForm->MethodName(M.Code)));
                                ((TDBLookupComboBox*)NewForm->Components[c])->OnExit=STR_DEBUG->HookOnExit;
                                registration=true;
                                }

                        if (registration)
                                {


                                RegisterObjectCounter++;

                                Log_fields[di_obj_name]=NewForm->Components[c]->Name;
                                Log_fields[di_obj_class]=String(NewForm->Components[c]->ClassName());
                                Log_fields[di_owner_name]=((TComponent*)NewObj)->Name;
                                Log_fields[di_owner_class]=String(NewObj->ClassName());
                                Log_fields[di_obj_field_type]= FieldType;
                                Log_fields[di_obj_field_value]=ComponentInfo;
                                Log_fields[di_event_counter]=IntToStr(RegisterObjectCounter);
                                Log_fields[di_event_name]="";
                                Log_fields[di_func_name]="";
                                Log_fields[di_info]="";
                                TDebugInterceptor_SafeWriteLog(Log_fields);
                                }

                 }
        }
  if (ObjClassName=="TDataModule")
        {
         TDataModule * NewDM=(TDataModule*)NewObj;

         RegisterObjectCounter++;
         Log_fields[di_obj_name]=NewDM->Name;
         Log_fields[di_obj_class]=ObjClassName;
         Log_fields[di_owner_name]="";
         Log_fields[di_owner_class]="";
         Log_fields[di_obj_field_type]= "";
         Log_fields[di_obj_field_value]="";
         Log_fields[di_event_counter]=IntToStr(RegisterObjectCounter);
         Log_fields[di_event_name]="";
         Log_fields[di_func_name]="";
         Log_fields[di_info]="";
         TDebugInterceptor_SafeWriteLog(Log_fields);

         STR_DEBUG->MethodNamesList->Add(String(NewObj->ClassName())+"::"+((TComponent*)NewObj)->Name+"=");

          for (int c=0; c<NewDM->ComponentCount; c++)
                  {

                  if (String(NewDM->Components[c]->ClassName())==String("TADOConnection"))
                     {
M= *(TMethod*)&(((TADOConnection*)NewDM->Components[c])->OnWillExecute) ;
FullObjName=String(NewObj->ClassName())+"::"+((TComponent*)NewObj)->Name+"::"+String(NewDM->Components[c]->ClassName())+"::"+NewDM->Components[c]->Name;
STR_DEBUG->MethodNamesList->Add(FullObjName+"="+NewDM->MethodName(M.Code));
((TADOConnection*)NewDM->Components[c])->OnWillExecute=STR_DEBUG->HookADOConnectionWillExecute;
String TmpQ=String(((TADOConnection*)NewDM->Components[c])->ConnectionString);
//STR_DEBUG->WriteLog("0,"+String(__FUNC__)+","+FullObjName+","+TmpQ+", ");
         RegisterObjectCounter++;

         Log_fields[di_obj_name]=NewDM->Components[c]->Name;
         Log_fields[di_obj_class]=String(NewDM->Components[c]->ClassName());
         Log_fields[di_owner_name]=((TComponent*)NewObj)->Name;
         Log_fields[di_owner_class]=String(NewObj->ClassName());
         Log_fields[di_obj_field_type]= "ConnectionString";
         Log_fields[di_obj_field_value]=TmpQ;
         Log_fields[di_event_counter]=IntToStr(RegisterObjectCounter);
         Log_fields[di_event_name]="";
         Log_fields[di_func_name]="";
         Log_fields[di_info]="";
         TDebugInterceptor_SafeWriteLog(Log_fields);
                      }

                if (String(NewDM->Components[c]->ClassName())==String("TADODataSet"))
                     {
M= *(TMethod*)&(((TADODataSet*)NewDM->Components[c])->AfterOpen) ;
FullObjName=String(NewObj->ClassName())+"::"+((TComponent*)NewObj)->Name+"::"+String(NewDM->Components[c]->ClassName())+"::"+NewDM->Components[c]->Name;
STR_DEBUG->MethodNamesList->Add(FullObjName+"="+NewDM->MethodName(M.Code));
((TADODataSet*)NewDM->Components[c])->AfterOpen=STR_DEBUG->HookADODataSetAfterOpen;
         RegisterObjectCounter++;
         Log_fields[di_obj_name]=NewDM->Components[c]->Name;
         Log_fields[di_obj_class]=String(NewDM->Components[c]->ClassName());
         Log_fields[di_owner_name]=((TComponent*)NewObj)->Name;
         Log_fields[di_owner_class]=String(NewObj->ClassName());
         Log_fields[di_obj_field_type]= "";
         Log_fields[di_obj_field_value]="";
         Log_fields[di_event_counter]=IntToStr(RegisterObjectCounter);
         Log_fields[di_event_name]="";
         Log_fields[di_func_name]="";
         Log_fields[di_info]="";
         TDebugInterceptor_SafeWriteLog(Log_fields);
                     }
                if (String(NewDM->Components[c]->ClassName())==String("TADOTable"))
                     {
M= *(TMethod*)&(((TADOTable*)NewDM->Components[c])->AfterOpen) ;
FullObjName=String(NewObj->ClassName())+"::"+((TComponent*)NewObj)->Name+"::"+String(NewDM->Components[c]->ClassName())+"::"+NewDM->Components[c]->Name;
STR_DEBUG->MethodNamesList->Add(FullObjName+"="+NewDM->MethodName(M.Code));
((TADOTable*)NewDM->Components[c])->AfterOpen=STR_DEBUG->HookADOTableAfterOpen;
         RegisterObjectCounter++;
         Log_fields[di_obj_name]=NewDM->Components[c]->Name;
         Log_fields[di_obj_class]=String(NewDM->Components[c]->ClassName());
         Log_fields[di_owner_name]=((TComponent*)NewObj)->Name;
         Log_fields[di_owner_class]=String(NewObj->ClassName());
         Log_fields[di_obj_field_type]= "";
         Log_fields[di_obj_field_value]="";
         Log_fields[di_event_counter]=IntToStr(RegisterObjectCounter);
         Log_fields[di_event_name]="";
         Log_fields[di_func_name]="";
         Log_fields[di_info]="";
         TDebugInterceptor_SafeWriteLog(Log_fields);
                     }
                if (String(NewDM->Components[c]->ClassName())==String("TADOQuery"))
                     {
M= *(TMethod*)&(((TADOQuery*)NewDM->Components[c])->AfterOpen) ;
FullObjName=String(NewObj->ClassName())+"::"+((TComponent*)NewObj)->Name+"::"+String(NewDM->Components[c]->ClassName())+"::"+NewDM->Components[c]->Name;
STR_DEBUG->MethodNamesList->Add(FullObjName+"="+NewDM->MethodName(M.Code));
((TADOQuery*)NewDM->Components[c])->AfterOpen=STR_DEBUG->HookADOQueryAfterOpen;
         RegisterObjectCounter++;
         Log_fields[di_obj_name]=NewDM->Components[c]->Name;
         Log_fields[di_obj_class]=String(NewDM->Components[c]->ClassName());
         Log_fields[di_owner_name]=((TComponent*)NewObj)->Name;
         Log_fields[di_owner_class]=String(NewObj->ClassName());
         Log_fields[di_obj_field_type]= "";
         Log_fields[di_obj_field_value]="";
         Log_fields[di_event_counter]=IntToStr(RegisterObjectCounter);
         Log_fields[di_event_name]="";
         Log_fields[di_func_name]="";
         Log_fields[di_info]="";
         TDebugInterceptor_SafeWriteLog(Log_fields);
                     }
                  }
        }

}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::CreateLog(String LogFileName)
{
if (FileExists(LogFileName))
        {
        RenameFile(LogFileName,LogFileName+"_back.txt");
        }
        debug_file=FileCreate(LogFileName);


Fwait_write=false;
}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::WriteLog(String Log_fields[14])
{
Fwait_write=true;
String TmpLogStr="";
static rec_num=1;

 for (int i=0; i<14; i++)
 {
  TmpLogStr=TmpLogStr+";"+AnsiQuotedStr(SingleLine(Log_fields[i]),'"');
 }


MEMORYSTATUS ms;
GlobalMemoryStatus(&ms);

String TmpStr=AnsiQuotedStr(IntToStr(rec_num),'"')+";"+AnsiQuotedStr(FormatDateTime("yyyy-mm-dd",Now())+" "+TimeToStr(Now()),'"');
TmpStr=TmpStr+";"+HostForLog +";"+AnsiQuotedStr(FloatToStr(ms.dwMemoryLoad),'"')+TmpLogStr+"\r\n" ;

FileWrite(debug_file,TmpStr.c_str(),TmpStr.Length());

rec_num++;
Fwait_write=false;
}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::CloseLog()
{

dbg_fields_dbg
WriteLog(Log_fields);

Fwait_write=true;
FileClose(debug_file);

}

//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor::CloseDebugInterceptor()
{
Application->UnhookMainWindow(STR_DEBUG->HookMainWindow);
Screen->OnActiveFormChange=NULL;
Application->OnMessage=NULL;

if (MethodNamesList!=NULL)
        {
        MethodNamesList->Clear();
        delete MethodNamesList;
        MethodNamesList=NULL ;
        }

CloseLog();
return;
}
//------------------------------------------------------------------------------
void __fastcall TDebugInterceptor_SafeWriteLog(String Log_fields[14])
{
  if (STR_DEBUG==NULL)return;
  while (STR_DEBUG->wait_write)
  {
   Sleep(100);
   Application->ProcessMessages();
  }
 STR_DEBUG->WriteLog(Log_fields);
}

//------------------------------------------------------------------------------
String __fastcall TDebugInterceptor::SingleLine(String InStr)
{
 String RetStr=InStr.Trim();
 if (RetStr.Length()>0)
 {
 for (int i=1;i<RetStr.Length();i++)
        {
        if (RetStr[i]=='\r' || RetStr[i]=='\n' ||RetStr[i]=='\0')
                {
                RetStr[i]=' ';
                }
        }
  }
  return RetStr;
}

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


 

 

Инициализировать перехватчик так:

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) 
{               
 Application->Initialize();   
 #ifdef debug_mode
 STR_DEBUG=new  TDebugInterceptor();
 #endif             
   Application->CreateForm(__classid(TPasswordDlg), &PasswordDlg);                  
   Application->CreateForm(__classid(TConfirmForm), &ConfirmForm);                  
   Application->CreateForm(__classid(TSomeForm), &SomeForm);                  
   Application->CreateForm(__classid(TProgressForm), &ProgressForm);                  
   Application->Run();  
#ifdef debug_mode
STR_DEBUG->CloseDebugInterceptor();
delete STR_DEBUG;
#endif                   
return 0;
} 
 
 
 Не обязательно, но желательно сразу регистрировать все формы при их создании:
Перехватчик найдет форму сам, но с некоторой задержкой. Если это критично, то лучше вызвать явную регистрацию фоормы
	void __fastcall TPasswordDlg::FormCreate(TObject *Sender)
	{
	#ifdef debug_mode
	TDebugInterceptor_RegisterObject(Sender);
	#endif
	}


Разумеется, при необходимости, можно в любом месте программы напрямую обратиться к перехватчику и "попросить" его записать что-либо в лог.

 

 Из нерешенных проблем:
 - Неудачная "глобализация" TDebugInterceptor_log_fields порождает огромное кол-во "варнингов" такого типа: 

[Linker Warning] Public symbol '_TDebugInterceptor_log_fields' defined in both module
C:\PLUGINSAMPLE\PLUGINSAMPLE.OBJ and C:\PLUGINSAMPLE\PLUGINSAMPLEMAIN.OBJ

Хотя все работает и так, но  желательно переделать. 

 

 

Add comment