Whether you’re using your own reporting framework, or QTP’s native reporter, your report is probably filled with a lot of full-desktop screen-captures, which are supposed to make the report more comprehensible, but usually just confuse the average reader. An effective screen-capture should highlight the relevant area in the application, and not leave the reader with the daunting task of scanning every pixel for the relevant information for the report step.
Unfortunately, highlighting the relevant area only occurs when QTP encounters a control specific error (e.g. when a control is disabled or partially hidden). Other times highlighting the relevant control is left up to you, which can be very tedious (you must highlight each object before calling the report command), and something just flat-out impossible to do.
If you happen to work on a .Net application, the following reporting solution would work well most of the time, generically and automatically.
The solution uses the .Net runtime property .ActiveControl, which usually holds the focused child-control of the current .Net object. By drilling down though the .ActiveControl property, you could potentially reach the most inner-focused control, and highlight it.
In order to highlight the object, the code uses external Windows DLL functions, so you must include the following statements once, at the top of your action / external library file:
'Needed for the screen capture mechanism:
Extern.Declare micHwnd, "GetDesktopWindow", "user32", "GetDesktopWindow"
Extern.Declare micULong, "GetWindowDC", "user32", "GetWindowDC", micHwnd
Extern.Declare micInteger, "ReleaseDC", "user32", "ReleaseDC", micHwnd, micULong
Extern.Declare micULong, "CreatePen", "gdi32", "CreatePen", micInteger, micInteger, micDword
Extern.Declare micInteger, "SetROP2", "gdi32", "SetROP2", micULong, micInteger
Extern.Declare micULong, "SelectObject", "gdi32", "SelectObject", micULong, micULong
Extern.Declare micULong, "DeleteObject", "gdi32", "DeleteObject", micULong
Extern.Declare micULong, "GetStockObject", "gdi32", "GetStockObject", micInteger
Extern.Declare micULong, "Rectangle", "gdi32", "Rectangle", micULong, micInteger, micInteger, micInteger, micInteger
And here’s the actual screen-capture functions: ScreenCapture is a helper function which takes the actual capture and saves it to a unique file, and CaptureActiveControl (the function you should call) tries to highlight the active object and snap it.
'This funciton takes the actual capture
'Assume the control is highlighted
'Has a built in mechanim for generating unique file names
Private Function ScreenCapture 'As string = filename
Dim sImageFile
sImageFile = "C:\" & Replace(Replace(Now, ":", ""), "/", "") & ".png"
On Error Resume Next
'Try the original file name
desktop.CaptureBitmap(sImageFile)
While Err.Number <> 0
'While the name we generated already exists
Err.Clear
wait 1
sImageFile = "C:\" & Replace(Replace(Now, ":", ""), "/", "") & ".png"
desktop.CaptureBitmap(sImageFile)
Wend
On Error Goto 0
'Return the file name
ScreenCapture = sImageFile
End Function
'Catches the active control and captures it
Public Function CaptureActiveControl
Dim oControl
Dim iHwnd
'The foloowing constants and variables are required for the highlight mechanism
Const PS_SOLID = 1 : Const PS_INSIDEFRAME = 6 : Const R2_NOT = 6
Const NULL_BRUSH = 5 : Const PEN_WIDTH = 5:
Dim hDC, hPen
Dim nX, nY, nH, nW, i
Dim Sender
On Error Resume Next
'Check if the main application window exists
If Not SwfWindow("Your App Main Window Here").Exist(0) Then
'Just take an image of the desktop and get out
CaptureActiveControl = ScreenCapture
Exit Function
End If
'Get active control - start by attaching to the top main window
Set oControl = SwfWindow("Your App Main Window Here").Object
Do
'Keep drilling down until you can now longer find an active child control
Set oControl = oControl.ActiveControl
Loop Until oControl.ActiveControl is Nothing
iHwnd = ""
'Get the inner control’s handle for identification purpose.
iHwnd = oControl.Handle
If iHwnd = "" Then
'No .Net active control - get out
CaptureActiveControl = ScreenCapture
Exit Function
End If
'The focused object
Set Sender = SwfWindow("Your App Main Window Here").SwfObject("hwnd:=" & iHwnd)
' ** Retieve The sender information…
With sender
nX = .GetROProperty( "abs_x" )
nY = .GetROProperty( "abs_y" )
nW = .GetROProperty( "width" )
nH = .GetROProperty( "height" )
End With
' ** Get the Desktop DC
hDC = Extern.GetWindowDC( Extern.GetDesktopWindow() )
' ** Create a three pixel wide pen
hPen = Extern.CreatePen( PS_INSIDEFRAME, PEN_WIDTH, RGB( 255, 0, 0 ) )
Extern.SetROP2 hDC, R2_NOT
Extern.SelectObject hDC, hPen
' ** Use an empty fill
Extern.SelectObject hDC, Extern.GetStockObject( NULL_BRUSH )
' ** Do the highlight
Extern.Rectangle hDC, nX, nY, nX + nW, nY + nH
'Take the screen capture and save the file name
CaptureActiveControl = ScreenCapture
wait 0, 50
' ** Release Resources '
Extern.ReleaseDC Extern.GetDesktopWindow, hDC
Extern.DeleteObject hPen
End Function
CaptureActiveControl returns the file name of the captured screenshot, so you could use it in your reporting framework, or with the Reporter.ReportEvent command in QTP 10.
Similar methods can be used for Java, web, and other environments.
I hope you’ve found this article helpful in creating useful and effective automation reports.

Yaron Assa




July 16th, 2009 at 11:34 am
Nice tip, it works on web applications as well, I just tested it.
I just created the same function but it receives an object and then highlights it for the screenshot.
Thanks alot!