COMMON VISUAL BASIC PROGRAMMING QUESTIONS

What's the difference between MODAL and MODELESS forms?
When/Why should I use Option Explicit?
How do I make a text box not beep but do something else when I hit the Enter key?
How do I get the Tab key to be treated like a normal character?
What is passing by reference?
How should dates be implemented so they work with other language and country formats?
Why does everyone say I should save in TEXT not BINARY?
Is the Variant type slower than using other variable types?
How do I implement an incremental search in list/dir/combo/file boxes?
Is there any way to pass a variable to a form apart from using global variables?
I get a "file not found" error on the IIF function when I distribute by program. Uh?
How do I dial a phone number without using MSCOMM?
Why doesn't "my string" & Chr$(13) do what I want?
How do I avoid the overflow error when assigning values to my variable?
'Unable to register <dllname>' error when installing VB onto Windows 95 or Windows 98.
'Unable to register <dllname>' error when installing an application onto a user's Windows 95 or Windows 98 machine.
Can I add my own variables, in addition to any already there, to a form or control sub so I can pass additional data?
I have a simple project. Can I declare an API or user-defined type (UDT) just in a form?
How can I activate a running app, or launch it if it is not running?
How can I display an ampersand (&) in a menu item or in a label?
How can I find out how many MDI child forms are open in a MDI project?
I've finished designing, but is there an easy way to reorder my Tabstops at design-time?
How can I use an accelerator in a label but have the focus move to the textbox or control associated with that label?
How can I show the Windows 95/98 'Open With' dialog in response to a command button click?
How can my application receive command-line parameters?
How do I call a procedure in one form from a routine in another form in VB4/5/6?
How do I call a procedure in one form from a routine in another form in VB3?
How do I declare a constant to make it available to all procedures in all forms?
How do I declare a variable to make it available to all procedures in all forms?
What is the scope of a variabled DIM'med in different parts of a project? (excludes static variables)
What is the easiest way to find the number of open forms in my app?

MISCELLANEOUS TIPS AND INFORMATION

Multiple identifiers after the DIM statement can be confusing

Some programmers with background from Pascal can try the following

Dim iA, iB, iC as Integer

and think that all these 3 variables end up as Integer. In fact, the first two end up as default data type, normally Variant.

Instead you should do

Dim iA as Integer
Dim iB as Integer
Dim iC as Integer

which takes up more space, but gives you room to comment your variables (hint, hint); or

Dim iA%, iB%, iC%

which does the whole job.

Q): What's the difference between MODAL and MODELESS forms?

A): MODAL forms are forms which require user input before any other actions can be taken place. In other words, a modal form has exclusive focus in that application until it is dismissed. When showing a modal form, the controls outside this modal form will not take user interaction until the form is closed. The internal MsgBox and InputBox forms are examples of modal forms. To show a form modally, use
the syntax:

  MyForm.SHOW.vbModal ' a predeclared constant for 1

MODELESS forms are those which are shown but do not require immediate user input. MDI child forms are always modeless. To show a form modeless, use the syntax:

  MyForm.SHOW

Q): When/Why should I use Option Explicit?

A): Option Explicit forces you to declare all variables before usingthem. Opinions vary greatly on this subject. The main reason to use the OPTION EXPLICIT statement at the top of all modules is to minimize the amount of bugs introduced into your code by misspelling a variable name. Most variants of BASIC (including VB) have the capability to create variable 'on the fly' (without any clarations).This capability can be a double edged sword.

The OPTION EXPLICIT statement causes VB to 'disable' its ability to create variables on the fly. Thus, all variables must be declared using a DIM or REDIM statement. All variables not declared will cause an error when the OPTION EXPLICIT statement is used. This will eliminate bugs caused by a misspelled variable. The option works module-wide,so you can have some modules with and some without this option in your project. Declaring variables "on the fly" makes all but the most trivial code difficult to debug and maintain.

VB creates Variant type variables by default.

Q): How do I make a text box not beep but do something else when I hit the Enter key?

A): Put something else in your _KeyPress event, depending on what you really want. This code example makes nothing happen, for an extended period of time:

  Sub Command1_KeyPress (KeyAscii As Integer)
      If KeyAscii = 13 Then  
' 13 is Enter key   
          KeyAscii = 0   
      End If  
  End Sub

To have the Enter key emulate the Tab key action, you will need to add the line 'SendKeys "{tab}"' above 'KeyAscii = 0' in the example above (yes, I thought KeyAscii = 9 works but it doesn't! Tab is obviously handled by Windows on a lower level).

Q): How do I get the Tab key to be treated like a normal character?

A): You must set TabStop = False for ALL controls on the active form. Then the user will be able to insert Tab (chr9) characters in controls  if you feel you need the Tab key to behave like "normal" (e.g. jump to next control) outside this specific control, it is trivial to emulate its functionality code: 

  Sub Command1_KeyDown (KeyCode As Integer, Shift As Integer)
    If KeyCode = 9 Then
      If Shift = 0 Then
        Command2.SetFocus ' Tab= Next control
      ElseIf Shift = 1 Then
        Command3.SetFocus ' Shift-Tab = Prev.ctrl
      End If
    End If
  End Sub

Q): What is passing by reference?

A): Arguments are either passed by reference or by value. When they are passed by value,they cannot be changed by the procedure or function they are passed to. They can be altered when passed by reference, since passing by reference is just passingthe address.

Note that procedure are less strict about variable types when you use BYVAL. If you declare that your Sub takes a Variant, VB takes that seriously and gives a nasty "mismatch error" if you try to pass e.g. a string to it. Make it ByVal (at the cost of some speed) and your sub will be more tolerant.

Also note the following nasty trap: Arguments are passed by reference unless enclosed by parentheses or declared using the ByVal keyword.

Q): How should dates be implemented so they work with other language and country formats ?

A): If you use e.g.MM/DD/YY format dates in a program, you will get either a runtime-error(e.g.month>12)or the wrong date (e.g.March 12 instead of December 3) when your program isused in Europe. And vice versa, of course. Even Microsoft's own example programs (like the MAPI sample in VB3) make this stupid mistake and fail miserably.Use the format command to make sure you get the date you want. For example

  strTodaysDate = Format[$](Now,"Short Date")

As a side note, Microsoft has taken much heat on the newsgroup for VB's bad support for internationalization! Just try to make a date literal in source code that works everywhere as little exercise. Answer elsewhere in this document.)

Q): Why does everyone say I should save in TEXT not BINARY?

A): Applies only to VB 3.0 and below: Actually, saving in binary mode is a bit faster, so why is it recommended to save in text?

If you save the source and project as text, it becomes ASCII (or really, ANSI) code that you can edit with any text edit or (if you are careful when you save) word processor. If you save in binary only, only the VB development environment, current or later versions, will understand the code. The Setup Wizard can not scan binary projects. Also, source documenters and other programming tools usually require text mode. If you use text, you can use a simple text editor (e.g. notepad) to cut and paste code from other source/form modules into your current project. Some 'tricks' (like making an array of one control into a single non-array control again) is easily done with an editor but not that easy in the environment. If you want to import the text files into your word processor. And, finally, if something goes wrong (only one byte is changed!) you may be out of luck in binary mode. In text mode you will more easily be able to fix it.

VB 4.0 and greater does not offer the option of saving in binary, saving us from a lot of problems.

Q): Is the Variant type slower than using other variable types?

A): Generally, yes, if we are talking numeric variable types. The Variant type also increases memory overhead. To test the speed difference, try the following piece of code in something like a button_click event and keep the debug window on the screen:

  Dim Va As Variant
  Dim In As Integer

  T1! = Timer
  For i% = 1 To 32767
      Va = i%
  Next

  T2! = Timer
  Debug.Print "With a variant: "; Format$((T2! - T1!), "0.0000")

  T1! = Timer
  For i% = 1 To 32767
      In = i%
  Next

  T2! = Timer
  Debug.Print "With an integer: "; Format$((T2! - T1!), "0.0000")

This test shows (on our test system using VB 6.0) that integers are 60% faster! However, for strings there were no real difference, or in some instances, variants were faster than strings for routines with heavy conversion usage. For best results in your application, test your routines directly. 

Q): How do I implement an incremental search in list/dir/combo/file boxes?

A): This is your lucky day. Dan Champagne (Dan_Champagne@dell.com) made some VB code (no DLLs are necessary!) which easily provides this feature for your application:

  ' Code by Dan Champagne
  ' 4/18/94
  ' This code can be used to do an incremental search in either a list box,
  ' dir, combo, or a file box. The following code is set for a file box with a
  ' different name, change all occurences of FILE1 with whatever you or VB has
  ' named your list, combo, dir, or file box. There are two places where you
  ' will need to change these. They are on the last couple of lines in the
  ' KeyPress code. Also, thanks to John Tarr for helping debug the code.
  '
  ' In a .BAS file, add the following:
  ' searchme$ is a global variable that will keep track of what the user has
  ' typed so far.

  Global searcme$
  Global Const WM_USER = &H400
  Global Const LB_SELECTSTRING = (WM_USER + 13)
  Global Const LB_FINDSTRING = (WM_USER + 16)

  Declare Function SendMessageByString& Lib "User32" ALIAS "SendMessage" _
  (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, ByVal lParam$)

  ' In File1 under keydown, add the following:
  ' This checks if the user has pressed the up or down arrow.
  ' If they have, reset searchme$ to vbNullString.

  If KeyCode = 40 Or KeyCode = 38 Then
      searchme$ = vbNullString
  End If

  ' In File1 under lostfocus, pathchange, patternchange, and click add:
  ' If the user has done any of the above, reset searchme$
  searchme$ = vbNullString

  ' In File1 under KeyPress add:
  Dim result&

  Select Case KeyAscii
      Case 8  ' Backspace
          If searchme$ <> vbNullString Then
              searchme$ = Left$(searchme$, Len(searchme$) - 1)
          Else
              File1.ListIndex = 0
          End If
          KeyAscii = 0
          Exit Sub
      Case 27  ' Escape
          searchme$ = vbNullString
          KeyAscii = 0
          Exit Sub
      Case 13  ' Enter
          searchme$ = vbNullString
          KeyAscii = 0
          Exit Sub
      Case Asc("a") To Asc("z"), Asc("A") to Asc("Z"), Asc(","), Asc("."), _
           Asc(" "), Asc("0") To Asc("9")
          searchme$ = searchme$ & Chr$(KeyAscii)
          KeyAscii = 0
  End Select

  result& = SendMessage(FILE1.hWnd, LB_FINDSTRING, 0, searchme$)
  If result& = -1 Then
      searchme$ = Left$(searchme$, Len(searchme$) - 1)
  Else
      result& = SendMessage(FILE1.hWnd, LB_SELECTSTRING, -1, searchme$)
  End If

Q): Is there any way to pass a variable to a form apart from using global variables?

A): The standard workaround is to put an invisible text box (or caption or any other control that suits your use.) on the target form and access it by:

  Form.textbox = "value"

Then you can use the Change event of that control to do anything you want in that form. Also, check out the Tag property which is a "what-you-want" property where you can hook any string you want onto a control. This property can also be accessed from other modules.

Perhaps a more elegant and flexible way is to use the ArrayPlus ActiveX component to implement a stack or as a property bag.

Q): I get a "file not found" error on the IIF function when I distribute by program. Uh?

A): There's a documentation error in VB 3.0, since the manual does not tell you that the IIF function requires the file MSAFINX.DLL to be distributed with your application. No, IIF is not really a financial function, so go figure.

Q): How do I dial a phone number without using MSCOMM?

A): For 32-bit programming, you can call the phone dialer directly through the API. If you make a standard one-form project with two buttons (OK, Cancel), and two text boxes (txtName and txtNumber), you can use the following code to dial a number:

  Private Declare Function tapiRequestMakeCall Lib "TAPI32.DLL" _
   (ByVal DestAddr$, ByVal AppName As String, ByVal CalledParty As String, _
    ByVal Comment As String) As Long

  Public Sub CallPhone(Number As String, Name As String)
      Dim lRes As Long

      If (Trim(Number) = vbNullString) Then
          MsgBox "Sorry, no phone number supplied"
          Exit Sub
      End If
      If (MsgBox("About to call " & Trim(Name) & " at phone number " & _
          Trim(Number) & vbCrLf & "Do It?", vbYesNo, App.Title) = vbYes) Then
          lRes = tapiRequestMakeCall(Trim(Number), App.Title, Trim(Name), "")
          Debug.Print Now; " CallPhone -> tapiRequestMakeCall Result Code = "; lRes
      End If
  End Sub

  Private Sub cmdCancel_Click()
      Unload Me
  End Sub

  Private Sub cmdDial_Click()
      CallPhone txtNumber.Text, txtName.Text
  End Sub

For a more rough approach, try the following code:

  PhoneNumber$ = "(123) 456-7890"
  Open "COM2:" For Output As #1  ' Or COM1:
  Print #1, "ATDT" & PhoneNumber$ & Chr$(13)
  Close #1

Q): Why doesn't "my string" & Chr$(13) do what I want?

A): You need to also add a Chr$(10) to the line. Or us vbCrLf instead. For example:

  NewString$ = "my string" & vbCrLf

Q): How do I avoid the overflow error when assigning values to my variable?

A): Be wary of the scopes of variables. In particular, be wary of the Integer. It accepts values from -32768 to 32767. Anything else causes an overflow run-time error:

  i% = 30000 + 30000  ' fails!!

There is little reason not to use the Long data type instead, which brings us to some other, more subtle problem. Try this:

  l& = 30000 + 30000  ' fails!! Huh?

The reason for this very odd problem is that VB evaluates the expression internally, using the type of the constants, and in this case it's still Integer. So the run-time error occurs even before it tries to assign the value to the Long type variable. To make it work, you have to do the following:

  i& = 30000& + 30000&  ' ok

While the Long uses two bytes more internal space, this is nothing to worry about. Since the 32-bit CPU actually works with 32-bit long integers internally, it is even a (theoretical) bit faster than the shorter counterpart. So if in doubt, and even if not, stick to Longs.

[Note] Some perfectly sane programmers prefer to create their VB applications using the Variant data type almost exclusively. While that would not solve the actual problem above, it certainly will remove most type-conversion and mismatch problems with VB. The extra execution time and memory overhead is rarely significant.

Q): 'Unable to register <dllname>' error when installing VB onto Windows 95 or Windows 98.

A): Windows 95 and Windows 98 have a maximum registry size of 64k. This limit can be quickly met with the installation of some of the larger applications (i.e. Office 97/2000, VB6, Informix). Most often the problem is related to unneeded and unreferenced string entries in the registry's Shared DLL key. VBNet has a Microsoft utility - shrdll.exe - available for download (less than 28k). Shrdll.exe is used to identify -- and optionally remove -- errant entries in the registry's Shared DLL section. Typically, I have found that using this utility reduces an Office/Win95/Win98 system registry from 64k+ to under 47k, after which installations run correctly.

Another thing I've heard, but cannot verify personally because its what I already do, is that VB6 uses so much shared dll/registry space that it almost needs to be installed first after a clean format of the system. The 64k registry limit exists on 95 and 98 systems. And although this utility is especially helpful on Win95/98 systems, it is equally helpful on NT4 systems as well.

Q): 'Unable to register <dllname>' error when installing an application onto a user's Windows 95 or Windows 98 machine?

A):  See the solution to the above FAQ.

Q): Can I add my own variables, in addition to any already there, to a form or control sub so I can pass additional data?

A): No. Form and control routines that are listed in the Object combo box in the code window can only use fixed variables assigned that make up the routines declaration.

Q): I have a simple project. Can I declare an API or user-defined type (UDT) just in a form?

A): Yes, however the declare or Type must be prefaced with the Private keyword. This restricts the scope of the variables (or APIs) to the form they are declared in. Be careful doing this, as its easy to forget to modify a Private type if used in more than one form.

Q): How can I activate a running app, or launch it if it is not running?

A): Declare the FindWindow and BringWindowToTop APIs in a form or module, then use:

  Private Sub Command1_Click()
      Dim hCalcWnd As Long
      Dim x As Long

      hCalcWnd = FindWindow("SciCalc", "Calculator")
      If hCalcWnd = 0 Then
          x = Shell("CALC.EXE", vbNormalFocus)
      Else
          BringWindowToTop(hCalcWnd)
      End If
  End Sub

Q): How can I display an ampersand (&) in a menu item or in a label?

A): The ampersand is a special symbol to windows denoting a keyboard accelerator key (underscoring the ALT<key> used to access the command from the keyboard). To display an actual ampersand in a label or menu, use two ampersands together, i.e. 'Save && Exit' displays as 'Save & Exit'.

Q): How can I find out how many MDI child forms are open in a MDI project?

A): The Count property of the Forms collection returns the number of forms currently loaded in the project. However, this number also includes the MDI parent form itself, as well as any other non-MDI child forms and hidden forms open in the project. To determine which open forms are actual MDI child windows, you can use:

  Private Function GetMDIChildCount() As Integer
      Dim frm As Form
      Dim cnt As Integer

      For Each frm In Forms
          If (frm.MDIChild And frm.Visible) Then cnt = cnt + 1
      Next frm
      GetMDIChildCount = cnt

  End Function

Call the routine as:

  NumberOfOpenForms = GetMDIChildCount()

Q): I've finished designing, but is there an easy way to reorder my tabstops at design-time?

A): The easiest and most sure-fire way of keeping your tabstops in numerical order is to begin with the LAST control you want in the tabstop order, and set its tab index to 0. Then move to the next-to-last control, and set its tab index to 0. Continue backwards through the controls setting each to 0 until the first control's tabstop has been reset; the rest of the controls will then be ordered correctly.

Q): How can I use an accelerator in a label but have the focus move to the textbox or control associated with that label?

A): Label controls, although they support the ampersand accelerator and tabstop, can never actually receive focus. By setting the tabstop of the textbox or control to one more than the label tabstop, pressing the label accelerator key will move the focus to the next control in the tabstop array.

Q): How can I show the Windows95/98 'Open With' dialog in response to a command button click?

A): The Open With dialog can be invoked with the following code:

  Private Sub Command1_Click()
      Dim x As Long

      x = Shell("rundll32.exe shell32.dll,
OpenAs_RunDLL [path]filename.ext")
  End Sub

Q): How can my application receive command-line parameters?

A): Simple. All command line parameters are passed to the VB program and are accessable via the Command$ function. It is fully documented in the VB help.

Q): How do I call a procedure in one form from a routine in another form in VB4/5/6?

A): In VB4, any new sub or function that you create in the form will exposed to all other forms and bas modules if you declare the routines as Public. Note though that you can not invoke a control's subs this way, that is,

Q): How do I call a procedure in one form from a routine in another form in VB3?

A): VB3 does not support Public routines in a form, so form-level routines are not directly callable from outside that form.

Q): How do I declare a constant to make it available to all procedures in all forms?

A): In a BAS module, place the words Public Const (VB4) or Global Const (VB2,3) before the variable assignment.

Q): How do I declare a variable to make it avaliable to all procedures in all forms?

A): In a BAS module, place the word Public (VB4) or Global (VB2,3) before the variable.

Q): What is the scope of a variable DIM'med in different parts of a project? (excludes Static variables)

A): A variable DIM'med inside any sub or function (in any module or form) is local to that routine only (only known about by the dim'ming routine), and is destroyed upon exiting that routine.

A variable DIM'med as Private in the general declarations section of a form is available only to routines in that form. The variable remains valid as long as the form is loaded.

A variable DIM'med as Public in the general declarations section of a form (i.e. Public Foo As Long) is available to routines in that form by referencing the variable name (i.e. Foo), or to modules or forms other than the declaring form by explicity referring to the variable with its form name as part of the prefix (i.e. Form1.Foo). The variable remains valid as long as the form is loaded.

A variable DIM'med Private in the general declarations section of a BAS module is available to routines only in that bas module. The Private keyword is not available in VB2 or VB3; use a standard DIM statement.

A variable DIM'med Public (or Global in VB2 or VB3) in the general declarations section of a BAS module is available to all routines in all forms and bas modules.

A variable DIM'med inside any sub or function that has the same name as a Public or Global variable takes precedence over the Public one in that routine where it is DIM'med. Its initial value is 0 or empty. The Public variable's content is not affected by use of the local variable of the same name. The local variable is destroyed upon exiting that routine; the global variable remains valid. Download VB4 demo and view the code. Generally, using the same name for a Public and Local variable is concidered poor programming practice.

Q): What is the easiest way to find the number of open forms in my app?

A): There are two ways - equally easy. The first is to retrieve the form collection count:

  numFormsOpen = Forms.Count

And the second is to use the DoEvents function:

  numFormsOpen = DoEvents           

 


Copyright © 1999-2002 Mckisic Software, Inc. All rights reserved
This file was last modified on 05/03/2002