• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 660
  • Last Modified:

Global Exception Handler Solution


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
Looking_4_Answers
Asked:
Looking_4_Answers
  • 5
  • 3
  • 3
  • +1
1 Solution
 
Looking_4_AnswersAuthor Commented:
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
 
Looking_4_AnswersAuthor Commented:
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
 
systanCommented:
>>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
Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

 
Ephraim WangoyaCommented:
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
 
Emmanuel PASQUIERFreelance Project ManagerCommented:
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
 
Looking_4_AnswersAuthor Commented:
@ 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
 
Looking_4_AnswersAuthor Commented:
@ 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
 
Looking_4_AnswersAuthor Commented:
@systan:

where should i try this?

if Assigned( GlobalExcept ) then halt;
0
 
Emmanuel PASQUIERFreelance Project ManagerCommented:
>> 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
 
Ephraim WangoyaCommented:
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
 
Emmanuel PASQUIERFreelance Project ManagerCommented:
> 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
 
systanCommented:
finally
 if Assigned( GlobalExcept ) then
 begin
 halt;
 // raise GlobalExcept at GlobalExceptAddr;
 end; //if Assigned( GlobalExcept ) then
 end; //try
0
 
Ephraim WangoyaCommented:
@epasquire
Correct if the mainform uses resources from the datamodule then the mainform can not be created before the datamodule
0
 
systanCommented:
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
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Cloud Class® Course: CompTIA Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

  • 5
  • 3
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now