-
[RESOLVED] Get Calling Procedure On Error
I have one function that shows up in my error log quite a bit.
In "static" mode running in the IDE it does NOT error.
It errors Only in realtime.
The function is a common function so it is called by a number of procedures.
I'd like to isolate "which" calling procedure(s) may be the problem.
Anyway in compiled mode to ID which of the calling procedures is the problem?
Only thing I could think of was to REM the error handler in the common procedure and let it error from the calling.
Anyone have a better solution?
Thanks
David
-
Re: Get Calling Procedure On Error
What is the error being raised? And care to post the code of that problematic procedure? In order to determine the procedure that called that one then you can try implementing a stack trace wherein the procedure is logged/unlogged upon entry/exit.
-
Re: Get Calling Procedure On Error
dee-u
Thanks for taking your time to reply.
I get periodic #6 and #11 errors. But only shows up in the compiled version.
Believe I've Id it to when this function is called while scrolling.
FWIW here's the function.
vb Code:
Public Function GetYFromYValue(ByVal Index As Integer, ByVal YValue As Single) As Integer
'Returns the Y Pixel location
On Error GoTo Error_GetYFromYValue
Dim iReturn As Integer
Dim iYAxisHeight As Integer
Dim sngYMax As Single
Dim sngYMin As Single
Dim sngYNumInc As Single
'--------------
'*******
'STARTUP
'*******
iReturn = 0
'*******
'MAIN
'*******
With TChart(Index)
If .TSubGraph(giSubGraphNumber).Active Then
Select Case .TDataSet(giDataSetNumber).Scale
Case SCALE_YAXIS 'Use YAxis (Subgraph) Values for All DataSets
With .TSubGraph(giSubGraphNumber)
sngYMax = .YMax
sngYMin = .YMin
End With 'TSubGraph
Case SCALE_SCREEN 'Each DataSet is Independent
If .TDataSet(giDataSetNumber).FirstInSubGraph Then
With .TSubGraph(giSubGraphNumber)
sngYMax = .YMax
sngYMin = .YMin
End With 'TSubGraph
Else
With .TDataSet(giDataSetNumber)
sngYMax = .YMax
sngYMin = .YMin
End With 'TDataSet
End If
Case SCALE_USER
With .TDataSet(giDataSetNumber)
sngYMax = .YMax
sngYMin = .YMin
End With 'TSubGraph
Case SCALE_LOGY
With .TDataSet(giDataSetNumber)
sngYMax = Log(.YMax)
sngYMin = Log(.YMin)
End With 'TDataSet
Case SCALE_ENTIREDATASET
With .TDataSet(giDataSetNumber)
sngYMax = .YMax
sngYMin = .YMin
End With
End Select
'Calc Y Axis Height
iYAxisHeight = MCommon.GetYAxisHeight(Index)
'Stop any possible division by zero
If iYAxisHeight = 0 Then iYAxisHeight = 1
'Get Y Beginning Number and Y Price Increment
sngYNumInc = (sngYMax - sngYMin) / iYAxisHeight
iReturn = (sngYMax - YValue) / sngYNumInc
End If 'TSubGraph Active
End With 'TChart
'******
'WRAPUP
'******
GetYFromYValue = iReturn
Exit Function
Error_GetYFromYValue:
If Err.Number = 6 Then 'Overflow
With TError
.Type = ERR_CRITICAL
.Src = mstrModule & "GetYFromYValue"
.Action = LogOnly
.Data = "ErrNo: 6" & "-" & Err.Description
End With
ElseIf Err.Number = 11 Then 'Division by zero
With TError
.Type = ERR_CRITICAL
.Src = mstrModule & "GetYFromYValue"
.Action = LogOnly
.Data = "ErrNo: 11" & "-" & Err.Description
End With
Else
With TError
.Type = ERR_CRITICAL
.Src = mstrModule & "GetYFromYValue"
.Action = MsgAndLog
.Data = "ErrNo: " & str$(Err.Number) & "-" & Err.Description
End With
End If
Call DoError
End Function
-
Re: Get Calling Procedure On Error
You should start by finding out exactly where the errors are occurring.
To do that add ERL (error line) to your error message, and put numbers at the start of each line of code - the easy way to do that is with an Addin, such as MZTools (link in my signature) which can do it for all lines with just a click of a button.
-
Re: Get Calling Procedure On Error
si_the_geek
I believe line numbers only work in IDE -- Not Complied.
-- OR --
Am I mistaken?
-
Re: Get Calling Procedure On Error
You are mistaken - they work perfectly when compiled.
The only issue is putting them in, but with MZTools (like with all of the other useful things it does) that only takes a couple of seconds.
-
Re: Get Calling Procedure On Error
Thanks si.
Will give them a try.
-
Re: Get Calling Procedure On Error
Besides this the MZTools can be used to implement the stack trace of calling functions upon function enter/exit, the thing for which you started this thread. :)
-
Re: Get Calling Procedure On Error
Not sure if you will find this useful, but something I add to my apps when testing. Using MZTools to add line numbers and setting your error handler though..
It basicaly writes the details to a log file grabbing the procedure and line number, but also takes a screen shot when the error occured
Code:
' Module
Option Explicit
Public ErrRef As Long 'Random Ref No of Bitmap
Declare Sub keybd_event Lib "user32" _
(ByVal bVk As Byte, ByVal bScan As Byte, _
ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Public Function erCapture()
' Clear the Clipboard so we dont save the wrong image
Clipboard.Clear
' send a print screen button keypress event
' and DoEvents to allow windows time to process
' the event and capture the image to the clipboard
keybd_event vbKeySnapshot, 0, 0, 0
DoEvents
' send a print screen button up event
keybd_event vbKeySnapshot, 0, &H2, 0
DoEvents
' save the image to a file using the application path
SavePicture Clipboard.GetData(vbCFBitmap), App.Path & "\" & ErrRef & ".bmp"
End Function
Public Function epErrorHandler(ErrorNumber As String, ErrorLine As Integer, ErrorDescription As String, ErrorProcedure As String, ErrorForm As String, ApplicationPath As String)
Dim ErrMsg As String ' error message for log file
' produce random reference number for bitmap
Randomize
ErrRef = (Rnd * 9999)
' Capture the Screen
erCapture
' Add details to the ErrMsg for error log
ErrMsg = " Error: " & ErrorNumber & " (" & ErrorDescription & _
") Has occured at line: " & ErrorLine & " In Procedure: " _
& ErrorProcedure & " of form: " & ErrorForm & " Reference: " _
& ErrRef
' Send Error Message to EU
MsgBox "Sorry but error: " & ErrorNumber & _
" (" & ErrorDescription & ") Has occured at line: " & _
ErrorLine & " In Procedure: " & ErrorProcedure & _
" of form: " & ErrorForm
'Save the log file
Open ApplicationPath & "\err.log" For Append As #1
Print #1, Now & ErrMsg
Close #1
End Function
' Form (to create the error and see the error handler
Private Sub Command1_Click()
Dim s As Integer
On Error GoTo Command1_Click_Error
s = "some text not number"
On Error GoTo 0
Exit Sub
Command1_Click_Error:
epErrorHandler Err.Number, Erl, _
Err.Description, "Command1_Click", _
"Form1", App.Path
End Sub
Hope this helps
-
Re: Get Calling Procedure On Error
Quote:
Originally Posted by Pradeep1210
Besides this the MZTools can be used to implement the stack trace of calling functions upon function enter/exit, the thing for which you started this thread. :)
It certainly can be, if you add the code to call it to your error handling template (it allows you to use placeholders like {RoutineName}, which get auto-filled when they are placed).
The benefits wouldn't be as good as for a new program (where you could add the error handler at the same time), but it would still save effort.
The only issue is that if you use Exit Sub etc, you need to add a call to the tracer before that.
-
Re: Get Calling Procedure On Error
Quote:
Originally Posted by
pr1ngl3
Not sure if you will find this useful, but something I add to my apps when testing. Using MZTools to add line numbers and setting your error handler though..
It basicaly writes the details to a log file grabbing the procedure and line number, but also takes a screen shot when the error occured
...
That's a good idea, and is similar to what I do (for all finished code, not just in testing, but it seems that dw85745 is already doing something like it, albeit in a slightly less efficient way (by using a Type rather than parameters).
Note that there is no need to pass Err or Erl to the error handling routine, as they will still be set - you can just detect them at the start of the error handler routine.
Oh, and I added [Code] (code here) [/code] tags to your post, to make it easier to read.
-
Re: Get Calling Procedure On Error
Thanks Si,
I see what the 'code' option is for now (damn newbies :) )
incidentally, (for others viewing this thread) the reason I pass Erl to the error handling routine and not attemp to capture it is, I suffered problems when I first put this module together caused by the ERROR LINE NUMBER (Erl) within the routine.
(idleness... Blame the compiler, not the coder)
-
Re: Get Calling Procedure On Error
I've had problems like that too, but found that it was due to errors (or other things which reset Err) within the error handler routine itself - so they were solved by storing the apt info to variables at the start of that routine.
-
Re: Get Calling Procedure On Error
Thanks all for responses.
Si:
[
Quote:
That's a good idea, and is similar to what I do (for all finished code, not just in testing, but it seems that dw85745 is already doing something like it, albeit in a slightly less efficient way (by using a Type rather than parameters).
1) I opted for Type as it gives me more flexibility. I may or may not use certain of the Type variables depending on the procedure. If I go with Params it would be quite a long list -- or-- require Optional Params and I don't use variants.
2) Wrote a quick program and your correct line numbers worked in a compiled App. Just downloaded MZTool again as the one I installed several years back (and rarely use) didn't generate any line numbers.
============
pr1ngl3
Interesting idea to use a screen capture. Won't help my case but will keep info for future reference.
==============
Pradeep1210
Will check out MZTools for Stack Trace. Hope it will work compiled.
==============
My App runs 24/7 and out of this it is just generating about 20 errors a day and only this function.
which is called every few seconds. So obviously some odd condition since Error 6 and Error 11, which are both trapped.
-
Re: Get Calling Procedure On Error
This has been my Stack Trace implementation and here's a sample usage.
Code:
Private Sub Command1_Click()
On Error GoTo HandleError
PushStack "Form1.Command1_Click"
MsgBox "x"
PopStack
Exit Sub
HandleError:
Select Case MsgBox("Form1.Command1_Click", vbCritical + vbAbortRetryIgnore, "Error")
Case vbAbort
PopStack
Exit Sub
Case vbRetry
Resume
Case vbIgnore
Resume Next
End Select
End Sub
The SkipTrace variable is a public one, when I am in my error handling routine then I set it to true so that there will be no stack tracing of the different procedures used in my common error handling routine.
-
Re: Get Calling Procedure On Error
Quote:
Originally Posted by
dw85745
1) I opted for Type as it gives me more flexibility. I may or may not use certain of the Type variables depending on the procedure. If I go with Params it would be quite a long list -- or-- require Optional Params and I don't use variants.
Optional params do not need to be (and usually should not be) Variant - you can use a proper data type, and have a default value, eg:
Code:
Sub Example(p_strCaller as String,
Optional p_Type as YourEnum = ERR_CRITICAL,
Optional p_strErrorDescription as String = "")
Dim strDescription as String
If p_strErrorDescription = "" Then
strDescription = Err.Description
Else
strDescription = p_strErrorDescription
End If
...
Even without that kind of thing, your current error handler can be made quite a bit shorter by eliminating the duplication, eg:
Code:
Error_GetYFromYValue:
With TError
.Type = ERR_CRITICAL
.Src = mstrModule & "GetYFromYValue"
.Data = "ErrNo: " & str$(Err.Number) & "-" & Err.Description
Select Case Err.Number
Case 6, 11 'Overflow 'Division by zero
.Action = LogOnly
Case Else
.Action = MsgAndLog
End Select
End With
Call DoError
Quote:
Originally Posted by dw85745
Will check out MZTools for Stack Trace. Hope it will work compiled.
It isn't something that is built-in to MZTools, but it simplifies the process of putting it in (by altering the Error Handler template as apt, then clicking the button for each sub). As you already have error handlers, the bonus you get isn't as good as it would be for new routines.
In order to have a stack trace, you need to use something like dee-u posted - and it will work when compiled.
-
Re: Get Calling Procedure On Error
dee-u
Thanks for the example
===============
si:
Still using VB5, but will check out optional as other than variant. VB5 docs only indicate variant
Quote:
Re: VB5 IsMissing Help
Returns a Boolean value indicating whether an optional Variant argument has been passed to a procedure.
-----------
Re: Error Handler
Thanks -- had already caught it and our code is identical. Checked all error handlers in program, and "believe it or not -- found only 1 other that needed work. Also reworked the posted procedure.
-----------
Just for kicks, kept the "On Error" call in the posted procedure but commented out the actual error handler code to see if it would error in the calling procedure(s). As of today -- no errors logged from ANY of the calling procedures. Go Figure!!
-
Re: Get Calling Procedure On Error
Quote:
Re: VB5 IsMissing Help
Returns a Boolean value indicating whether an optional Variant argument has been passed to a procedure.
That's for the IsMissing function. The IsMissing function can only tell about variant types whether they were actually passed to the procedure or not. For other datatypes the IsMissing function does not return correct result (returns always false).
-
Re: Get Calling Procedure On Error
Quote:
Originally Posted by
dw85745
Just for kicks, kept the "On Error" call in the posted procedure but commented out the actual error handler code to see if it would error in the calling procedure(s). As of today -- no errors logged from ANY of the calling procedures. Go Figure!!
The way to go in cases like this is using Error Handlers in the calling procedure(s) and logging the Calling Procedure name there. For doing this, you need to Raise the Error from the posted Function, like this:
Code:
'...
Exit Function
Error_GetYFromYValue:
err.Raise err.Number , err.Source , err.Description
End Function
Another way (if you don't want to change/add so much code) would be sending the calling procedure name as String parameter to this Function and using it in the Function's Error Handler.
EDIT: About raising the Error, actually you don't even need to raise the error manually, removing the On Error and the Handler would automatically Raise the Error to the calling procedure, but you need to catch it there. The manual raising is specially useful when you want to change something in the Error object, like sending a customized err.Description.
-
Re: Get Calling Procedure On Error
=============
Pradeep120
I recognize IsMissing tests only variants. This is the issue I've always had with "Optional" (may be my confusion) in that - if other - than a variant type parameter is passed as "Optional", how you go about testing for whether it exists (the parameter was passed) or not. If I read Si correctly, he indicated to just pass a default value -- which is a solution -- but my understanding regarding the purpose of "Optional" is to reduce the parameter list being passed.
==================
jcis:
Based on my limited testing prior to my post (see code below), the error returns to the calling procedure even with On Error in the called procedure. That's why I'm a little confused as error in called procedure with error handler, yet no error shows in any of the calling procedures when the called procedure error handler is commented out.
NOTE: The above statement to jcis is INCORRECT. Exit Sub was originally omitted from the sample code calling procedure. It has now been added.
Code:
Option Explicit
Private Sub Form_Load()
On Error GoTo Form_Load_Error
Dim Msg As String
' Dim i As Integer
' Dim k As Integer
' k = i / 0
Call Test
Exit Sub
Form_Load_Error:
Msg = "Error # " & Str(Err.Number) & " was generated by " _
& Err.Source & Chr(13) & Err.Description
MsgBox Msg
End Sub
Private Sub Test()
On Error GoTo Test_Error
Dim i As Integer
Dim k As Integer
k = i / 0
Test_Error:
End Sub
-
Re: Get Calling Procedure On Error
It should be
Code:
Option Explicit
Private Sub Form_Load()
On Error GoTo Form_Load_Error
Dim Msg As String
Call Test
Form_Load_Error:
Msg = "Error # " & Str(Err.Number) & " was generated by " _
& Err.Source & Chr(13) & Err.Description & _
" Error on line " & Erl
MsgBox Msg
End Sub
Private Sub Test()
Dim i As Integer
Dim k As Integer
k = i / 0
End Sub
-
Re: Get Calling Procedure On Error
Quote:
Originally Posted by
dw85745
I recognize IsMissing tests only variants. This is the issue I've always had with "Optional" (may be my confusion) in that - if other - than a variant type parameter is passed as "Optional", how you go about testing for whether it exists (the parameter was passed) or not. If I read Si correctly, he indicated to just pass a default value -- which is a solution -- but my understanding regarding the purpose of "Optional" is to reduce the parameter list being passed.
You do reduce the parameter list, if you pick apt defaults then most things won't need to be passed.
With default values, you can (if needed) check against the default value in each case... but in many cases you don't need to bother.
With my previous example, you would normally call it like this:
Code:
Call Example("Command1_Click")
..and if you want a specific description, like this:
Code:
Call Example("Command1_Click", , "oops!")
The code inside the 'Example' routine checks/uses the parameters as apt - in the case of p_strErrorDescription, the code I showed will use the value only if specified (otherwise it will use Err.Description).
For the p_Type parameter, you don't need to do any more checking than your original version.
To replicate your Type, you could change DoError so that the parameters (and checking for defaults) are like this:
Code:
Sub DoError(p_Source as String, _
Optional p_Action as yourActionENum = MsgAndLog, _
Optional p_Type as yourTypeEnum = ERR_CRITICAL, _
Optional p_Data as String = "")
Dim strData as String
If p_Data ="" Then
strData = "ErrNo: " & str$(Err.Number) & "-" & Err.Description
Else
strData = p_Data
End If
(to make the usage more obvious, you could change both instances of "" to something like "<err num and description>")
For the error handler you had earlier, the code could now be reduced to this:
Code:
Error_GetYFromYValue:
Select Case Err.Number
Case 6, 11 'Overflow 'Division by zero
Call DoError(mstrModule & "GetYFromYValue", LogOnly)
Case Else
Call DoError(mstrModule & "GetYFromYValue")
End Select
-
Re: Get Calling Procedure On Error
dee-u
Regarding your response
[QUOTE]
I omitted an "Exit Sub" in front of the Error Handler in the calling procedure.
With "On Error" in the called procedure, if that procedure Errors, the error does NOT go
back up the Stack to the calling procedure. However if "On Error" is omitted in
the called procedure, then the error will show in calling procedure. Works as designed.
Posted code corrected.
I was referring to my actual program NOT the example
==================
Si:
Regarding:
Code:
Sub DoError(p_Source as String, _
Optional p_Action as yourActionENum = MsgAndLog, _
Optional p_Type as yourTypeEnum = ERR_CRITICAL, _
Optional p_Data as String = "")
Never seen this before with "Optionals". Interesting concept. Will definitely keep for future reference.
THANKS.
================
Thanks to everyone for their input.
Will consider this thread closed. One confusing question is why I get an Error Code 11 (Division by 0) when I specifically screen for it in the procedure (see below).
Will pursue independently and post if resolution.
Code:
'Stop any possible division by zero
If iYAxisHeight = 0 Then iYAxisHeight = 1
'Get Y Beginning Number and Y Price Increment
sngYNumInc = (sngYMax - sngYMin) / iYAxisHeight
-
Re: [RESOLVED] Get Calling Procedure On Error
That isn't the only division you do - the very next line does it too:
Code:
sngYNumInc = (sngYMax - sngYMin) / iYAxisHeight
iReturn = (sngYMax - YValue) / sngYNumInc
..so when sngYMax and sngYMin are equal (or very close to it), sngYNumInc will be 0 and cause the error on the next line.
This is one of those situations where line numbers should make corrections much easier.
-
Re: [RESOLVED] Get Calling Procedure On Error
Si:
Thanks for pointing that out.
Had time to sit and look this over and Found my error. Had changed the order of something because of a bitmap issue and this in turn caused -- in rare cases -- the scale not be be set (sngYMax and sngYMin were 0).
The old adage applies here:
Quote:
"When your up to your arse in Alligators it is hard to remember that your objective was to drain the swamp"
Thanks to everyone. Learned a few things, improved the code, got the original problem corrected, and got a refresher that sometimes it's better to sit back and relax before dealing with a problem.
All and all a GOOD DAY!