Recent

Author Topic: What is the correct way to use a Try/Except in a Windows console application  (Read 11780 times)

funk247

  • New Member
  • *
  • Posts: 33
I'm certain I've missed something elementary here but I've never used a Try/Except block outside of a GUI application before and I'm having problems getting it to work properly.

I've bashed a console script together that reads a list of sequential csv files in a directory and then links to a DB, creates a table and inserts the contents of the csv file, now I'm trying to handle errors in the csv files so that it fails gracefully rather than just dumping the contents of the stack to the console.

I've added a try/except/finally block to the actual insert statement itself in the hope of eventually creating a log detailing which inserts failed and in which file, however when my script reaches an incorrect entry it simply drops out with the following error.

Code: [Select]
failed to create win32 control, error: 1407 : Cannot find window class
Presumably the exception is intended to appear within a windows message box and the console application doesn't have access to these facilities, so how should I be handling exceptions and errors, I literally just want it to write a line in a file!

Code: [Select]
    try
      connect.ExecuteDirect(ins);
    finally
      Rows.Clear;

      Writeln(fileOut, ins);
      WriteLn('Inserting data... ' + IntToStr(i));
    end;

    except
      on E : Exception do
        WriteLn(fileError, E.ClassName + ' ' + tblNm + ' error raised, with message : ' + E.Message);
    end;

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1260
I think you're handling try/finally/end & try/except/end correctly, but I don't think you're showing us the correct code.

There's a try missing from your posted code, but that's trivial.

Quote
Presumably the exception is intended to appear within a windows message box and the console application doesn't have access to these facilities

I'm not sure that's a correct assessment of what's going on. 

Exactly which line is the exception raised on?    I'm guessing it's none of the ones you've posted... 
« Last Edit: September 29, 2014, 06:50:29 pm by Mike.Cornflake »
Lazarus Trunk/FPC Trunk on Windows [7, 10]
  Have you tried searching this forum or the wiki?:   http://wiki.lazarus.freepascal.org/Alternative_Main_Page
  BOOKS! (Free and otherwise): http://wiki.lazarus.freepascal.org/Pascal_and_Lazarus_Books_and_Magazines

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Just to make sure(crystal ball humming) you do have unchecked win32 gui application in project options/config and target?
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

snorkel

  • Hero Member
  • *****
  • Posts: 817
You use it the same way.  No difference.
I use try except and try finally all the time in console apps.
***Snorkel***
If I forget, I always use the latest stable 32bit version of Lazarus and FPC. At the time of this signature that is Laz 3.0RC2 and FPC 3.2.2
OS: Windows 10 64 bit

funk247

  • New Member
  • *
  • Posts: 33
I think you're handling try/finally/end & try/except/end correctly, but I don't think you're showing us the correct code.

There's a try missing from your posted code, but that's trivial.

Quote
Presumably the exception is intended to appear within a windows message box and the console application doesn't have access to these facilities

I'm not sure that's a correct assessment of what's going on. 

Exactly which line is the exception raised on?    I'm guessing it's none of the ones you've posted...

I'm not missing a try, the opening of the block is much farther up the code before I start construction of the insert statement.

Its hard to say on which line the exception occurs, the stack trace generated doesn't mention my aplication at all. I've attached a screengrab in case it's of some use. Lazarus debugger also doesn't tell me that a line of code failed, only that theres a mismatch between the data being inserted and the columns defined in the table.

Presumably (I know I'm making a lot of assumptions but in absence of actual granular data on whats failed I can do little else) its on the line where I call connect as that's the only place where I actually attempt to add the data or do something otherwise meaningful with it.

The exceptions I'm trying to handle mostly relate to column/data mismatches so could appear anywhere in the csv files.

win32Gui is unchecked in the project options, I tried checking and rebuilding, no change, same error. 2 secs, I'll dump all my code :D

funk247

  • New Member
  • *
  • Posts: 33
Full program code

Code: [Select]
program csv2sql;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, FileUtil, sysutils, Interfaces, Dialogs, RegExpr, strutils, sqldb, db, mysql55conn, windows
  { you can add units after this };

var
   path, fileN, fno : String;
   idx, idy, i : Integer;
   connect : TMySQL55Connection;
   trans : TSQLTransaction;

procedure CheckDuplicateStrings(StringL:TStringList);
var
  i,j,k:integer;

begin
  for i:=0 to StringL.Count-1 do
    for j:=i+1 to StringL.Count-1 do begin
      k := CompareText(StringL.Strings[i], StringL.Strings[j]);
      if k = 0 then
        // WriteLn('Duplicate string : "'+AnsiUpperCase(StringL.Strings[i])+ '" in line ' + IntToStr(i)+' and '+ IntToStr(j));
        StringL.Strings[j] := StringL.Strings[j] + 'a';
    end;
end;

procedure convertToSQL(filePath, fileName : String);
var
  TextLines, Headers, Rows :  TStringList;
  i, j, k, l :  Integer;
  cre, ins, tblNm : String;
  remChar: TRegExpr;
  fileOut, fileError : TextFile;

begin
  remChar := TRegExpr.Create;
  remChar.Expression := '[/]';
  // remChar.ModifierI := true;

  TextLines := TStringList.Create;
  Headers := TStringList.Create;
  Rows := TStringList.Create;

  TextLines.LoadFromFile(filePath + fileName);

  tblNm := Copy2Symb(fileName, '.');

  AssignFile(fileOut, '... some path ...');
  Rewrite(fileOut);  // creating the file

  AssignFile(fileError, '... some path ...');
  Rewrite(fileError);  // creating the file

  for i := 1 to TextLines.Count -1  do begin
    TextLines[i] := ReplaceStr(TextLines[i], '''', '|');
    TextLines[i] := ReplaceStr(TextLines[i], '"', '');
    TextLines[i] := ReplaceStr(TextLines[i], '\', '');
  end;

  // Cleanup Header row
  for i := 0 to 0  do begin
    if remChar.Exec(TextLines[i]) then
    begin
      // Remove expr chars
      TextLines[i] := ReplaceStr(TextLines[i], remChar.Match[i], '');
    end;
    // Replace Colons
    TextLines[i] := ReplaceStr(TextLines[i], ':', '');
    // Remove space chars
    TextLines[i] := ReplaceStr(TextLines[i], ' ', '');
    // Convert CAPS to lowercase
    TextLines[i] := LowerCase(TextLines[i]);
    // rem other extraneous bits
    TextLines[i] := ReplaceStr(TextLines[i], '\', '');
    TextLines[i] := ReplaceStr(TextLines[i], '''', '');
    // Pick out header elements
    ExtractStrings(['^'], [' '], PChar(TextLines[i]), Headers, true);
  end;

  // Rename duplicate strings
  CheckDuplicateStrings(Headers);

  // Append ^ to end of field rows
  for i := 1 to TextLines.Count-1  do begin
    TextLines[i] := TextLines[i] + '^';
  end;

  // Create Tbl
  cre := 'CREATE TABLE ' + tblNm + ' (';

  for i := 0 to Headers.Count -1 do begin
    cre := cre + '`' + Headers[i] + '`' + ' VARCHAR(255), ';
  end;

  // Trim trailing comma
  RemoveTrailingChars(cre, [',', ' ']);

  WriteLn('Creating Table...');
  cre := cre + ');';

  Writeln(fileOut, cre);
  connect.ExecuteDirect(cre);

  // Ins Statements
  for i := 1 to TextLines.Count-1 do begin
    try
    ExtractStrings(['^'], [' '], PChar(TextLines[i]), Rows, true);

    ins := 'INSERT INTO ' + tblNm + ' (';

    for j := 0 to Headers.Count-1 do begin
      ins := ins + '`' + Headers[j] + '`' + ',';
    end;

    RemoveTrailingChars(ins, [',', ' ']);

    ins := ins + ') VALUES (';

    for k := 0 to Rows.Count-1  do begin
      ins := ins + QuotedStr(Rows[k])  + ',';
    end;

    RemoveTrailingChars(ins, [',', ' ']);

    ins := ins + ');';

    // Re-insert apostrophes
    ins := ReplaceStr(ins, '|', '''''');

    // Do the insert

    try
      connect.ExecuteDirect(ins);
    finally
      Rows.Clear;

      Writeln(fileOut, ins);
      WriteLn('Inserting data... ' + IntToStr(i));
    end;

    except
      on E : Exception do
        WriteLn(fileError, E.ClassName + ' ' + tblNm + ' error raised, with message : ' + E.Message);
    end;
  end;
  CloseFile(fileOut);
  CloseFile(fileError);
end;

begin
  // DB Transaction
  trans := TSQLTransaction.Create(nil);

  // Connect to DB
  connect := TMySQL55Connection.Create(nil);
  connect.UserName := 'summat';
  connect.Password := 'summat';
  connect.DatabaseName := 'summat';
  connect.Port := 3306;
  connect.Connected := True;
  connect.Transaction := trans;

  idy := 206; // Number of files to process
  path := '... some path ...';

  WriteLn('Enter number of file to begin conversion from:');
  ReadLn(idx);

  try
    for i := idx to idy do begin
      fno := Format('%.3D', [i]);
      fileN := 'EXP0' + fno + '.TXT';
      WriteLn('Converting: ' + fileN);
      convertToSQL(path, fileN);
    end;
  finally
    connect.Connected := False;
    connect.Free;
    trans.Free
  end;
end.


ChrisF

  • Hero Member
  • *****
  • Posts: 542
Unit Dialogs in a console program is not really recommended...

As far as I can see you can remove these units:
FileUtil, Interfaces, Dialogs and Windows.

I've got a problem with your ExtractStrings function: the last 'true' parameter is is not accepted with the standard function. May be a different version in one of the units I've removed ?

Anyway, the try/except blocks are working properly here: I've got one when I'm trying to connect to the base (I've put the connection step in a such block).

**Edit** BTW, check also your dependencies. You've probably the LazUtils package, for instance (it can be removed for this test program). And may be some more (related or not to LCL).
« Last Edit: September 29, 2014, 08:42:32 pm by ChrisF »

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1260
I didn't say you were missing a try, i said you were "missing a try in the posted code".  Big difference :-)

Many thanks for posting the full code.

Firstly, exactly what ChrisF has said - remove all those LCL dependencies.

Your trace (which was extremely useful, thanks for posting) indicates the problems starts when Application.HandleException calls:

Code: [Select]
function PromptUser(const DialogMessage: String; DialogType: longint; Buttons: PLongint;
  ButtonCount, DefaultIndex, EscapeResult: Longint): Longint;
begin
  Result := WidgetSet.PromptUser('', DialogMessage, DialogType, Buttons, ButtonCount, DefaultIndex, EscapeResult);
end; 

Now, that's definitely in the LCL.  Try/Except are FPC constructs, so nothing to do with the LCL.  There *may* be something in the LCL intercepting the Exception and trying to show it as you suggested.  In which case, removing ALL references to the LCL in your uses clause should bring some sanity to what's going on :-)

Quote
Its hard to say on which line the exception occurs

Not true, there's several ways you can identify the exact line.  Off the top of my head:
*  Add debugln before suspect calls, then move the position of the debuglns to narrow in on the problem over the duration of several runs.
*  We know this line works...   WriteLn('Creating Table...');  Breakpoint it, then step over each line until you have the problem line. 

BreakPoint/Step Into/Step Over isn't always feasible if your code is complex, in which was the debugln will do nicely.  But for the code you have here, yeah - this is why Breakpoints were invented :-)

Good luck :-)
« Last Edit: September 29, 2014, 09:11:02 pm by Mike.Cornflake »
Lazarus Trunk/FPC Trunk on Windows [7, 10]
  Have you tried searching this forum or the wiki?:   http://wiki.lazarus.freepascal.org/Alternative_Main_Page
  BOOKS! (Free and otherwise): http://wiki.lazarus.freepascal.org/Pascal_and_Lazarus_Books_and_Magazines

Laksen

  • Hero Member
  • *****
  • Posts: 745
    • J-Software
Try to remove all the unnecessary units and dependencies. Your program doesn't use the LCL or Windows directly. Not sure why you include that?

The following compiles for me(but I don't have the dll's)
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, sysutils, RegExpr, strutils, sqldb, mysql55conn;

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Unit Dialogs in a console program is not really recommended...

As far as I can see you can remove these units:
FileUtil, Interfaces, Dialogs and Windows.
Agreed but FileUtil can be useful as it offers UTF8 replacements for FPC ANSI file functions and some other functionality.

FileUtil does require part of the LCL to be a project requirement - fortunately you can use LCLBase for that as it won't pull in graphical stuff.

See e.g.
http://wiki.lazarus.freepascal.org/copyfile
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: What is the correct way to use a Try/Except in a Windows console application
« Reply #10 on: September 30, 2014, 12:01:24 pm »
Agreed but FileUtil can be useful as it offers UTF8 replacements for FPC ANSI file functions and some other functionality.

I quite agree with you, concerning FileUtil.

But as I don't know exactly which LCL packages use graphical stuff or not, I've preferred to remove all of them for this test.

Furthermore, I'm getting an annoying issue with this package (i.e. LCLBase).

With these units (I've removed Interfaces to not included the LCL package) for this project:
Code: [Select]
Classes, FileUtil, sysutils, Dialogs, RegExpr, strutils, sqldb, db, mysql55conn, windows
and with LCLBase (only) in the dependencies of this project, the compilation locks at the linking step: no warning, no error, the IDE is just locked (tested on Windows XP 32 bits). Apparently the linker is not responding...

Adding LazUtils (only) instead in the dependencies is working properly.
« Last Edit: September 30, 2014, 12:08:31 pm by ChrisF »

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: What is the correct way to use a Try/Except in a Windows console application
« Reply #11 on: September 30, 2014, 02:03:02 pm »
Funny...

Am I the only one to get a "link lock" with this project (see project file attached) ?

KO on Windows XP 32 bits, last stable Lazarus version (Lazarus 1.2.4, FPC 2.6.4).

Code: [Select]
program project1;

uses Dialogs;

var MyString: string;

begin
  MyString:='test';
end.

with LCLBase in the dependencies.


BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: What is the correct way to use a Try/Except in a Windows console application
« Reply #12 on: September 30, 2014, 02:20:57 pm »
Laz trunk, fpc trunk x86, windows.

No problems with link lock (never seen that) but a lot of errors - don't know if that is expected or not:
Code: [Select]
Compile Project: Exit code 1, Errors: 49, Hints: 2
Hint: Start of reading config file C:\development\fpctrunk\bin\i386-win32\fpc.cfg
Hint: End of reading config file C:\development\fpctrunk\bin\i386-win32\fpc.cfg
Error: Undefined symbol: WSRegisterCustomImageList
Error: Undefined symbol: WSRegisterMenuItem
Error: Undefined symbol: WSRegisterMenu
Error: Undefined symbol: WSRegisterMainMenu
Error: Undefined symbol: WSRegisterPopupMenu
Error: Undefined symbol: WSRegisterDragImageList
Error: Undefined symbol: WSRegisterLazAccessibleObject
Error: Undefined symbol: WSRegisterControl
Error: Undefined symbol: WSRegisterWinControl
Error: Undefined symbol: WSRegisterGraphicControl
Error: Undefined symbol: WSRegisterCustomControl
Error: Undefined symbol: WSRegisterScrollingWinControl
Error: Undefined symbol: WSRegisterScrollBox
Error: Undefined symbol: WSRegisterCustomFrame
Error: Undefined symbol: WSRegisterCustomForm
Error: Undefined symbol: WSRegisterHintWindow
Error: Undefined symbol: WSRegisterCustomScrollBar
Error: Undefined symbol: WSRegisterCustomGroupBox
Error: Undefined symbol: WSRegisterCustomComboBox
Error: Undefined symbol: WSRegisterCustomListBox
Error: Undefined symbol: WSRegisterCustomEdit
Error: Undefined symbol: WSRegisterCustomMemo
Error: Undefined symbol: WSRegisterButtonControl
Error: Undefined symbol: WSRegisterCustomButton
Error: Undefined symbol: WSRegisterCustomCheckBox
Error: Undefined symbol: WSRegisterToggleBox
Error: Undefined symbol: WSRegisterRadioButton
Error: Undefined symbol: WSRegisterCustomStaticText
Error: Undefined symbol: WSRegisterCustomLabel
Error: Undefined symbol: WSRegisterCustomBitBtn
Error: Undefined symbol: WSRegisterCustomSpeedButton
Error: Undefined symbol: WSRegisterShape
Error: Undefined symbol: WSRegisterCustomSplitter
Error: Undefined symbol: WSRegisterPaintBox
Error: Undefined symbol: WSRegisterCustomImage
Error: Undefined symbol: WSRegisterBevel
Error: Undefined symbol: WSRegisterCustomRadioGroup
Error: Undefined symbol: WSRegisterCustomCheckGroup
Error: Undefined symbol: WSRegisterCustomLabeledEdit
Error: Undefined symbol: WSRegisterCustomPanel
Error: Undefined symbol: WSRegisterCustomTrayIcon
Error: Undefined symbol: WSRegisterCommonDialog
Error: Undefined symbol: WSRegisterFileDialog
Error: Undefined symbol: WSRegisterOpenDialog
Error: Undefined symbol: WSRegisterSaveDialog
Error: Undefined symbol: WSRegisterSelectDirectoryDialog
Error: Undefined symbol: WSRegisterColorDialog
Error: Undefined symbol: WSRegisterColorButton
Error: Undefined symbol: WSRegisterFontDialog
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: What is the correct way to use a Try/Except in a Windows console application
« Reply #13 on: September 30, 2014, 03:04:18 pm »
The errors you've got are "normal" IMHO: apparently Dialogs + LCLBase are not detected as non used here. But my feeling is that it's not really a bug (I should not use them in a console program, at least not Dialogs).

Anyway, this is why I get too, when not getting a "lock".

Have you added LCLBase in your project dependencies ? Or used the project I've uploaded ?

As far as I can say, this error occurred for me with:

-Dialogs unit in the uses clause,
-LCLBase in the dependencies,
-and finally a string in the program (don't ask me why).


BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: What is the correct way to use a Try/Except in a Windows console application
« Reply #14 on: September 30, 2014, 03:14:24 pm »
I used the project and didn't look why the errors occurred - in my best "monkey tests bug" mode ;)

Anyway, as the scenario you've painted isn't very useful.... who cares ;)
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

 

TinyPortal © 2005-2018