Solved

Global Exception Handler Solution

Posted on 2010-11-09
14
609 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
Best Practices: Disaster Recovery Testing

Besides backup, any IT division should have a disaster recovery plan. You will find a few tips below relating to the development of such a plan and to what issues one should pay special attention in the course of backup planning.

 
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
 

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

ScreenConnect 6.0 Free Trial

Explore all the enhancements in one game-changing release, ScreenConnect 6.0, based on partner feedback. New features include a redesigned UI, app configurations and chat acknowledgement to improve customer engagement!

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Simple Delphi Question 9 90
error 1.1 400 Bad request idhttp delphi 18 92
tidtcpserver connection lost handle 2 88
Breakpoint doesn't stop in my variable 3 28
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
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…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…

831 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