Solved

Global Exception Handler Solution

Posted on 2010-11-09
14
605 Views
Last Modified: 2012-06-27

I need a global exception handler solution for my application.

As an example: lets say i only have two forms (main form -frmMain, and data module form - frmDataMod


I need to be able to kick out of the entire application when an acception has occurred. Whether im in the main form, or in the datamod form.

Currently, when the datamod is created in the main source, if an exception happens (Example: Database not found), it throws the exception message, and doe not terminate the entire app, it loads the main form

I dont understand why?  Please help?

thanks


Currently, when an exception happens in the datamodule, it is handled, i get the raised exception,


{PROJECT SOURCE}





begin

 GlobalExcept := nil;

 GlobalExceptAddr := nil;



  //first check for license file

 gLicenseFound:=  FileExists(ChangeFileExt(Application.ExeName, '.lcs'));

 SL:= TStringList.Create;

 if gLicenseFound then

 begin

 //open license file and read in data



  try

   SL.LoadFromFile(ChangeFileExt(Application.ExeName, '.lcs'));

   Cipher:= TDCP_rc4.Create(nil);

   Cipher.InitStr('genie',TDCP_sha1);

   SL.CommaText:= Cipher.DecryptString(SL.CommaText);

   Cipher.Burn;

   Cipher.Free;

  except

  end; //try



  if SL.Count = 5 then

  begin

   gRegLast:= SL[0];

   gRegFirst:= SL[1];

   gRegEMail:=  SL[2];

   gRegDate:= SL[3];

   gRegDays:= SL[4];

  end //if SL.Count = 5 then

  else

  begin

   raise ECustomException.Create('Invalid License!');

   Application.Terminate;

  end; //else

 end; //if LicenseFound then

 //free list

 SL.Free;



 AppMsg := RegisterWindowMessage('Genie_Win_Message');

 Mutex := CreateMutex(nil, True, 'Genie_Mutex_Name');

 if (Mutex = 0) OR (GetLastError = ERROR_ALREADY_EXISTS) then

 begin

  SendMessage(HWND_BROADCAST, AppMsg, 0, 0);

  raise ECustomException.Create(('An Instance is already running!');

 end //if (Mutex = 0) OR (GetLastError = ERROR_ALREADY_EXISTS) then

 else

 begin

  

  try

   Application.Initialize;

   if gDBPath = '' then

   begin

    //prompt user for the location of database

      frmDBSettings:= TfrmDBSettings.Create(Application);

      if frmDBSettings.ShowModal = mrOK then

      begin

       WriteRegSettings;

      end; //if frmDBSettings.ShowModal = mrOK then

     end;

     frmDBSettings.free;

     frmSplash:= TfrmSplash.Create(Application);

     //fill splash screen form controls here

     frmSplash.Show;

     frmSplash.Update;

     Application.ProcessMessages;

     if gDBPath <> '' then

     begin

      Application.CreateForm(TfrmDataMod, frmDataMod);

      Application.CreateForm(TfrmMain, frmMain);

      Application.ProcessMessages;

      Sleep(3000);

      frmSplash.Close;

      frmSplash.Free;

      Application.Run;

     end; //if DBPath <> '' then

     finally

     if Assigned( GlobalExcept ) then

     begin

      raise GlobalExcept at GlobalExceptAddr;

     end; //if Assigned( GlobalExcept ) then

    end; //try

   end; //else

   if Mutex <> 0 then

    CloseHandle(Mutex);

end.



{COMMON UNIT SHARED WITH MAIN SOURCE AND TWO FORMS}



type

  

  ECustomException = Class(Exception);



var

 GlobalExcept: Exception;

 GlobalExceptAddr: Pointer;





{DATA MODULE FORM CREATION}



procedure TfrmDataMod.DataModuleCreate(Sender: TObject);

begin

 gAppPath:= ExtractFilePath(Application.ExeName);

 if gDBPath = '' then gDBPath:= ChangeFileExt(Application.ExeName, '.dat');

 if FileExists(gDBPath) then

 begin

  dbMain.ConnectionString:=  'Provider=Microsoft.Jet.OLEDB.4.0;Jet OLEDB:Database Password=TEST_DBA;' +

                             'Data Source=' + gDBPath + ';' +

                             'Persist Security Info=False';

 try

  dbMain.Connected:= True;

  if dbMain.Connected then

  begin

   //

  end; //if

 except

  raise ECustomException.Create('Error Opening Database!');

  Application.TErminate;

 end; //try

 end

 else

 begin

  raise ECustomException.Create(Database not found!');

  Application.Terminate;

 end;

end;

Open in new window

0
Comment
Question by:Looking_4_Answers
  • 5
  • 3
  • 3
  • +1
14 Comments
 

Author Comment

by:Looking_4_Answers
ID: 34093604
sorry , i forgot the exception handler

procedure TrmMain.ApplicationException(Sender: TObject; E: Exception);
begin
  if E is EAccessViolation then
  begin
    // Keep the exception object from being destroyed!
    AcquireExceptionObject;
    GlobalExcept := e;
    GlobalExceptAddr := ExceptAddr;
    Application.Terminate;
  end;
end;
0
 

Author Comment

by:Looking_4_Answers
ID: 34093817
i also tried moving the creation of the datamod up above the creation of the splash screen

 begin
  Application.Initialize;
  Application.CreateForm(TfrmDataMod, frmDataMod);
  frmSplash:= TfrmSplash.Create(Application);
  //fill splash screen form controls here
  frmSplash.Show;
  frmSplash.Update;
  Application.CreateForm(TfrmMain, frmMain);
  frmSplash.Close;
  frmSplash.Free;
  Application.Run;
 end;//else

 finally
 if Assigned( GlobalExcept ) then
 begin
  raise GlobalExcept at GlobalExceptAddr;
 end; //if Assigned( GlobalExcept ) then
 end; //try


still, if an exception occurs in the datamod form, the main form still loads after the exception message!
0
 
LVL 14

Expert Comment

by:systan
ID: 34093946
>>still, if an exception occurs in the datamod form, the main form still loads after the exception message!
>>I need to be able to kick out of the entire application when an acception has occurred. Whether im in the main form, or in the datamod form.

Ahm, try
//halt;
if Assigned( GlobalExcept ) then halt;


Sorry If I understood it wrong
0
 
LVL 32

Expert Comment

by:ewangoya
ID: 34094218
You have an exception handler in the main form while your error occurs even before the mainform is created

Some of your code needs to be moved to the main form especially the cases where you are raising the exceptions
0
 
LVL 25

Expert Comment

by:epasquier
ID: 34094226
when you raise an exception, the code jumps out immediately after to the next exception handler.
So :
try
...
 except
  raise ECustomException.Create('Error Opening Database!');
   Application.TErminate; // THAT CODE NEVER EXECUTES
 end;

do it in the other order
try
 ...
 except
   Application.Terminate;
   raise ECustomException.Create('Error Opening Database!');
end;
 
0
 

Author Comment

by:Looking_4_Answers
ID: 34094433
@ epasquier


so why do i even need the Application.Terminate anyways????

why doesn't the raise kick it to the  ApplicationException  which has the terminate

procedure TrmMain.ApplicationException(Sender: TObject; E: Exception);
begin
  if E is EAccessViolation then
  begin
    // Keep the exception object from being destroyed!
    AcquireExceptionObject;
    GlobalExcept := e;
    GlobalExceptAddr := ExceptAddr;
    Application.Terminate;
  end;
end;

i have one of these in each of the two forms

@ewangoya:

Please tell me what code needs to go where. I have the same code (ApplicationException) as i described above, in each form, and both forms use the commonunit



0
 

Author Comment

by:Looking_4_Answers
ID: 34094519
@ epasquier

after testing your suggestion to reverse, the result are that the application is terminated and there is no exception message raised.

In some cases, depending on the exception, I get the MS Windows window "GSM.exe has encountered a problem and needs to close.  We are sorry for the inconvenience." with the send or dont send buttons to report it to microsoft


0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

Author Comment

by:Looking_4_Answers
ID: 34094528
@systan:

where should i try this?

if Assigned( GlobalExcept ) then halt;
0
 
LVL 25

Expert Comment

by:epasquier
ID: 34094588
>> so why do i even need the Application.Terminate anyways????
well, to tell the application to terminate when it will be in the Application.Run method.
That has no other use, Terminate only signals Application to quit the main message loop when the WM_QUIT message will be treated (by setting FTerminate := True).

FYI , Run loop is mainly this
repeat
  HandleMessage
until Terminated;  // = FTerminate

To immediately stop the application, use Halt;
example :
instead of
   raise ECustomException.Create('Invalid License!');
   Application.Terminate;
put
   ShowMessage('Invalid License!');
   Halt;


>> why doesn't the raise kick it to the  ApplicationException  which has the terminate
because when the exception occurs, you haven't yet create the form. I don't know where you associated that event to Application.OnException
It is not enough to declare an exception handler, you also must set it in the application's event property

you also don't need one per form. I recommend you declare ONE handler in the first module you create, and set Application.OnException right in the creation of this module (TfrmDataMod)
0
 
LVL 32

Expert Comment

by:ewangoya
ID: 34094667
Move datamodule creation to after MainForm Creation

In your MainForm.OnShow, check for the license


procedure TMainForm.FormShow(Sender: TObject);
var
  SL: TStringList;
begin
 //first check for license file
 gLicenseFound:=  FileExists(ChangeFileExt(Application.ExeName, '.lcs'));
 SL:= TStringList.Create;
 try
   if gLicenseFound then
   begin
   //open license file and read in data

    try
     SL.LoadFromFile(ChangeFileExt(Application.ExeName, '.lcs'));
     Cipher:= TDCP_rc4.Create(nil);
     try
       Cipher.InitStr('genie',TDCP_sha1);
       SL.CommaText:= Cipher.DecryptString(SL.CommaText);
       Cipher.Burn;
     finally
       Cipher.Free;
    except
    end; //try

    if SL.Count = 5 then
    begin
      gRegLast:= SL[0];
      gRegFirst:= SL[1];
      gRegEMail:=  SL[2];
      gRegDate:= SL[3];
      gRegDays:= SL[4];
    end //if SL.Count = 5 then
    else
    begin
     //no need for exception
     MessageDlg('Invalid License!', mtError, [mbOk], 0);
     PostMessage(Handle, WM_CLOSE, 0, 0);
     Exit
    end; //else
   end; //if LicenseFound then
  finally
     SL.Free;
  end;
end;

in your datamodule create


procedure TfrmDataMod.DataModuleCreate(Sender: TObject);
  procedure PerformExit(const AMessage: string);
  begin
    MessageDlg(AMessage, mtError, [mbOK], 0);
    PostMessage(Application.MainForm.Handle, WM_CLOSE, 0, 0);
  end;
begin
  gAppPath:= ExtractFilePath(Application.ExeName);
  if gDBPath = '' then
    gDBPath:= ChangeFileExt(Application.ExeName, '.dat');
  if FileExists(gDBPath) then
  begin
    dbMain.ConnectionString:=  'Provider=Microsoft.Jet.OLEDB.4.0;Jet OLEDB:Database Password=TEST_DBA;' +
                             'Data Source=' + gDBPath + ';' +
                             'Persist Security Info=False';
  try
    dbMain.Connected:= True;
    if dbMain.Connected then
    begin
     //
    end; //if
  except
    //no need for exception
    PerformExit('Error Opening Database!');
    Exit;
  end; //try
  end
  else
  begin
    PerformExit('Database not found!');
    Exit;
  end;
end;
0
 
LVL 25

Accepted Solution

by:
epasquier earned 500 total points
ID: 34094769
> Move datamodule creation to after MainForm Creation
that is generally a bad idea. Datamodules are often regrouping resources needed for all the application. It is best created before all forms

for better clarity in your code, I would recommend putting everything you check at application start in the datamodule creation, just after setting the Application.onException handler, just to keep the main code in your project source minimal, AND standard (like what Delphi generates when you add autocreated forms in your project)

use of Halt is the quickest way to get away on application initialization. Just not recommended when you have loaded a lot of data /system resources that could use some cleaner exit (general advice : avoid if you have created forms)
0
 
LVL 14

Expert Comment

by:systan
ID: 34094884
finally
 if Assigned( GlobalExcept ) then
 begin
 halt;
 // raise GlobalExcept at GlobalExceptAddr;
 end; //if Assigned( GlobalExcept ) then
 end; //try
0
 
LVL 32

Expert Comment

by:ewangoya
ID: 34094979
@epasquire
Correct if the mainform uses resources from the datamodule then the mainform can not be created before the datamodule
0
 
LVL 14

Expert Comment

by:systan
ID: 34098110
Why use datamodule?, While you can asssign/create nil.

adoquery: tadoquery;
adocon: tadoconnection;
adotran: tadotransaction;
..
..
adoquery := tadoquery.create(nil);
adocon := tadocon.create(nil);
...
...
adoquery.connection := adocon;
adotran.open;
adoquery.transaction := adotran;
adoquery.sql.text := ;select * from table';
adoquery.open
...
..
just like that, incase your interested, i'll show you more.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
This is a video describing the growing solar energy use in Utah. This is a topic that greatly interests me and so I decided to produce a video about it.
This is a video that shows how the OnPage alerts system integrates into ConnectWise, how a trigger is set, how a page is sent via the trigger, and how the SENT, DELIVERED, READ & REPLIED receipts get entered into the internal tab of the ConnectWise …

929 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now