Accessing PRIVATE Properties in .Net Controls
Posted by admin - Oct 13, 2009 .Net, Articles, Yaron Assa 0 0 Views : 633 Receive Updates For This Category
Article Tools
- Print this page
- Add Comment
- Send to Friend
- Last Updated on :
Jul 17, 2011
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.

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

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:
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.


