Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

Global Exception Handler Solution

Posted on 2010-11-09
14
Medium Priority
?
634 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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 32

Expert Comment

by:Ephraim Wangoya
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:Ephraim Wangoya
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 2000 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:Ephraim Wangoya
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

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Question has a verified solution.

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

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Screencast - Getting to Know the Pipeline
Suggested Courses

926 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