Microsoft KB Archive/88936

= Microsoft Knowledge Base =

INF: Tips for Using TestCtrl with VB Programs (Complete)
Last reviewed: July 25, 1996

Article ID: Q88936

This information applies to:


 * Microsoft Test for Windows, versions 1.0 and 2.0

SUMMARY
Below is a list of three points to keep in mind when you write Microsoft Test scripts to test Visual Basic applications:


 * The &quot;control name&quot; parameter for TestCtrl routines is the Caption of the control being tested. (For example, WButtonExists &quot;Command1&quot;).
 * TestCtrl routines use the Caption of a static control to test controls that lack a Caption of their own. (For example, list boxes, combo boxes, and so on.) TestCtrl then uses the next enabled, non- static control in the tab order for the comparison. Since Visual Basic labels are not standard Windows static controls, there are several complications that may prevent Microsoft Test from being able to locate captionless Visual Basic controls by their associated label.
 * For Test to recognize Visual Basic for Windows control classes, you must set the control class correctly by using the appropriate W SetClass routine. For example, before using WCheckExists to test for the existence of a Visual Basic for Windows check box control, you must use WCheckSetClass &quot;ThunderCheckBox&quot; in your code. Otherwise, WCheckExists will fail, because by default it looks for Windows class controls (in this case, a Windows class check box).

MORE INFORMATION
Testing Controls that Do Not Have a Caption

As mentioned above, TestCtrl routines use the caption of a static control to locate controls that lack a caption. Microsoft Test treats controls without a caption (list boxes, combo boxes, text/edit boxes, etc.) differently than it treats controls with captions. For controls without captions, TestCtrl searches for an enabled label control with the indicated caption, then finds the next control in the tab order with the expectation that it will be the desired captionless control.

For example, suppose you have a standard Windows static control and a standard edit control arranged on a dialog as follows:

+--+ (Static) --> File Name: |             | <-- (Edit) +--+

Because an edit control has no caption of its own, you use the text of the preceeding static label as its 'name'. The following code demonstrates how you could check for the existence of the above edit control:

If WEditExists(&quot;File Name:&quot;) Then ... TestCtrl searches for a static control (label) with the caption &quot;File Name:&quot;. Once it finds that static control, it then looks to the next control in the tab order (in this case the edit control) and applies the WEditExists function to it. (In this case, WEditExists would return a value of TRUE, because the edit control exists).

Unfortuately, Visual Basic differs from standard Windows-based applications in several ways.

In Visual Basic versions 2.0 and 3.0, label controls are light weight controls. This means that these controls do not actually exist at run time. Visual Basic creates and manages a bitmap that makes it appear that there is a label on the form at the desired location, when in fact no such control exists.

You can verify this by using the TestDlgs utility or the Spy utility provided with the Windows SDK. Since the label controls do not exist, there is no way for Microsoft Test to find the label, and therefore no way to locate captionless controls using the label.

Visual Basic also maintains its own interal notion of the tab order of controls (the TabIndex property), and does not keep its controls in this order as a standard Windows-based application does. This means that Microsoft Test may not be able to find a captionless control based on its associated label, because these controls may not actually follow one another in the internal control order.

You can work around this in several ways:

If the application you are testing was written in Visual Basic version 1.0 for Windows, you can write your own code to locate captionless Visual Basic controls. Please see the VBDEMO.MST sample program, at the end of this article for more information on this method.

If your application was written in Visual Basic version 2.0 or 3.0, you will not be able to locate captionless controls be their associated label, because Visual Basic labels do not exists as true controls in these versions of Visual Basic. The only alternative in Microsoft Test version 1.0 is to place the focus on the desired control and pass an empty string (&quot;&quot;) as the control name. Passing an empty string for the control name will cause Microsoft Test to operate on the control that has the focus.

In Microsoft Test version 2.0, you can also try locating the control by its ordinal position by passing &quot;@#&quot;, where # = the ordinal position of the control (that is, &quot;@1&quot;, &quot;@2&quot;, and so on), for the control name. This will allow you to check for controls without having to set the focus to them. However, because Visual Basic does not keep its internal control order consistent with the tab order it uses, some experimentation will be necessary to find a given control's ordinal postion.

Recognizing Visual Basic for Windows Class Controls
As mentioned above in the Summary section, for Test to recognize Visual Basic for Windows class controls, you must set the control class correctly by using the appropriate W SetClass routine before testing.

Visual Basic for Windows class controls differ structurally from Windows class controls. A Visual Basic for Windows option button, for example, is different from a Windows option button. Microsoft Test functions test for Windows class controls by default.

Because of this structural difference, Microsoft Test will not recognize non-Windows class controls unless you use the appropriate W SetClass routines before testing.

The general syntax for using the SetClass routines is:

W SetClass &quot; &quot; The Visual Basic for Windows class names are:

Microsoft Test      Visual Basic for    Visual Basic for Routines            Windows Control     Windows Class Name ---

WCheck... Check Box          ThunderCheckBox WCombo... Combo Box          ThunderComboBox WEdit... Text Box           ThunderTextBox WList... List Box           ThunderListBox WOption... Option Button      ThunderOptionButton WButton... Command Button     ThunderCommandButton WStatic... Label              ThunderLabel If, for example, you use the controls in the example above (a label and a text box), you need to have the following in your code

WStaticSetClass &quot;ThunderLabel&quot; WEditSetClass  &quot;ThunderTextBox&quot; ... before you can use the Microsoft Test routines listed above.

Microsoft Test version 2.0 automatically recognizes the Visual Basic class names listed above as the associated type of control. You do not need to set the class name for Microsoft Test version 2.0 to recognize these controls.

TestCtrl Routines Fail when Looking for Captionless Controls Based on a Preceding Label

Two programs are listed below, VBCTRL.INC and VBDEMO.MST. VBDEMO.MST demonstrates the TestBasic routines in VBCTRL.INC, which are designed to test Visual Basic applications.

Note: VBDEMO.MST is only a demonstration program to show how the routines in VBCTRL.INC are used. Only VBCTRL.INC needs to be included in your own programs to test Visual Basic applications.

This method will only work with Visual Basic version 1.0 for Windows applications because in Visual Basic versions 2.0 and 3.0 for Windows, labels are painted directly to the form, and are not really controls.

Four useful functions you can call from VBCTRL.INC are:

WSetVBClasses - Performs all W SetClass calls necessary to work with Visual Basic applications.

WGetVBComboHwnd - Sets focus to the Combo Box with the specified associated label and returns the window handle.

WGetVBEditHwnd - Sets focus to the Edit control (Text Box) with the specified associated label and returns the window handle.

WGetVBListHwnd - Sets focus to the list box with the specified associated label and returns the window handle. To run the example, do the following:

  With Visual Basic version 1.0 for Windows, create a Visual Basic for Windows form with the following controls: Control             Caption           TabIndex Property -

Command1            Command1                  0 Label1               Label1                   1 Combo1                ---                     2 Check1               Check1                   3 Option1              Option1                  4 Label2               Label2                   5 Edit1                 ---                     6 Label3               Label3                   7 List1                List1                    8  From the File menu, choose &quot;Make EXE file...&quot; to make the program executable by selecting it. Enter and save the code for VBDEMO.MST and VBCTRL.INC (listed below). Load VBDEMO.MST into Test (TestDriver) and run the program.  To view the results of the test, see the TestDriver Viewport after running VBDEMO.MST. '*********************************************************************

' VBDEMO.MST - demonstrates using the TestBasic routines in VBCTRL.INC ' (when necessary) to test a Visual Basic application. ' This demo tests the active window of a Visual Basic for Windows ' program for the existence of the following Visual Basic for ' Windows controls: ' '  Control Type      Caption      Associated label caption '  '   Command button    Command1 '  Check box         Check1 '  Option button     Option1 '  Combo box                           Label1 '  Text box                            Label2 '  List box                            Label3 ' '*********************************************************************

'$Include: 'VBCTRL.INC'

' Initialize TESTCTRL.DLL so that it uses the Visual Basic for ' Windows class names. 

WSetVBClasses

' Start the Visual Basic for Windows executable. Replace the name of the ' executable with the name of your Visual Basic application. Run &quot;VBAPP.EXE&quot;, Nowait

' Non-label controls with captions can be tested with the standard ' TESTCTRL.DLL routines. Print &quot;Checking for controls with captions. A -1 indicates&quot; Print &quot;That the control was found:&quot; Print Print &quot; Button 'Command1' exists?&quot;; WButtonExists(&quot;Command1&quot;) Print &quot; Checkbox 'Check1' exists?&quot;; WCheckExists(&quot;Check1&quot;) Print &quot; Option button 'Option1' exists?&quot;; WOptionExists(&quot;Option1&quot;) Print

' Call the appropriate WGetVB Hwnd routine (from VBCTRL.INC) ' to set the focus to the desired control and retrieve its window ' handle (hwnd). ' ' After calling WGetVB Hwnd, you can use the appropriate ' TESTCTRL routines on the target control by specifying an empty ' string (&quot;&quot;) for the control name. Print &quot;Checking for captionless controls. A non-zero value&quot; Print &quot;Indicates that the control was found:&quot; Print

comboHwnd% = WGetVBComboHwnd(&quot;Label1&quot;) Print &quot; Combo with label 'Label1' exists?&quot;; comboHwnd% If comboHwnd% Then

Print &quot;  Combo has text '&quot;; ComboText(&quot;&quot;); &quot;'&quot; End If

EditHwnd% = WGetVBEditHwnd(&quot;Label2&quot;) Print &quot; Edit with label 'Label2' exists?&quot;; editHwnd% If editHwnd% Then

Print &quot;  First line of Edit text is '&quot;; EditLineText(&quot;&quot;, 1); &quot;'&quot; ' (NOTE: Above two lines should be on one line.) End If

listHwnd% = WGetVBListHwnd(&quot;Label3&quot;) Print &quot; Listbox with label 'Label3' exists?&quot;; listHwnd% If listHwnd% Then

Print &quot;  First item in List box has text '&quot;; ListItemText(&quot;&quot;,     1); &quot;'&quot; ' (NOTE: Above two lines should be on one line.) End If

'********************************************************************* ' VBCTRL.INC - Microsoft Test 1.0 include file for working with Visual ' Basic version 1.0 for Windows standard controls. '********************************************************************* ' '$Define TESTEVNT '$Define TESTCTRL '$Include: 'MSTEST.INC' '$Include: 'WINUSER.INC'

'********************************************************************* '                  Declarations and Constants '********************************************************************* Declare Sub WSetVBClasses Declare Function VerifyClass(hwnd%, targetClass$) As Integer Declare Function FindVBLabel(parentHwnd%, LabelTxt$) As Integer Declare Function WGetVBListHwnd(labelName$) As Integer Declare Function WGetVBComboHwnd(LabelName$) As Integer Declare Function WGetVBEditHwnd(LabelName$) As Integer Const VB_BUTTON_CLASS$ = &quot;ThunderCommandButton&quot; Const VB_CHECK_CLASS$ = &quot;ThunderCheckBox&quot; Const VB_COMBO_CLASS$ = &quot;ThunderComboBox&quot;

Const VB_EDIT_CLASS$  = &quot;ThunderTextBox&quot; Const VB_LIST_CLASS$  = &quot;ThunderListBox&quot; Const VB_OPTION_CLASS$ = &quot;ThunderOptionButton&quot; Const VB_STATIC_CLASS$ = &quot;ThunderLabel&quot;

' MAX_NESTING_LEVEL controls how many levels of nested controls ' the FindVBLabel routine will search. Const MAX_NESTING_LEVEL = 6

' wVBStack is an array used to preserve window handles while searching ' nested controls. Because all TestBasic Subs are all static, you need ' to create your own stack to preserve information as you recursively ' search a level of nested controls. This variable is used only in ' FindVBLabel, but is defined globally to avoid redefinition ' problems when you recursively call the static routine FindVBLabel. Global wVBStack(MAX_NESTING_LEVEL) As Integer

'********************************************************************* '                          Subs and Functions '*********************************************************************

'********************** WSetVBClasses ******************************** ' Initializes TESTCTRL.DLL to use the Visual Basic class names instead ' of the standard Windows class names. '********************************************************************* Sub WSetVBClasses Static WButtonSetClass VB_BUTTON_CLASS$ WCheckSetClass VB_CHECK_CLASS$ WComboSetClass VB_COMBO_CLASS$ WEditSetClass VB_EDIT_CLASS$ WListSetClass VB_LIST_CLASS$ WOptionSetClass VB_OPTION_CLASS$ WStaticSetClass VB_STATIC_CLASS$ End Sub

'******************** WGetVBComboHwnd ******************************** ' Given the name of the associated label, WGetVBComboHwnd sets the ' focus to the specified combo box and returns its window handle ' (hwnd). '********************************************************************* Function WGetVBComboHwnd(LabelName$) Static As Integer

labelHwnd% = FindVBLabel(GetActiveWindow, LabelName$) If labelHwnd% Then bool% = SetFocus(labelHwnd%) DoKeys &quot;{TAB}&quot; Else WGetVBComboHwnd = 0 Exit Function End If

comboHwnd% = GetFocus ' The focus will be directly on the combo for &quot;dropdown list&quot; ' style combo boxes. If VerifyClass(comboHwnd%, VB_COMBO_CLASS$) Then wGetVBComboHwnd = comboHwnd% Else ' Check to see if we are on the child edit control of a   ' &quot;simple combo&quot; or &quot;dropdown combo&quot;, If VerifyClass(comboHwnd%, &quot;Edit&quot;) Then If VerifyClass(GetParent(comboHwnd%), VB_COMBO_CLASS$) Then WGetVBComboHwnd = comboHwnd% Else WGetVBComboHwnd = 0 End if   Else WGetVBComboHwnd = 0 End if End If End Function

'******************** WGetVBEditHwnd ********************************* ' Given the name of the associated label, WGetVBEditHwnd sets the ' focus to the specified edit (text box) and returns its window handle ' (hwnd). '********************************************************************* Function wGetVBEditHwnd(LabelName$) Static As Integer

labelHwnd% = FindVBLabel(GetActiveWindow, LabelName$) If labelHwnd% Then bool% = SetFocus(labelHwnd%) DoKeys &quot;{TAB}&quot; Else WGetVBEditHwnd = 0 Exit Function End If

editHwnd% = GetFocus If VerifyClass(editHwnd%, VB_EDIT_CLASS$) Then wGetVBEditHwnd = editHwnd% Else WGetVBEditHwnd = 0 End If End Function

'******************** WGetVBListHwnd ********************************* ' Given the name of the associated label, WGetVBListHwnd sets the ' focus to the specified Listbox and returns its window handle (hwnd). '********************************************************************* Function wGetVBListHwnd (LabelName$) Static As Integer

labelHwnd% = FindVBLabel(GetActiveWindow, LabelName$) If labelHwnd% Then bool% = SetFocus(labelHwnd%) DoKeys &quot;{TAB}&quot; Else WGetVBListHwnd = 0 Exit Function End If

listHwnd% = GetFocus If VerifyClass(ListHwnd%, VB_LIST_CLASS$) Then wGetVBListHwnd = ListHwnd% Else WGetVBListHwnd = 0 End If End Function

'*********************** VerifyClass ********************************* ' Verify that a window or control is of the requested class, using the ' Windows API call GetClassName. Returns TRUE if the window or ' control is of the correct type, or FALSE if it is not. '********************************************************************* Function VerifyClass (hwnd%, TargetClass$) Static As Integer

ClassBuffer$ = String$(255, &quot; &quot;) l% = GetClassName(hwnd%, ClassBuffer$, Len(ClassBuffer$)) ClassBuffer$ = Mid$(ClassBuffer$, 1, l%) If ClassBuffer$ = TargetClass$ Then VerifyClass = TRUE Else VerifyClass = FALSE End If End Function

'*********************** FindVBLabel ********************************* ' Returns the window handle (hwnd) of the specified VB label control ' if it is found, or NULL if the label is not found. FindVBLabel will ' search nested controls that are nested MAX_NESTING_LEVEL or less in ' depth. The initial call to FindVBLabel should normally be passed ' the window handle of the active window. '********************************************************************* Function FindVBLabel (parentHwnd%, labelTxt$) Static As Integer

' Get the window handle of the first child window of parentHwnd% hwnd% = GetWindow(parentHwnd%, GW_CHILD)

' Search through all child windows While hwnd% > 0 ' Check if the current window (identified by the 'hwnd' window   ' handle) is a Visual Basic for Windows label control If VerifyClass(hwnd%, VB_STATIC_CLASS$) Then lenCaption% = SendMessage(hwnd%, WM_GETTEXTLENGTH, zero%, 0) caption$ = String$(lenCaption%, &quot; &quot;) + Chr$(0) bool% = SendMessage(hwnd%, WM_GETTEXT, len(caption$), caption$) If caption$ = labelTxt$ Then FindVBLabel = hwnd% Exit Function End If

' If not, check the children (if any) of the window ElseIf nestingLevel% < MAX_NESTING_LEVEL Then ' Preserve current hwnd wVBStack(nestingLevel%) = hwnd% nestingLevel% = nestingLevel% + 1 ' Make a recursive call to FindVBLabel to search child controls hwnd% = FindVBLabel(hwnd%, labelTxt$) If hwnd% Then FindVBLabel = hwnd% nestingLevel% = nestingLevel% - 1 Exit Function Else ' Restore last hwnd nestingLevel% = nestingLevel% - 1 hwnd% = wVBStack(nestingLevel%) End If

End If   ' Get next window hwnd% = GetWindow(hwnd%, GW_HWNDNEXT) Wend FindVBLabel = FALSE End Function