Accessing PRIVATE Properties in .Net Controls

Article Tools

Background

One of the more frustrating aspects of automating any complex application is knowing that the application holds a lot useful information which isn’t accessible to QTP. Sometimes accessing the information is just not possible by any means (for example, accessing runtime properties from MFC applications), but other times, it seems to be just a creative hack away.

Reading private .net properties is one of those times. If we point QTP’s regular or .Net spy to a .Net application, it will show all the public properties, fields and methods, but it will skip the private ones. And sometimes, these are exactly the ones the hold the critical missing piece of information we need. Luckily, we can resort to external .Net spies (for example this, or this), which can browse these properties and fields just as if they were public.

For example, this is the .Net spy output on a standard .Net window. The scrollPosition property can be easily accessed (and shown to be a point, with coordinates = 0,0) – even though it’s private, and will not show up on QTP’s built-in spies. This can actually be an important property, as it indicates what part of the screen is visible.

image image

 

Reading private properties at runtime

OK, so we have the tools for spying these properties and browsing through their values; but can we use this information while running our script?

Well, it seems that QTP has some limitations in this matter. QTP 10 can access “simple” private properties, which have Boolean, string or integer values (for example, the VScroll private property, which indicates if the vertical scrollbar is displayed), but it cannot access private objects, or their child properties. This is not QTP’s fault – these properties were declared as private specifically to prevent others from accessing the information within them. So, if we’ll try to access the scrollPosition property in our script, we’ll get an error stating that the window object has no scrollPosition property, let alone the X, Y sub properties for it.

 

The following script will first display “False”, for the VScroll property (even though it’s private and won’t show up on QTP’s built in spies), but will then throw an error when accessing the X sub-property of scrollPosition:

MsgBox SwfWindow("Window").Object.VScroll 'This will work

 


 

MsgBox SwfWindow("Window").Object.scrollPosition.x 'This will throw an error

 

 

image

 

This can actually be very frustrating, because usually the most useful information is help up in private objects, or their nested properties. Luckily, we can use the .Net spy’s engine from within QTP, and get the private information we need through this ugly, yet effective hack. The following hack works on any version of QTP from 9.0 and above.

A comprehensive solution

First, download this .Net spy, and extract it to an accessible directory. For example, “C:\Spy”.

Next, make sure you have you .Net addin loaded, and that QTP correctly recognizes your .Net application.

Now, copy the following function to QTP:

Function GetPrivateData(oQTPSWFObject, sProperty)

 

    Const sSpyDir = "C:\Spy" 'The .Net spy locations

 


 

    Dim sResult 'Will hold the result data

 

    Dim Int32 'A .Net Int32 variable

 

    Dim IntPtr 'A .Net pointer variable

 

    Dim Injector 'The .Net injector object of the spy

 

    Dim Arg 'Will hold a .Net typed array

 

    Dim ObjectType 'Will hold a .Net System.Type Object

 


 


 

    'Create an Int32 containing the handle number of the relevant control

 

    Set int32 = DotNetFactory("System.Int32", "")

 

    Set Int32 = Int32.Parse(cStr(oQTPSWFObject.GetROProperty("hwnd")))  'Enter the Handle of the control here, as a string

 


 

    'Create a pointer to the relevant control

 

    Set IntPtr = DotNetFactory("System.IntPtr", "", Int32)

 


 

    'Set up the object to inject the code

 

    Set Injector = DotNetFactory("Bds.Inject.Injector", sSpyDir &"\InjectLib.dll")

 


 

    'Create an array of objects to pass as a paramter

 

    Set Args = DotNetFactory("System.Array")

 

    Set ObjectType = DotNetFactory("System.Type")

 

    Set Args = Args.CreateInstance(ObjectType.GetType("System.Object"), 2)

 


 

    'Enter values to the object array

 

    Args.SetValue IntPtr, 0

 

    Args.SetValue sProperty, 1

 


 

    Result = Injector.InvokeRemote(IntPtr, sSpyDir & "\ObjectSpyLib.Dll", "Bds.ObjectSpy.ObjectSpyEvaluator", "Evaluate", Args)

 


 

    GetPrivateData = Result

 


 

End Function

 

And make sure to change the sSpyDir constant to point to wherever you downloaded the .Net spy to.

 

This function extract the handle for the .Net control, and create a .Net pointer to it. It then create a .Net object array holding the pointer and the property to be read, and sends them to the .Net spy’s engine to retrieve the information.

The function may seem complicated (especially since it creates a lot of .Net objects), but using it is very simple:

Msgbox GetPrivateData(SwfWindow("Window"), "scrollPosition.x")

 

And indeed, the result is as you’d expect:

image

 

You can use it on all SWF objects (it’s not limited to the top level windows), and you can specify as complex a path to your required data as you need. For example: “Controls[0].ActiveControl.Name”, or any other property path you see fit. Any property you see via the .Net spy will be accessible at runtime using this solution.

Summary

Private properties and fields may hold vital information for automating an application, even though they aren’t meant to be accessed from outside their hosting objects.

Using external tools, we can browse and explore this information, and using the above function, we can even carry it over and use it in our scripts.

This technique can prove critical to automating custom .net controls, as reviewed before here.

Enjoy.

Previous postFind a File Recursively (revised) Next postRad ComboBox for ASP.NET AJAX ( Telerik )

Related Posts

Post Your Comment

You must be logged in to post a comment.