.Net Objects, Runtime Hierarchies and Bizarre QTP Errors

Article Tools

I’ve recently came across a disturbing phenomenon regarding .Net automation with QTP, and  thought I would share it with our readers. I hope some of you could shed some light on this mystery.

First, some background:

As described in our article about Exploring Custom Controls, sometimes QTP fails to properly recognize the AUT’s controls, and would show them as Objects (i.e. SWFObjects for .Net applications, JavaObjects for Java applications etc.). As simple objects, they lack the methods to input and manipulate them (for example, a combobox will have no .Select method), so this forces us to work “behind the scenes”, using the object’s Runtime methods and properties (as opposed to its Test-Object’s methods and properties).

So, for example, in order to input a textbox, we would have to use something like the following code snippet:

SwfWindow("x").SwfObject("y").Object.Text = "New Text"

As you can see, since we do not have a .Set method to relay on, we have to resort to overwriting the object’s runtime properties – specifically it’s .Text property.

 

Sometimes, our application is so complex, that we don’t even use QTP to reach the object, but “drill down” the the .Net runtime hierarchy:

Dim oTextEdit
Set oTextEdit = SwfWindow("x").Object.Controls.Item(0)

oTextEdit.Text = "New Text"

(obviously this is just a simple example, so the hierarchy is pretty much straightforward. We usually use this method only when the hierarchy is extremely complex).

As you can see, we only use QTP to get an easily described anchor in the application (in this case – its main form). Then we immediately  “punch through” to the runtime level, and make our way to the relevant control through there. This can be quite easily achieved, as .Net (and other high-level languages) is very strict and hierarchical – each object has a .Parent property which connects him to his housing control, and a .Controls property containing a collection of all its child-controls.

This is also a common technique in Web, Java and other environments, where .ParentNode, .ChildNodes, and other matching properties are used to navigate through the application’s control tree.

Now, to the actual problem:

I’ve been working for quite a while with this method for manipulating the runtime properties of objects, when I encountered a very strange problem – it seems that overwriting runtime properties works fine only when your “touching” the immediate control QTP reached; If you’re working with child/parent controls that are more distant from the QTP-Runtime bridge, it will result in an error. I’ll illustrate what I mean:

SwfWindow("x").Object.Text = "Something New" 'This works fine
SwfWindow("x").SwfEdit("y").Object.Text = "Something New" ' This also works fine

'The following line will generate an error:
SwfWindow("x").Object.Controls.Item(0).Text = "Something New"

This will occur even through SwfWindow(“x”).Object.Controls.Item(0) and SwfWindow(“x”).SwfEdit(“y”).Object refer to the very same object!

I even wrote a small program to illustrate this (Downloadable here). Run this code when the application is opened, and you get this very annoying error message:

'This Works Just Fine:
SwfWindow("swfname:=frmDemo").SwfEdit("swfname:=txtDemo").Object.Text = "Something"

'Now, to prove that we've reached the very same object:
Print SwfWindow("swfname:=frmDemo").Object.Controls.Item(0).Text 'Will print "Something"

'But, when we try to change the text, we'll get an error:
SwfWindow("swfname:=frmDemo").Object.Controls.Item(0).Text = "Something Else"

Development

To sum-up: Performing the very same action via different “channels” yields different results – it either works excellent, or fails miserably. Download the demo and see for yourselves.

Possible Workarounds:

To be honest – it’s not the end of the world. We could always “climb back” to QTP, get a pointer to our control, and perform the operation via that pointer. The following code snippet demonstrates this using the runtime object’s windows handle:

'Get the control windows handle
iWhnd = SwfWindow("swfname:=frmDemo").Object.Controls.Item(0).Handle

'Use that to "find" the control in QTP:
SwfWindow("swfname:=frmDemo").SwfEdit("hwnd:=" & iWhnd).Object.Text = "Something Else"

(once more – this example is so simple, it would be quite a waste to use such an elaborate method to find the relevant object. In real-world examples, it’s impossible to find the relevant object through QTP – so working through the runtime hierarchy is the only possible solution).

Conclusion:

It seems that manipulating the AUT’s runtime objects only works in the immediate objects QTP reached. Venturing further into the runtime hierarchy via the .Parent / .Controls properties works fine for retrieving properties (remember – we were able to get the text from the textbox), but fails for updating and manipulating them.

While this is not a show-stopping problem (as our Handle workaround demonstrated), it’s bound to annoy us, slow us down, and will make our code less readable and maintainable.

I’ve so far traced this behavior on several computers, both on VS2005 and VS2008 applications, and both on WinXP SP2 / SP3 machines. It doesn’t seem to be a straightforward security issue, since we can perform the operation when we reach the runtime object directly attached to QTP. Plus, I can swear I’ve done these sorts of things before, and have never gotten any errors.

I would very much like to hear your take on the issue – can you reproduce this behavior? If not – on what application / machine configuration doesn’t it occur?

Thanks!

Previous postUpcoming: My System by SOLMAR Knowledge Networks Next postImplementing a Generic Iterator with Function Pointers

Related Posts

Post Your Comment

You must be logged in to post a comment.