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, useMyForm.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.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 examplestrTodaysDate = 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
|
This file was last modified on 05/03/2002 |