TryISO8601ToDate generates an exception - parsing

DateUtils.TryISO8601ToDate('8', Result) generates an exception. But it is Try !!! It's task is to try without throwing any exceptions.
The problem is not that Delphi IDE reacts on this exception. The problem is that the "Try" function produces such an exception instead of returning true/false.

I think you may need to adjust your IDE debugger options.
In the IDE go to Tools | Options and on the Language Exceptions tab below Debugger Options | Embarcadero Debuggers, uncheck the Notify on Language Exceptions box.
With that box checked, the debugger will stop execution in the IDE before your (or the RTL's) exception-handling executes. It pops up a dialof asking whether you want to break execution or continue. If you choose the latter, the exception handler in the code will then execute.
Update Tbh, with the updates to your q, I'm not at all clear what you are saying is the problem. Which part of the following code does not behave as you expect/wish and how?
procedure TForm1.TestISODates;
var
S : String;
DT : TDateTime;
begin
DT := Now;
S := DateToISO8601(DT, False);
Caption := S;
if TryISO8601ToDate(S, DT, False) then
Caption := 'OK';
S := 'banana';
if TryISO8601ToDate(S, DT, False) then
Caption := 'OK'
else
Caption := S + ' not ok';
end;

Related

Get exception text from FireDAC debugger notification

I'm trying to capture the debugger notification text, Firedac connection
//FDScript1.SQLScripts.Add.SQL.LoadFromFile('C:\SGI\Rodar_Apos_Atualizar_3.txt');
//FDScript1.ValidateAll;
//FDScript1.ExecuteAll;
//MSScript1.SQL.LoadFromFile('C:\SGI\Rodar_Apos_Atualizar_3.txt');
//MSScript1.Execute;
try
FDConnection1.Connected := True;
FDScript1.SQLScripts.Add.SQL.LoadFromFile('C:\SGI\Rodar_Apos_Atualizar_3.txt');
FDScript1.ExecuteAll;
FDScript1.ValidateAll;
except
//EMSSQLNativeException
on E: EMSSQLNativeException do
begin
//ShowMessage('Erro'+FDGUIxErrorDialog1.ErrorDialog.Caption);
//Memo1.Clear;
Memo1.Text := 'Erro de Sistema: '+#13#10+ E.Message;
end;
end;
It really would help if you would show us the scripts you are trying to execute,
or at least the script which does not execute correctly. In any case, your code is wrong
because the documentation
states
It is good practice to call the ValidateAll method before the ExecuteAll method.
Note the 'before'. Your ValidateAll is after ExecuteAll, not before. Both are Boolean
functions but you are not checking their results, which you should.
With some trivial experimenting I found that I can provoke a EMSSQLNativeException
using SqlServer 2014 with the code below:
procedure TForm2.Button1Click(Sender: TObject);
const
sScript = 'select m.*, d.* from master m join detail d on, m.masterid = d.masterid';
var
FDScript : TFDSqlScript;
begin
try
FDConnection1.Connected := True;
FDScript := FDScript1.SQLScripts.Add;
FDScript.SQL.Text := sScript;
//FDScript1.ExecuteAll;
//FDScript1.ValidateAll;
// FDScript1.ValidateAll then
//FDScript1.ExecuteAll;
FDQuery1.SQL.Text := sScript;
FDQuery1.Open();
except
//EMSSQLNativeException
on E: EMSSQLNativeException do
begin
//ShowMessage('Erro'+FDGUIxErrorDialog1.ErrorDialog.Caption);
//Memo1.Clear;
Memo1.Text := 'Erro de Sistema: '+#13#10+ E.Message;
end;
end;
end;
Note the blatantly wrong syntax in the Sql statement, namely the comma after the on.
When FDQuery1.Open is called, this exception is raised (and caught initially by the debugger)
---------------------------
Debugger Exception Notification
---------------------------
Project sqlerror.exe raised exception class EMSSQLNativeException with message '[FireDAC][Phys][ODBC][Microsoft][SQL Server Native Client 11.0][SQL Server]Incorrect syntax near 'd'.'.
---------------------------
Break Continue Help
---------------------------
When I click Continue, execution proceeds into your exception handler, exactly as #TomBrunberg described in a comment
and the exception's message text is inserted into Memo1.
So I cannot reproduce the behaviour you describe based on the information in your question. It
must be caused by something you are not telling us, possibly in code you have
not included in your q or some property setting of the components you are
using.
Hopefully, trying the code above, you will find the debugger behaving as I
have described and this may give you some clue as to why you are getting the
problem you've described. Note that it is very important that you try the code
above in a new project, not your existing one. The only property setting
you need to do before executing it is to set FDConnection1 so that it can connect
to your server.
FWIW, if I uncomment-out the lines
FDScript1.ValidateAll then
FDScript1.ExecuteAll;
they execute without complaint, and I get the exact same behaviour as i've described without them.

FireDAC TFDScript error trying to drop a non-existent table

I have a nasty error when executing a Firedac TFDScript error trying to drop a non-existent table:
Delphi Berlin 10.1 Upd 2
Database Firebird 2.5
It give an error when calling FDScript.ExecuteAll (it passes the FDScript.ValidateAll without any problem)
The code I am executing is as follows:
FDScript: TFDScript;
{...}
begin
FDScript.ScriptOptions.Reset;
FDScript.SQLScripts.Clear;
FDScript.SQLScriptFileName := '';
FDScript.ScriptOptions.CommandSeparator := ';';
FDScript.ScriptOptions.CommitEachNCommands := 1;
FDScript.ScriptOptions.DropNonexistObj := True; // seems to ignore this directive
FDConnection.StartTransaction;
try
FDScript.SQLScripts.Add.SQL.Add('drop table countries;');
FDScript.ValidateAll; // no errors here
ScriptStatus := GetEnumName(TypeInfo(TFDScriptStatus), Ord(FDScript.Status));
if FDScript.Status = ssFinishSuccess then begin
FDScript.ExecuteAll; // ERROR HERE! TABLE COUNTRIES DOES NOT EXIXTS
if FDScript.TotalErrors = 0 then begin
FDConnection.Commit;
end
else begin
FDConnection.Rollback;
end;
end
else begin
FDConnection.Rollback;
end;
except
FDConnection.Rollback;
raise;
end;
end;
Implementation seems to be correct. The engine checks if a raised exception is of ekObjNotExists kind, and if so and the DropNonexistObj property is enabled and command kind is DROP or ALTER, it logs to its console. Otherwise re-raises the caught exception.
The only explanation is then, that you are seeing an exception message dialog shown by debugger. These dialogs are displayed even for handled exceptions (so long you won't add them to an ignore list or turn this feature off, which you should not do).

How do I add the key binding Shift+Ctrl+H X to the Delphi IDE using the ToolsApi?

Adding a new ShortCut to the Delphi IDE is not too difficult because the Open Tools API provides a service for this. I am trying something apparently more complex: Add a Wordstar like additional ShortCut:
I want something to happen when the user presses
Shift+Ctrl+H followed by the single key X
where X should work regardless of the state of the Shift key.
This is my code:
procedure TGxKeyboardBinding.BindKeyboard(const BindingServices: IOTAKeyBindingServices);
const
DefaultKeyBindingsFlag = kfImplicitShift + kfImplicitModifier + kfImplicitKeypad;
var
GExpertsShortcut: Byte;
ShiftState: TShiftState;
FirstShortCut: TShortCut;
SecondShortCut: TShortCut;
begin
GExpertsShortcut := Ord('H');
ShiftState := [ssShift, ssCtrl];
FirstShortCut := ShortCut(GExpertsShortcut, ShiftState);
SecondShortCut := ShortCut(Ord('X'), []);
BindingServices.AddKeyBinding([FirstShortCut, SecondShortCut],
TwoKeyBindingHandler, nil,
DefaultKeyBindingsFlag, '', '');
end;
So, if I set ShiftState := [ssCtrl] pressing
Ctrl+H X
calls my TwoKeyBindingHandler method.
But with ShiftState := [ssShift, ssCtrl] pressing
Shift+Ctrl+H X
does nothing.
Oddly enough, when specifying ShiftState := [ssShift, ssCtrl] (which should only affect the first key) pressing
Shift+Ctrl+H Shift+X
calls my TwoKeyBindingHandler method, even though the second ShortCut is added without a modifier key.
Any idea? Is this maybe a known limitation/bug of the Delphi IDE/Open Tools API? Is there a known workaround?
I tried it in Delphi 2007 and Delphi 10 Seattle, no difference.
You should be able to do it using the GetKeyState function.
The program has two operations - Think of it as opening a drop down menu item. When ctr-shift-h is pressed your programme will need to flag that the 'Menu' is now open and that subsequent keypresses will either activate an option or close the 'menu' if an invalid key is presses.
function IsKeyDown(const VK: integer): boolean;
begin
IsKeyDown := GetKeyState(VK) and $8000 <> 0;
end;
procedure Form1.OnkeyDown(...)
begin
if Not H_MenuOpen then
if IsKeyDown(vk_Control) and IskeyDown(vk_Shift) and IsKeyDown(vk_H) then
begin
//Some Boolean in the form
H_MenuOpen:=True;
//Will probably need to invalidate some parameters here so that
//no control tries to process the key
exit;
end;
if H_MenuOpen then
begin
if key=vk_X then
begin
//x has been pressed
*Your code here*
//possibly invalidate some of the params again
exit;
end;
//Nothing valid
H_MenuOpen:=False;
end;
end;
OK, since apparently nobody has found an answer, here is what I ended up doing:
I had already planned to show a hint window listing all possible characters for the second key (actually that code was already working fine, using the approach suggested by Helen Fairgrieve in her answer to this question). Instead, I now register only a one-key shortcut:
BindingServices.AddKeyBinding([FirstShortCut],
TwoKeyBindingHandler, nil,
DefaultKeyBindingsFlag, '', '');
And in the TwoKeyBindingHandler method, I show a popup menu which contains those characters as the shortcuts. The IDE/VCL/Windows then handles the rest for me.
This is what it looks like:
It's not an answer to the actual question but it solves my problem. Sorry if you got here expecting something more.

User friendly exception error delphi

I have this method where i execute a sql statement and catch a error in a try except statement
AdoQuery := TAdoQuery.Create(self);
AdoQuery.connection := AdoConnection;
AdoQuery.SQL.Add(sqlStr);
AdoQuery.Prepared := true;
try
begin
AdoQuery.ExecSql;
AdoQuery.Active := false;
end;
except on e:eAdoError do
ShowMessage('Error while creating the table: ' + e.Message);
end;
I can catch the error like this and show it to the user but it's showing some useless info for the user. I Would like to show only the %msg part of the error, take a look at the pic:
I tought e.MEssage allow me to get only the %msg part but it give me the whole thing hardly understoodable by a random user. How do i get only the usefull info in this case
Table reftabtest.rPCE already exists
Thank you.
You can use the Errors property of the TADOConnection object, what you want is the Description member of the Error object.
In your case:
function ParseOBDCError(ErrorDescription : String) : String;
var
Ps : Integer;
Pattern : String;
begin
Pattern := '%msg:';
Ps := Pos(Pattern, ErrorDescription);
if Ps > 0 then
begin
Result := Copy(ErrorDescription, Ps+Length(Pattern)+1);
// if you want, you can clean out other parts like < and >
Result := StringReplace(Result, '<', , '', [rfReplaceAll]);
Result := StringReplace(Result, '>', , '', [rfReplaceAll]);
Result := Trim(Result);
end
else
Result := ErrorDescription;
end;
...
AdoQuery := TAdoQuery.Create(self);
AdoQuery.connection := AdoConnection;
AdoQuery.SQL.Add(sqlStr);
AdoQuery.Prepared := true;
try
AdoQuery.ExecSql;
AdoQuery.Active := false;
except on e : Exception do
begin
if AdoConnection.Errors.Count > 0 then
ShowMessageFmt('Error while creating the table: %s',
[ParseOBDCError(AdoConnection.Errors[0].Description)])
else
ShowMessageFmt('something went wrong here: %s', [e.Message]);
end;
end;
That message dialog wasn't shown by your ShowMessage() code.
First, the icon is wrong - that's not the ShowMessage icon.
Second, the text you added ('Error while creating the table: ') to the message is missing.
This means your exception swallower is not catching the exception, because it's not of the EADOError class. So what's happening is the application's default exception handler is showing the exception.
Before I explain how to fix it, I need to point out that your exception swallower is wrong (your's should not be misnamed an exception handler).
Because you're swallowing the exception: If another method calls yours it will incorrectly think you method succeeded, and possibly do something it shouldn't. You should never write code that makes an assumption that there isn't a significant call stack leading into your method.
Swallowing to show a message to the user doesn't help, because it hides the error from the rest of the program. Especially since, as you can see: there is already code in one place that tells the user about the error without hiding it from the rest of the program. (The problem you have is that you want a friendlier message.)
To fix it:
First find out what the actual exception class is, so you're able to catch the correct error.
Now you have a number of options, but the simplest is as follows:
First log the exception, preferably with call stack. You don't want to be stuck in the situation where the user gets a friendly message but you as developer lose critical information if you need to do some debugging.
To get the call stack you can consider third-party tools like Mad Except, Exceptional Magic, JCLDebug to name a few.
Now show the message to the user.
Finally call Abort. This raises an EAbort exception which by convention is a "silent exception". It tells the rest of the program that there was an error (so it doesn't do things assuming everything is fine). But by convention, any further exception handlers should not show another message to the user. This includes the default handler's dialog in your question.
If the default handler is incorrectly showing EAbort messages, then it should be fixed.

Why is except not catching this error?

I have a program that simulates dice rolls and compares them to values in a chart (set of String lists). I currently get the value from a TEdit. If the box is empty it raises a EConvertError that should be caught by my Try/Except statement, but it's not. Thoughts and advice? Code below, Delphi 7.
try
//Shooting
if ShootingRadio.Checked then
BS := StrToInt(Edit1.Text);
Randomize;
Roll := RandomRange(1,7);
Label3.Caption := IntToStr(Roll);
if (Roll < StrToInt(ShootingHitChart[BS-1])) then
begin
Label3.Caption := (IntToStr(Roll)+' Miss');
RichView1.AddTextNL((IntToStr(Roll)+' Miss'),7,0,1);
RichView1.Reformat;
end
else
begin
Label3.Caption := (IntToStr(Roll)+' Hit');
RichView1.AddTextNL((IntToStr(Roll)+' Hit'),6,0,1);
RichView1.Reformat;
end;
except
MessageBox(0,'No number entered.','Error',mb_OK);
end;
'Stop on Delphi exceptions' is checked in the debugger options. The exception is actually caught just fine, but the IDE stops when you get it. When you continue running, you will not see the exception, but your message instead. Out of the IDE it will run fine.
You can uncheck this option (I usually do). You can always re-check it when you need to debug some stubborn problem.

Resources