Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Error handling - listing properties of a form to an error file 1

Status
Not open for further replies.

AndrewMozley

Programmer
Oct 15, 2005
621
GB
When an error happens in an application, this may have been trapped by :

Code:
ON ERROR DO ErrorRoutine WITH ERROR(), MESSAGE(), PROGRAM(), LINENO() NOCONSOLE

The code which is then executed includes :

Code:
LIST MEMORY LIKE * TO <filename> NOCONSOLE

That works fine: Local, Public &c variables are listed with their names and values. There is other code in the error routine which shows what tables and indexes are in use.

I would like the report also to show the values of variables which are properties of the form where the error occurred; these can be seen at design time with Form | Edit property/method – the items of type ‘P’.

There would usually be no more than half a dozen of these : things like the account number of a customer being processed &c

When the error occurs, the command AMEMBERS(laProp, ) produces an array which includes these items, and this includes the names of the properties which I would like to show (with their current values) on the error report. But it also includes a large number of properties of the parent classes, which I do not usually wish to include in the report.

Would be grateful if anyone can provide code which would list the properties of the current form and their values.

- Andrew
 
FWIW, I'm working on a large vertical market application that's in use at a number of customers. My client (the owner of the vertical)gets any bug reports in his email overnight. For the most part, they are not reproducible errors.

When the client sends them to me along with the error log, I need to be able to dig in and try to understand exactly what was happening at that moment. It's complicated by the app being highly configurable, so different users see different stuff, and so on.

I've been adding more and more info to the error log to make this easier, but not having properties of objects has often meant I got stuck in my manual step-through. I'd been thinking a little about whether to do something to add that info. This thread shows me how easily I could do so (though we also have an application we'd have to do this for, but not a big deal) and makes me raise it on my priority list.

Tamar
 
Hi Tamar,

with some mild warning about object/property visibility, especially about hidden properties. The debugger can show you anything of any object even those hidden and protected, because it's a system tool. Code doing so with just the object reference can only see the public ones.

I think it would be possible to create an FLL that could completely flush out the name table and all values of variables, fields and properties of object variables. But as Andrew points out, most of the intrinsic and unchanged properties are of no interest.

I think it would be a good idea to add a property to any class that is a list of error reporting worthy properties and use that instead of using AMEMBERS.

Chriss
 
By the way, to illustrate how it's not trivial to handle visibility:

Code:
Clear
test = CreateObject("base")
test.Serialize()
test = CreateObject("sub")
? '---'
test.Serialize()

Define Class base as Custom
   Hidden mySecret
   Protected myPrecious
   myPrecious = "ring"
   mySecret = "top secret"
  
   Procedure Serialize()
      ? "Serialize "+this.class
      Local Array laProperties[1]
      Local lnI
      For lnI = 1 to AMembers(laProperties,This,0,"U")
          lcMember = laProperties[lnI]
          ? lcMember
          Try
             lvValue = GetPem(This,lcMember)
             ? lvValue
          Catch
             ? "can't read "+lcMember
          EndTry
      EndFor
   EndProc 
EndDefine

Define Class sub as base
   Hidden mySubSecret
   Protected mySubPrecious
   mySubPrecious = "magic wand"
   mySubSecret = "I won't tell you"
EndDefine

In the second run the subclass can't access its property "mysubsecret". But indeed it can read out "mySectret", which only the base class should be able to see. That's surely because the code is defined on the level of the base class and of course, should be able to read "mySecret", at the same time it wouldn't even know about the property "mySubsecret", but AMEMBERS tells it's there. So there's a split between what reflection methods like AMEMBERS can see and what's seen by code.

In a way this is fine, but if you expect the code to work in the context of the class, it does not, it works in the context of where it was defined. Although, the object knows when it's "baae" and when it's "sub" from THIS.Class and would be able to distinguish that running inherited it shouldn't be able to access "mySecret" but it should be able to access "mySubsecret" and vice versa if it runs as base class instance.

And what that all means is that you can't write a general routine that can serialize an object including its own hidden properties unless you explicitly write code on the level of the class itself that reads out its hidden properties. And well, that does not only sound right, it's by design, but then it's also counterproductive for such general implementation of evaluating members found by reflection (AMEMBERS).

If you don't inherit the Serialize() method but copy it 1:1 from base to the sub-class definition, it'll see the properties you expect it to be able to see, as is to be expected, indeed. Everything is correct, this isn't a bug, really, but it makes it impossible to make use of code inheritance to handle the class as if the code actually was written on the level of inheritance.

PS: I'm aware would inherited methods not be able to access hidden properties, you lose inheritance overall, you could only run the base class and not subclass instances, if it comes to hidden properties, and so visibility would be even less usable. It's good as it is, though it's hindering reflection.

I wonder if C# has the same problems with reflection, but then reflection there is a topic of its own and is handled differently than via some function like AMEMBERS.

Chriss
 
Oh, and in very short (TL;DR) This means even if you would write something that reads properties from the inside (THIS) instead of acting from outside with an object reference variable, it's still not possible to do with just one general base class method.

Chriss
 
Thanks, Chriss, for the reminder about the complications. None of our custom objects have hidden or private properties, so I think I'll be able to write something fairly quickly to handle the basics.

The idea of having a class tell us what properties matter is cool, but more work than I have time for now, and invariably, we'd guess wrong anyway.

Thanks
Tamar
 
Thanks Tamar,

Tamar said:
The idea of having a class tell us what properties matter is cool, but...
I merely thought of a property called - say - errorreportproperties, which holds a list of properties you want to report, instead of using AMEMBERS CFlags. Still a manual decision. And still could be wrong, it could be both too verbose or still not having the one property you'd need to see to understand the problem of the error.

But there's an idea, THIS_ACCESS could write a log of its parameter values, so you see which properties were accessed in chronological order. It just again takes time and memory. It could be like a queue limited to 5 entries to limit memory usage. But then again, the most recently addressed properties might not highlight the cause of a problem. You'd surely have the error throwing method, event and the last object property addressed before the error happened, but in case it's the USE of a non-existing or non-accessible DBF, just for sake of some example, the property last addressed likely isn't even involved in the problem.

Chriss
 
Yesterday, somewhere (probably one of the VFF sessions), I saw someone use LIST OBJECTS and realized that'll give me what I need with very little work.

Tamar
 
Tamar, good point about LIST OBJECTS. It would appear to meet Andrew's need for a way of showing properties and their values - and not just for the active form. But it's a pity that it doesn't have any kind of filtering: to only show properties that have changed from their defaults, for example. If you included it in an error routine, I imagine the output could be enormous.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Interesting, I second Mike, it's quite verbose output, but it has a LIKE clause, so at least you can only output one object.
It's not listing hidden and protected properties, though (why?).

If I replace "test.Serialize()" with "List Objects Like test" in my code above, I don't get output of MYSECRET and MYPRECIOUS.

Limiting the output to properties is not impossible, though, you just combine the output with AMEMBERS, go through the output of LIST OBJECTS and only keep lines about properties AMEMBERS outputs, too. Overall, I think that's more complicated though, than just iterating the AMEMBERS array. Since you also don't gain the hidden/protected properties, I don't get a benefit out of it, other than it being a simple way of getting all properties and values with just one line of code.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top