Page 1 of 2
DtaWin server exceptions
Posted: Mon Aug 26, 2019 4:39 pm
by Karl-Heinz
Hi Chris,
in the error.prg i see that you have added to the "Description :" etc. layout self:stacktrace, but the output is a bit confusing because in the resx file "ERROR_STACK" and "ERROR_CALLEDFROM" have the same value "called from". I think it´s better if the value of "ERROR_STACK" is "Stack" or maybe "Stacktrace"
Code: Select all
<data name="ERROR_STACK" xml:space="preserve">
<value>Called from</value>
</data>
<data name="ERROR_CALLEDFROM" xml:space="preserve">
<value>Called from</value>
Some remarks about how server errors are handled in Dta and DtaDlg windows: If a self:server error happens the exception doesn´t reach automatically a global handler, only if a server is stored in a var.
Code: Select all
Method EnforceError1() class dtawin
self:server:Fieldput ( #xxxxx , 123 ) // field doesn´t exist
Method EnforceError2() class dtawin
local o as dbserver
o := Dbserver { "foo" }
o:Fieldput ( #xxxxx , 123 ) // field doesn´t exist
method Error(e) class dtawin // this method is called if a self:server error happens
e:Throw() // forward the exception to the global handler
the two attached images show the exception results. Note: the LocalServer.png shows the file and line number where the error occurred.
Any chance that a self:server exception goes directly to a global handler ?
BTW. Are there beside "DBSERVER" and "WCERROR" exceptions some more VO specific exceptions that use the "Description :" etc. layout ?
regards
Karl-Heinz
DtaWin server exceptions
Posted: Tue Aug 27, 2019 6:42 am
by robert
Karl-Heinz,
These were my changes.
- ERROR_STACK should indeed have been StackTrace
- What do you mean with 'global error handler' ? Errors inside the framework will always be thrown. You need a try catch somewhere in your code to catch them. The only way to globally handle an error would be if the compiler would do something like this
for every line of code:
Code: Select all
do while true
try
// original source line
exit
catch e as Exception
// call errorblock code
local lResult := eval(cbErrorBlock, e) as LOGIC // this will either quit the app or return a logic
if lResult == TRUE // E_RETRY
Loop
else
// how to handle default values?
endif
end try
I personally think that will kill the performance of your apps and is not a good idea.
Robert
DtaWin server exceptions
Posted: Tue Aug 27, 2019 8:13 am
by Karl-Heinz
Hi Robert,
ErrorDialogTabFormatException is a subclass of the X# ErrorDialog class where i do the text formating. All the hardcopies i´ve posted the last days show the content of this dialog.
Is there anything wrong in this kind of exception handling ?
Code: Select all
[STAThread];
FUNCTION Start() AS INT
LOCAL oXApp AS XApp
LOCAL oDlg AS ErrorDialogTabFormatException
TRY
oXApp := XApp{}
oXApp:Start()
CATCH e AS Exception
oDlg := ErrorDialogTabFormatException { e }
oDlg:showDialog()
END TRY
RETURN 0
CLASS XApp INHERIT App
METHOD Start () CLASS XApp
....
END CLASS
regards
Karl-Heinz
DtaWin server exceptions
Posted: Tue Aug 27, 2019 8:41 am
by robert
Karl-Heinz,
This looks OK to me.
Is this what you call a 'global handler'. That should work.
The only 'problem' is that some of the VO SDK Classes do not call this but try to look for an error handler in a client and otherwise call the error handler registered with ErrorBlock().
For example the DbServer class does this:
Code: Select all
IF IsArray( aClients ) .AND. nClients != 0 .AND. IsMethod( aClients[1], #Error )
Send(aClients[1],#Error, oErr )
ELSE
lErrorProcessingSemaphor := FALSE
Eval( ErrorBlock( ), oErr )
ENDIF
If you want your error dialog to be called then you should set the ErrorBlock to something like this:
And add a function
Code: Select all
FUNCTION MyThrow(e as Exception)
THROW e
Robert
DtaWin server exceptions
Posted: Tue Aug 27, 2019 8:45 am
by wriedmann
Hi Karl-Heinz,
PMFJI: your error handler will work, if there is no other try/end try sequence.
Let me explain with a bit of pseudocode:
Code: Select all
function TestFunc()
DoSomething() // errors here will be catched by your handler
try
DoSomethingElse() // errors here will NOT be catched by your handler
end try
return nil
This is the big difference to VO where an error handler is set globally. In .NET the error handling is occurring all the time in the catch block of the try/end try sequence.
And begin/end sequence is translated to try/end try in X#.
I can see only one solution (and this solution is used internally in the WinForms classes, I think): the possibility to set a global error handler in the GUI and/or RDD classes. And then in relative classes ALL catch blocks must be changed to call this global error handler. That would be a major work, I think, but it could be the best compromise.
@Robert: what do you think about such a handler?
Wolfgang
DtaWin server exceptions
Posted: Wed Aug 28, 2019 6:53 am
by Karl-Heinz
Hi Robert
Thanks !
Yes, that´s what i call a "global handler" and the only problem i noticed so far is how to catch "self:server" errors correctly. Your advice how to handle this sounds promising, i´ll give it a try !
regards
Karl-Heinz
DtaWin server exceptions
Posted: Wed Aug 28, 2019 7:03 am
by Karl-Heinz
Hi Karl-Heinz,
PMFJI: your error handler will work, if there is no other try/end try sequence.
Hi Wolfgang,
Yes, that is what i expect. Let´s say i want to open a server not shared - though it´s already opened. The msginfo() Box popups and i see as expected the oscode 32.
Code: Select all
METHOD PshOpenAgain()
LOCAL o AS FooServer
LOCAL bLastErrorHandler AS USUAL
LOCAL oError AS USUAL
bLastErrorHandler := ErrorBlock( {|oErr| _Break(oErr)} )
BEGIN SEQUENCE
o := FooServer{ , FALSE }
RECOVER USING oError
msginfo ( "RECOVER" + crlf +;
"Description: " + oError:Description + crlf + ;
"OS code: " + AsString ( oError:Oscode ) )
END SEQUENCE
ErrorBlock(bLastErrorHandler)
RETURN SELF
regards
Karl-Heinz
DtaWin server exceptions
Posted: Wed Aug 28, 2019 7:43 am
by wriedmann
Hi Karl-Heinz,
currently in .NET/X# the errorhandler would be ignored.
And the recover block would only be executed if in code that is executed in the FooServer constructor no exception handling is defined.
In .NET all error handling is occurring locally.
Please see this pseudo code:
Code: Select all
function Func1() as void
try
func2()
catch oEx as Exception
? oEx:Message
end try
return
function func2() as void
.... // errors here will be catched in the calling function
try
.... // errors here will NOT be catched in the calling function because they are already handled
catch oEx as Exception
? oEx:Message
end try
return
The only possibility to catch the errors inside the the try/end try block of func2 also in the catch block of func1 would be to throw a new exception in the catch block of func2 and passing the old exception as InnerException to the new one.
Wolfgang
DtaWin server exceptions
Posted: Wed Aug 28, 2019 10:55 am
by Karl-Heinz
Hi Wolfgang,
maybe i misunderstand you. When i run the DoFirstCall(), the VO and X# behaviour is exactly the same. Depending on which '? "Test " + u' line in the DoNextCall() is activated, i see either "Recover I" or "Recover II" in the same order. Of course, when i already enforce an error in the DoFirstCall() is see either
the default VO-Error dialog , or in X# my exception dialog.
Code: Select all
FUNCTION DoFirstCall() AS VOID
LOCAL bLastErrorHandler AS USUAL
LOCAL oError AS USUAL
// LOCAL u := 12.34 AS USUAL
//
// ? "Test " + u // default VO-Error Dialog appears, or in X# my exception dialog
bLastErrorHandler := ErrorBlock( {|oErr| _Break(oErr)} )
BEGIN SEQUENCE
DoNextCall()
RECOVER USING oError
MsgInfo ( "RECOVER I" )
END SEQUENCE
ErrorBlock(bLastErrorHandler)
RETURN
FUNCTION DoNextCall() AS VOID
LOCAL bLastErrorHandler AS USUAL
LOCAL oError AS USUAL
LOCAL u := 12.34 AS USUAL
bLastErrorHandler := ErrorBlock( {|oErr| _Break(oErr)} )
// ? "Test " + u // "RECOVER1"
BEGIN SEQUENCE
? "Test " + u // "RECOVER2"
RECOVER USING oError
MsgInfo ( "RECOVER II" )
END SEQUENCE
ErrorBlock(bLastErrorHandler)
RETURN
i think we need a website blog like "X# Errorhandling to the max" , or similar
regards
Karl-Heinz
DtaWin server exceptions
Posted: Wed Aug 28, 2019 11:24 am
by wriedmann
Hi Karl,
When i run the DoFirstCall(), the VO and X# behaviour is exactly the same. Depending on which '? "Test " + u' line in the DoNextCall() is activated, i see either "Recover I" or "Recover II" in the same order. Of course, when i already enforce an error in the DoFirstCall() is see either the default VO-Error dialog , or in X# my exception dialog.
That is exactly what I meant....
But I have now made some tests, and I have seen that the development team has implemented the ErrorBlock() function, and when you do something like this:
Code: Select all
begin sequence
uValue := 12.45
System.Console.WriteLine( string( "Hello " + uValue ) )
recover
Eval( ErrorBlock() )
end sequence
effectively the ErrorBlock is executed, and so the VO class libraries seem to have already the central point for errorhandling.
Therefore if you write your own code with begin/end sequence or try/end try you can should use the same mechanism, and use the stored ErrorBlock.
So I have to revise what I've been thinking.....
I will try to write an article about it in the docs wiki: how errorhandling in migrated code can be handled.
What I'm interested in: how the code recover block in the begin/end sequence block is translated by the compiler.
Wolfgang