1. Common Questions/Issues With Visual Basic
  1. Working With Controls
  1. How do I save and read to textboxes?

Reading And writing text files requires use of file i/o functions. The functions/statements we will be using are the OPEN, CLOSE, LINE INPUT, EOF, and PRINT. First create a form that has one textbox and 2 command buttons on it. Make sure the textbox has it’s MultiLine property set to true. Below is the code to place in the control events as shown. We will be using the first command button to save the contents of the textbox to a file called "MyFile.txt" and we will be using the second button to load the text from the file. Please note the use of the "#1" in all statements involving the file. We use this file handler to identify which file we are talking about. At a given time you program could have a lot of files open for either reading or writing and this provides us with a more structured way to keep track of them and to identify them.

Private Sub Command1_Click()

Open "MyFile.txt" For Output As #1
‘ This opens the file "MyFile.txt" in the current directory for output.
Print #1, Text1.Text
This prints all of the text in the textbox to the file
Close #1 ‘ This closes the file we just opened.

End Sub

Private Sub Command2_Click()

Text1.Text = "" ‘ Clear the textbox
Open "MyFile.txt" For Input As #1 ‘ Open "MyFile.txt" so we can read it
Do Until EOF(1)
‘ Do the following code until we reach the end of the file
Line Input #1, temp$ ‘ Read in a line from the file
If Text1.Text <> "" Then
‘ If the textbox has text in it add the new text to it
Text1.Text = Text1.Text & vbCrLf & temp$
Else
‘ If the textbox is empty just stick our new text in it
Text1.Text = temp$
End If
Loop ‘ Loop back to the do as long as we aren’t at the end of the file
Close #1 ‘ Close the file

End Sub

Ok, now that you have all that code added run the program. Type a few lines of giberish and click the first button to save it. Now change the textbox so you’ll notice a difference when you reload the text file. Click the second button and you are back to your original text that you saved. Also if you check your vb directory you’ll see a text file called "MyFile.txt", that’s the file you just wrote. There is a lot more you can do with VB’s file i/o but this is a good start to get you along the right direction.

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I stop the textbox from beeping when I press enter?

There are a couple different ways to solve this problem, and the method to choose is really dependent on what effect you want to accomplish. Choose what you want form the options below.

One-line textbox that doesn’t do anything when the user presses enter.

Sub Text1_KeyPress (KeyAscii As Integer)

If KeyAscii = 13 Then KeyAscii = 0

End Sub

One-line textbox, and a command button on my form to execute when the user presses enter.

Set the command button’s "Default" property to true. This will cause the command button to take the enter key as a click on itself, and the textbox will not beep.

A textbox that moves the cursor down to the next line when enter is pressed.

Set the textbox’s "MultiLine" property to true. This will make the textbox like the windows notepad editing window.

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I use multiple colors in a textbox?

A normal textbox can not support multiple colors. So if you wish to have more then one color in your textbox at a time you have one of two options, use a RichTextBox or use a PictureBox. Below is a small example to get you started for each showing how to add text to them in different colors. Create a form with 2 command buttons, a RichTextBox, and a PictureBox on it. Make sure you keep all standard names or you will have to change the code to reflect your object name changes.

Sub Command1_Click()

RichTextBox1.Text = ""
For x = 0 to 15
RichTextBox1.SelColor = QBColor(x)
‘ set the current color ( you don’t ‘need to use the 16 color qb palette
‘ I’m using, I’m just using it to simplify color selection. )

RichTextBox1.SelText = "Color" & x & vbcrlf
‘ add our new text
Next x

End Sub

Sub Command2_Click()

Picture1.Cls ‘ Clears the PictureBox
For x = 0 To 15
Picture1.ForeColor = QBColor(x) ‘ sets the current color
Picture1.Print "Color" & x ‘ add our new text
Next x

End Sub

- Dave Sherman

[ Return to Table of Contents ]

  1. How do you set tabs in a listbox?

A simple example, in a module declare:

Public Const LB_SETTABSTOPS = &H192
Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd _
As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As _
Long

Then in the sub or function where you are adding to the list box:

Dim rc As Long
Dim tabStops As Long
tabStops = 20
rc = SendMessage(List1.hwnd, LB_SETTABSTOPS, 1, tabStops)
List1.AddItem "This is" & vbTab & "tabbed"

For multiple tabs, make tabStops an array, and set the third (wParam) parameter to SendMessage() to the number of items in this array.

- Guy Thompson

[ Return to Table of Contents ]

  1. How do I find a particular word in a textbox and change it?

Create a new project, on Form1 place a textbox and a command button. Now add the events below. This example will add a phrase to the textbox then will search/replace the word "Peter" with "Bob" when you click the command button

Private Sub Form_Load()

Text1.Text = "Peter Piper picked a peck of pickled peppers. If Peter Piper _
picked a peck of pickled peppers then how many pickled peppers did Peter _
Piper pick."

End Sub

Private Sub Command1_Click()

Dim SearchText As String
Dim FoundPos As Long
Dim ReplaceText As String
Dim EndPos As Long

FoundPos = 1
SearchText = "Peter"
ReplaceText = "Bob"

Do
FoundPos = InStr(FoundPos, Text1.Text, SearchText)
If FoundPos <= 0 Then
Exit Sub
End If
EndPos = InStr(FoundPos, Text1.Text, " ")
Text1.SelStart = FoundPos – 1
Text1.SelLength = EndPos – FoundPos
Text1.SelText = ReplaceText
Loop While InStr(FoundPos, Text1.Text, SearchText) > 0

End Sub

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I scroll a textbox like the mIRC channel window?

Your textbox must have the MultiLine property set to true, and the ScrollBars property set to 2 (show vertical). Use the following in your textbox’s change event.

Private Sub MyTextBox_Change()

MyTextBox.SelStart = Len(MyTextBox.Text)

End Sub

This causes the textbox to scroll to the bottom of the textbox every time the contents of the box changes. There is a somewhat negative sideaffect, however, in that you might be scrolling upwards when text is added, and this code will cause the textbox to jump to the end again. At this time, I know of no way to prevent this side affect, although I am sure there is at least one way to block it.

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I add a VBX/OCX to my project?

VB 5.0 - Select Project->Components from the menu bar for a list.
VB 4.0 - Select Tools->Custom Controls from the menu bar for a list.
VB 3.0 - Select File->Add File from the menu bar, and browse \windows\system for the file you want

- Guy Thompson

[ Return to Table of Contents ]

  1. How do I make something happen at regular intervals?

We will separate this into 3 different intervals. Intervals under 65 seconds apart , intervals over 65 seconds apart, and intervals that occur a certain time during the day. All of these examples use the timer control, just in different ways to accomplish the different effects.

If you wish your procedure to fire every 65 seconds or less, set the timer control’s interval property to the desired number of milliseconds.

60000ms = 60 seconds = 1 minute
30000ms = 30 seconds
01000ms = 01 second

Now in the timer’s event either place your procedure you wish to execute every x amount of seconds or call your procedure from the timer event using the call statement.

If you wish your event to fire at a time greater then 65 seconds you have t do a little fancy footwork. The timer control only allows intervals of a little over 65 seconds. So what you have to do is break up your desired interval into smaller chunks. If you want 100 seconds you could split it up into two 50 ms events, five 20 second events, or ten 10 second events. To keep track of the actual number of times the event has fired you will have to declare a variable in the form’s declare section.

In this example we want our event to fire every five minutes. Create a new project, put a timer control on form1 and set the timer controls interval property to 60000ms.

Form’s Declare Section:

Dim MyTimer As Long ‘ Var keeps track of how many times the event has fired.

Private Sub Timer1_Timer()

MyTimer = MyTimer + 1 ‘ Add to the fire var so we know the event has fired
If MyTimer >= 5 Then ‘ Check to see if this is the 5th time it’s fired (5 mins)
MyTimer = 0 ‘ Reset the var
Call MyProc ‘ Call the procedure we wish to execute
End If

End Sub

Finally, let’s say you wished your event to fire once a day at a given time. In this example set the timer’s interval property to 60 seconds. For this example we will fire the event at 2:25am.

Private Sub Timer1_Timer()

If Hour(Now) = 2 And Minute(Now) = 25 Then
‘ Compare the time against what we are looking for
Timer1.Enabled = False
Call MyProc ‘ Call the procedure we wish to execute
Timer1.Enabled = True
End If

End Sub

- Dave Sherman

[ Return to Table of Contents ]

  1. How do you add/remove items from a listbox?

First thing to remember when using listboxes is that they are zero based. What this means is the items start with an index of 0. So if there were 5 items in the list box they would have indexs of 0-4. If you do not specify an index value when adding items they default to being added onto the end of the list.

To add an item to a listbox use the AddItem method

List1.Clear ‘ Clears the listbox
List1.AddItem ("Foo") ‘ Adds item "Foo" to the listbox at index 0
List1.AddItem ("Foo2") ‘ Adds item "Foo2" to the listbox at index 1

List1.AddItem ("Foo3", 0) ‘ Adds item "Foo3" to the listbox at index 0

In the listbox you will see the items listed in the order of Foo3, Foo, Foo2. Because we specified an index value of zero for Foo3 it was placed in that index and whatever item, Foo in this case, had that value before it will be moved down on the list directly after the newly placed item.

In order to remove an item in a listbox you must first know it’s index. If you know it’s index you can just do a simple.

List1.RemoveItem 0 ‘ Remove item with the index of 0

The above code will remove whatever item has the index of 0. All other items will then shuffle up, item 1 will become 0, item 2 will become 1 and so forth. But what happens if you don’t know an item’s index you just know the data in it.

List1.Clear ‘ Clear the listbox
For x = 0 To 8
List1.AddItem ("Foo" & 0) ‘ Add some items to the listbox
Next x
For x = 0 To List1.ListCount – 1 ‘ Loop through list
‘ remember index 0 counts as an item so the ListCount will always be one
‘ higher then the top boundary. If you have 3 items they are 0-2 not 1-3
If List1.List(x) = "Foo6" Then ‘ Check this items data
List1.RemoveItem x ‘ Data matches, so remove the item
Exit Sub ‘ Exit the procedure because we found what we wanted
End If
Next x

If at all possible try to keep track of the item data/index of that data internally in your program rather then looping through to find what item contains what data.

- Dave Sherman

[ Return to Table of Contents ]

  1. How do you add/remove items from a combobox?
To add to a combo box:

Combo1.AddItem "Item"

To remove from a combo box:

Combo1.RemoveItem Index (0 based)

- Guy Thompson

[ Return to Table of Contents ]

  1. How do you add/remove items from a listview control?
Dim lv As ListItem
Set lv = ListView1.ListItems.Add(, , "item " & 1)

- Guy Thompson

[ Return to Table of Contents ]

  1. How do I turn on full-row-select in a listview control?

The ListView and other common controls found in the COMCTL32.OCX file only come with VB4-32pro or better, so people with VB4-32std and below can safely ignore this one.

By default the ListView control only highlights the word you choose, like in the Explorer's file view. However, many other MS apps use the same ListView and allow the entire line to be highlighted. You turn on/off this ability by using the SendMessage() API function to set an extended style: LVS_FULLROWSELECT

***** 32-bit Sample *****
In a project with 1 ListView, place the following code:

Option Explicit

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal _
lParam As Long) As Long

Private Const LVM_FIRST = &H1000
Private Const LVM_SETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 54
Private Const LVM_GETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 55
Private Const LVS_FULLROWSELECT = &H20

Private Sub Form_Load()

Dim itmX As ListItem
Set itmX = ListView1.ListItems.Add(, , "Sample Person 1")
itmX.SubItems(1) = "29"
itmX.SubItems(2) = "Male"
Set itmX = ListView1.ListItems.Add(, , "Sample Person 2")
itmX.SubItems(1) = "29"
itmX.SubItems(2) = "Female"
Set itmX = ListView1.ListItems.Add(, , "Sample Person 3")
itmX.SubItems(1) = "26"
itmX.SubItems(2) = "Male"
ListViewFullSelect ListView1, True
ListView1.Refresh

End Sub

Private Sub ListViewFullSelect(pcList As Control, bTurnOn As Boolean)

Dim lStyle As Long
Dim lResult As Long
lStyle = SendMessage(pcList.hwnd, _
LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0)
If bTurnOn Then
lStyle = lStyle Or LVS_FULLROWSELECT
Else
lStyle = lStyle And (Not LVS_FULLROWSELECT)
End If
lResult = SendMessage(pcList.hwnd, _
LVM_SETEXTENDEDLISTVIEWSTYLE, 0, lStyle)

End Sub

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do you create controls at run-time?

Visual Basic allows creation and destruction of controls at run-time by means of a control array. As the developer, you must create one control at design time which is a ‘model’ for the control array. Every control in the array will behave exactly the same as the model, but can have different properties. For example, if you create a picturebox control array and create multiple instances of the picturebox at runtime, each picturebox can be in a different location on the form, have a different picture loaded, etc. However, if you design a click event for that picture box which lets the user select a particular picture to be loaded, every instance of the picture box will have the same functionality at run-time.

To create the ‘model’ control, add the control to your form as you would normally, but set the Index property of the control to 0. The Index property (which is normally null) identifies the control as being part of a control array. The index property can be any integer. It is the existence of an index, not the specific value, which defines it as an arrayed control. The index also identifies each unique instance of the control at run-time, as you will see.

When you are using a control array, all methods and events for your control receive an additional parameter, Index. This parameter is used to identify exactly which instance of the control received the event or is being asked to perform a task. When using a control array, you refer to it in the same way you refer to individual elements in a normal array, by placing the index number in parenthesis after the name of the array. MyControl(1), MyControl(2), etc.

At run time, you must track or determine what index values are available before you can load a new instance of a control. If your model control is Index 0, you can not create a second control with Index 0. One method of tracking is to load multiple instances sequentially, and to use a global variable to identify the last index number loaded.

Ok, enough theory. To try out loading and unloading control arrays, try out this example from the Visual Basic help file. (Visual Basic 5.0 Help file topic "Index Property (Control Array)" Example)

This example starts with two OptionButton controls and adds a new OptionButton to the form each time you click a CommandButton control. When you click an OptionButton, the FillStyle property is set and a new circle is drawn. To try this example, paste the code into the Declarations section of a form that has two OptionButton controls, a CommandButton, and a large PictureBox control. Set the Name property of both OptionButton controls to optButton to create a control array.

Private Sub OptButton_Click (Index As Integer) 

Dim H, W ' Declare variables.
Picture1.Cls ' Clear picture.
Picture1.FillStyle = Index ' Set FillStyle.
W = Picture1.ScaleWidth / 2 ' Get size of circle.
H = Picture1.ScaleHeight / 2
Picture1.Circle (W, H), W / 2 ' Draw circle.

End Sub

Private Sub Command1_Click ()

Static MaxIdx ' Largest index in array.
If MaxIdx = 0 Then MaxIdx = 1 ' Preset MaxIdx.
MaxIdx = MaxIdx + 1 ' Increment index.
If MaxIdx > 7 Then Exit Sub ' Put eight buttons on form.
Load OptButton(MaxIdx) ' Create new item in array.
' Set location of new option button under previous button.
OptButton(MaxIdx).Top = OptButton(MaxIdx - 1).Top + 360
OptButton(MaxIdx).Visible = True ' Make new button visible.

End Sub

- Charles Haeberle

[ Return to Table of Contents ]

  1. Working With Files
  1. How do I find out how big a file is?

The FileLen function returns the size of a file in bytes.

Result = FileLen("C:\AUTOEXEC.BAT")

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I delete a file?

The Kill function deletes the file specified.

Kill "C:\MyFile.Txt"

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I rename a file?

The Name function renames a file.

Name "C:\OldFile.Txt" as "C:\NewFile.Txt"

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I copy a file?

To copy a file use the FileCopy functions as shown below.

FileCopy "C:\File.Txt", "C:\FileCopy.Txt"

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I move a file?

Moving a file involved two functions. First copying the file to a new location, then deleting the old file.

FileCopy "C:\File.Txt", "C:\FileCopy.Txt" ‘ Copy file to a new file
Kill "C:\File.Txt" ‘ delete the old file

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I check to see if a file exists?

Checking to see if a file exists is actually quite easy. All that it really entails is using the Dir$ function to check for a file pattern, then check the result to see if it’s the file you want.

In the example below the pattern doesn’t have any wildcards because we know exactly what file we are looking for. As you see I also used the LCase$ function because OPTION COMPARE TEXT might not be on, in which case the capitalization of the saved file would make quite the difference. When changing this code to meet the requirements of your program make sure you keep the filename in lowercase for comparison reasons.

If LCase$(Dir$("c:\autoexec.bat")) = "autoexec.bat" Then
' File Exists
Else
' File Doesn't Exist
End If

For the more advanced programmer, if your program checks multiple files when it runs, you may want to create a function with the above code in it and call it something like FileExists. Have the function set up to receive a filename as an argument, check if the file exists, and return a numerical value that would tell the calling procedure if it does or not. Then just evaluate that return value. In the end it will save you a lot of duplicated code which will in turn slightly increase overall performance.

- Dave Sherman

[ Return to Table of Contents ]

  1. Configuration
  1. How do I read/write INI Files?

For VB3, VB4-32 and VB5, there are the two API functions, Get- and WritePrivateProfileString().
For VB4-16 you can use those API functions or you can use the built-in VB functions Get- and SaveSettings.

An INI file is simply a text file that is formatted in a certain way. There are three parts: application, key and value

[ApplicationName]
Key=Value

***** 16-bit Sample *****
In a project with 1 commandbutton, place the following code:

Declare Function GetPrivateProfileString Lib "kernel" (ByVal lpApplicationName _
As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal _
lpReturnedString As String, ByVal nSize As integer, ByVal lpFileName As String) _
As integer
Declare Function WritePrivateProfileString Lib "kernel" (ByVal _
lpApplicationName As String, ByVal lpKeyName As String, ByVal lpString As _
String, ByVal lpFileName As String) As integer

Sub Command1_Click()

Dim sData As String
Dim iDataLen As integer
' Read from WIN.INI
' [Desktop]
' Wallpaper={path\filename.bmp}
sData = Space$(255)
' Allocate space for the string
iDataLen = GetPrivateProfileString("Desktop", "Wallpaper", "", sData, _
Len(sData), "win.ini")
' strip off the NULL that API routines put on the end of strings
sData = Left$(sData, iDataLen)
' Print the string to the debug window
Debug.Print sData
' Now write to WIN.INI
' [Desktop]
' Test=here is some text
sData = "here is some text"
WritePrivateProfileString "Desktop", "Test", sData, "win.ini"

End Sub

***** 32-bit Sample *****
In a project with 1 commandbutton, place the following code:

Option Explicit
Private Declare Function GetPrivateProfileString Lib "kernel32" Alias _
"GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal _
lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As _
String, ByVal nSize As Long, ByVal lpFileName As String) As Long
Private Declare Function WritePrivateProfileString Lib "kernel32" Alias _
"WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal _
lpKeyName As String, ByVal lpString As String, ByVal lpFileName As String) As _
Long

Private Sub Command1_Click()

Dim sData As String
Dim lDataLen As Long
' Read from WIN.INI
' [Desktop]
' Wallpaper={path\filename.bmp}
sData = Space$(255) ' Allocate space for the string
lDataLen = GetPrivateProfileString("Desktop", "Wallpaper", "", sData, _
Len(sData), "win.ini")
' strip off the NULL that API routines put on the end of strings
sData = Left$(sData, lDataLen)
' Print the string to the debug window
Debug.Print sData
' Now write to WIN.INI
' [Desktop]
' Test=here is some text
sData = "here is some text"
WritePrivateProfileString "Desktop", "Test", sData, "win.ini"

End Sub

Since Microsoft provides a clean interface to reading INI files, most people will use them...however now I'll discuss a method of how to do it without the API.

Paste the following code block into a form with one CommandButton on it. When pressed, the CommandButton will read the entire contents of an ini file into an array and then search for the requested application and key. If found, it will print the value in the debug window; otherwise it will print the given default value.

This is intended as an example to show methods of reading text files and general string manipulation. If you were to write an internal structure to handle this you would want to use a slightly different method to achieve optimum results.

There is no error handling here...In real life, you will run into improperly formatted INI files, problems opening/reading/writing/closing files, etc. so you should examine each step for possible problems.

Private Sub Command1_Click()

Debug.Print MyGetINI("intl", "sCountry", "none", "c:\windows\win.ini")

End Sub

Private Function MyGetINI(ByVal Application As String, ByVal Key As String, _
ByVal Default As String, ByVal lpFile As String) As Variant

Dim hFile As Integer
Dim i As Integer, pos As Integer
Dim sData() As String
Dim sTemp As String
Dim bSectionFound As Boolean
Dim bKeyFound As Boolean
hFile = FreeFile ' allocate a file handle
' Open the file
Open lpFile For Input As hFile
' Loop through the file, ignoring blank lines.
' when done, all data will be in the sData() array.
Do While Not EOF(hFile)
Line Input #hFile, sTemp
If Len(Trim$(sTemp)) > 0 Then
ReDim Preserve sData(0 To i) As String
sData(i) = Trim$(sTemp)
i = i + 1
End If
Loop
' Start the search for the correct application.
' UCase$() is used here to be sure that upper and lower casse does
' not interfere with our search.
For i = 0 To UBound(sData)
If UCase$(sData(i)) = "[" & UCase$(Application) & "]" Then
bSectionFound = True
Exit For ' we found the application, so no need to continue.
End If
Next i
' Now we loop until we either find the key, hit another INI application
' section or reach the end of the array.
If bSectionFound Then
Do
i = i + 1
' Look for the = separator.
pos = InStr(sData(i), "=")
If UCase$(Key) = UCase$(Left$(sData(i), pos - 1)) Then
bKeyFound = True
Exit Do
End If
If i = UBound(sData) Then Exit Do ' no more data, so quit.
Loop While (Left$(sData(i + 1), 1) <> "[")
End If
' If we found the key, we trim off the value and return it
' Otherwise, we return the default value.
If bKeyFound Then
MyGetINI = Mid$(sData(i), pos + 1)
Else
MyGetINI = Default
End If

End Function

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I read/write to the System Registry?

"The registry stores data in a hierarchically structured tree. Each node in the tree is called a key. Each key can contain both subkeys and data entries called values. Sometimes, the presence of a key is all the data that an application requires; other times, an application opens a key and uses the values associated with the key. A key can have any number of values, and the values can be in any form.

Each key has a name consisting of one or more printable ANSI characters — that is, characters ranging from values 32 through 127. Key names cannot include a space, a backslash (\), or a wildcard character (* or ?). Key names beginning with a period (.) are reserved. The name of each subkey is unique with respect to the key that is immediately above it in the hierarchy. Key names are not localized into other languages, although values may be." --Microsoft Win32 SDK

To read and write to the registry, you must use the registry API functions, or for Visual Basic 4 and 5 users, there is a function set called Get- and SaveSettings which save to a specific portion of the registry: (HKEY_CURRENT_USER\Software\VB and VBA Program Settings) If you go the API route you’ll have a little work ahead of you because while the registry is stored in a tree-style format, you access each individual key, sub-key or value, in a fashion which resembles somewhat regular file access. (i.e., open, read and/or write, close.)

I recommend creating these keys and values in the registry with the registry editor (regedt32.exe) before trying to write these yourself if you are unfamiliar or uncomfortable with editing the registry. If you use GetSetting and/or SaveSetting, there is no way to hurt anything important in the registry.

Registry Access using Get- and SaveSetting

‘ Write an entry to:
HKEY_CURRENT_USER\Software\VB and VBA Program Settings\MyApp\Settings\
SaveSetting "MyApp", "Settings", "Left", 10
‘ Read an entry and output to the Debug window.
Debug.Print GetSetting("MyApp", "Settings", "Left", 4)

Registry Access using the Windows API

Option Explicit

Private Declare Function RegOpenKey Lib "advapi32.dll" Alias _
"RegOpenKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, _
phkResult As Long) As Long
Private Declare Function RegQueryValueExStr Lib "advapi32.dll" _
Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName _
As String, ByVal lpReserved As Long, lpType As Long, ByVal lpData As _
String, lpcbData As Long) As Long
Private Declare Function
RegCloseKey Lib "advapi32.dll" (ByVal hKey As _
Long) As Long
Private Const HKEY_CURRENT_USER = &H80000001
Private Const REG_SZ = 1&

Private Sub Command1_Click()

Dim hKey As Long
Dim lRet As Long, lenData As Long
Dim sSubKey As String, sData As String
sSubKey = "Control Panel\Colors\"
‘ Open the specified Key (just a branch at this point.)

lRet = RegOpenKey(HKEY_CURRENT_USER, sSubKey, hKey)
‘ Query a Value in that open key.
lenData = 255
sData = Space$(lenData)
lRet = RegQueryValueExStr(hKey, "ButtonFace", 0, REG_SZ, _
sData, lenData)
‘ Trim off the excess & display the returned value
sData = Left$(lenData)
Debug.Print "The value is: " & sData
‘ Close the opened key.
lRet = RegCloseKey(hKey)

End Sub

- Robert Houghtaling

[ Return to Table of Contents ]

  1. So which is best to use?

There are no hard and fast rules to use when choosing a method for accessing the registry. Microsoft, in choosing to move focus to the registry, is naturally tending lean away from using INI files. INI files can be very convenient though, because they can be accessed much quicker than the registry and with slightly less overhead and work. For small applications, using an INI file (as opposed to the registry) might be a better solution; for example, INI files can be edited with any text editor.

If you plan on widely distributing your application, and you wish to keep multiple versions of your software on one system, with a little planning, using the registry to store user data is very convenient because branches (keys) in the registry are completely separate from each other. If you move from version 4.0 to 5.0 and have an entirely new set of options, those options won’t interfere with the old options, because you’ll be looking in separate areas of the registry.

HKEY_CURRENT_USER
----- Software
---------- MyCompany
--------------- MyProduct
-------------------- 4.0
------------------------- SomeOptions
-------------------- 5.0
------------------------- SomeOtherOptions

- Robert Houghtaling

There has become quite a controversy, in the programming community, over whether or not programmers should use the system registry in their programs. It has turned into a love/hate relationship, either you love the registry or you hate it. In all honesty the System Registry does have it’s use, but most programmers that do use it, overuse it.

The largest controversy over the registry is how much ram it takes up. Currently i have about 2 megs of my ram being eaten up with the configurations settings of programs that aren’t loaded. Why should the configuration settings for programs that are not loaded or haven’t been loaded for quite some time be sitting in your ram taking up memory? It is not at all acceptable to waste memory in this manor.

I find it quite appalling how many companies push off their responsibilities to create the best possible programs they can, and instead go for the quick/easy solution, their programs perform poorly and they just tell the end-users it’s their problem to deal with. Telling them and should buy more ram or a faster computer. Programmers should make better use of the ram, processors, and resources the users have rather then wasting it.

The system registry is a great tool for Multi-User environments, but using it for single user environments or using it to store lots of options is an overkill. Remember the information your program stores in the registry is loaded into the computer’s ram whether or not your program is loaded. The less ram there is free the slower their system and programs will perform, because the more memory swapping and other such tasks the system has to do.

In reality the INI/CFG files are far from dead, more and more programmers are returning to them every day. It is faster, although only slightly depending on the system, to load all of your configuration settings from an ini file then to deal with the declares to and communications with system registry. Plus you save your users a lot of problems on their systems which they will appreciate.

General Rules for Registry Use:

  • For multi-user environments rather then storing all of the configuration settings in the system registry under their name, store one setting which points to the path of an ini file that contains all of the config settings for that particular user.
  • Store program passwords encrypted into the registry. It provides a little more security because most users are quite unfamiliar with it.
  • If you have globally shared options such as the program’s location, installation date, or general data use the registry only if there are a couple of them you wish to save. If there are numerous settings, save your users the memory and store them in an ini file and just point to the ini file that contains all of the global options in the registry.
  • In general save your users the memory and put as much of your configuration settings in ini files as possible so that they are only loaded into memory when needed. 5kb may not seem like much but it adds up fast. As a programmer you have to keep in mind your program isn’t the only program being run on the system and thus have to respect the programmers of the other applications and respect the user of the computer by not having your program horde memory that it doesn’t need. If all programmers respected each other and respected the users by reducing memory required by their apps, then all of the applications on the system would perform better.
  • I have even seen some programs that load over 200+ kb of options into the registry that’s just quite absurd. If you are making an FTP Client or other program that has lots of usernames and passwords store it in a config, data, or password file anything but the registry. There is absolutely no reason such memory should be used up by data for a program that isn’t loaded at the time.

- Dave Sherman

[ Return to Table of Contents ]

  1. Working With Databases
  1. Why shouldn’t I use the Data Control? What should I use then?

According to the VB4.0 help, "you can perform most data access operations using the Data control without writing any code at all. Data-aware controls bound to a Data control automatically display data from one or more fields for the current record or, in some cases, for a set of records on either side of the current record." This is the primary reason many people choose to use the Data control; the learning-curve for this control is very small.

However, the Data control also hides much of the functionality and flexibility of DAO from the programmer. The Data control adds a bit of overhead to your project because you are stuck with the things that the controls designers thought you'd need, which makes the Data control quite a bit slower than the equivalent DAO methods and objects.

If you feel you want the extended functionality and if speed is a concern, then using the DAO methods would be a better choice than the Data control. If you need ease of use and a slightly faster development time (using DAO, after all is going to be a bit harder because you have to write the code yourself) then the Data control will be a better choice.

- Robert Houghtaling

[ Return to Table of Contents ]

  1. Why is there only one record in my recordset?

If you did a recordcount and it says only one record is in your recordset ( When you know there are more. )

let's say you had:

Dim rs As Recordset

then simply do a:

rs.MoveLast

and now your RecordCount will be correct. Don't forget to do a rs.MoveFirst to get back to the first record again.

- Guy Thompson

[ Return to Table of Contents ]

  1. How do I put a picture in my database?

I am unable to find a programmatic method of storing a picture in a jet database using visual basic. The only way I know is to open the database in access, and on a valid OleObject field, open the Insert Object dialog (Edit/Insert Object) and pick the file to insert.

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I add an ODBC data source in code?

The Jet Engine RegisterDatabase method can be used to define new ODBC data sources. For more details, see the Visual Basic help files entry for ‘RegisterDatabase Method’.

- Charles Haeberle

[ Return to Table of Contents ]

  1. Working With Graphics
  1. How do I load/display a picture off my hard drive?

A picture may be loaded and displayed by using a picture box control. Simply add a picture box to your project at design time, set the picture property of the picture box to the filename of the bitmap image you wish to display. Visual Basic 5 now supports JPG and GIF, so you can also use those file types if you are using a VB5 picture box.

- Charles Haeberle

At run time in order to load a picture you must use the LoadPicture Function. You can load a picture into a form, image control, picturebox, or variable. Below is an example of loading a picture named MyPicture.GIF into a picturebox.

Picture1.Picture = LoadPicture("C:\Windows\MyPicture.BMP")

- Dave Sherman

[ Return to Table of Contents ]

  1. How can I put a picture on a button?

Visual Basic 5 command buttons now have both a Picture, DownPicture and DisabledPicture property. Picture identifies the image to be displayed normally (including JPEG and GIFs), and if DownPicture is set, it will be displayed when the button is pressed. DisabledPicture, if set, identifies the graphic to display when the button is not enabled. The Style property of the command button must be set to 1 (Graphical) for these properties to do anything.

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I put bitmaps on menus?

Declare the following Windows API functions and Constants in a module:

Public Const MF_BITMAP = &H4
Public Const CLR_MENUBAR = &H80000004

Const Number_of_Menu_Selections = 3
'changes depending on the number of menu items

Declare Function GetMenu Lib "user32" Alias "GetMenu" (ByVal hwnd As Long) _
As Long
Declare Function GetMenuItemID Lib "user32" Alias "GetMenuItemID" (ByVal _
hMenu As Long, ByVal nPos As Long) As Long
Declare Function ModifyMenu Lib "user32" Alias "ModifyMenuA" (ByVal hMenu _
As Long, ByVal nPosition As Long, ByVal wFlags As Long, ByVal wIDNewItem _
As Long, ByVal lpString As String) As Long
Declare Function SetMenuItemBitmaps Lib "user32" Alias "SetMenuItemBitmaps" _
(ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long, ByVal _
hBitmapUnchecked As Long, ByVal hBitmapChecked As Long) As Long

The following code shows how to use both static and dynamic bitmaps for the menu.

Design a menu system using the Menu Design window such as the following:

Caption Control Name Indented Index
BitMenu Top Menu No  
Sub Menu0 SubMenu Once 0
Sub Menu1 SubMenu Once 1
Sub Menu2 SubMenu Once 2

Now create a picture control array with three bitmaps by creating three picture controls with the same control Name using the Properties list box.

Control Name Caption Index FontSize Comment
Picture1   0 N/A  
Picture1   1 N/A  
Picture1   2 N/A  
Picture2   N/A N/A check BMP
Picture3   0   Set Picture3
Picture3   1 9.75 FontSizes
Picture3   2 18 all different
Command1 Static      
Command2 Dynamic      

For each control index of Picture1, add a valid bitmap to the Picture property. Because these bitmaps will be displayed in the menu, you should use smaller bitmaps. Add a bitmap to the Picture2 Picture property that you want to be your check mark when you select a menu option.

And finally add the following code to your project:

Sub SubMenu_Click (Index As Integer)

' Uncheck presently checked item, check new item
Static LastSelection As Integer
SubMenu(LastSelection).Checked = False
SubMenu(Index).Checked = True
LastSelection = Index

End Sub

Sub Command1_Click ()

Dim i As Integer
Dim x As Long
Dim hMenu As Long
Dim hSubMenu As Long
Dim menuId As Long

'to create a static bitmap menu
hMenu = GetMenu(Me.hWnd)
hSubMenu = GetSubMenu(hMenu, 0)
For i = 0 To Number_of_Menu_Selections - 1
menuId = GetMenuItemID(hSubMenu, i)
x = ModifyMenu(hMenu, menuId, MF_BITMAP, menuId, _
CLng(picture1(i).Picture))
x = SetMenuItemBitmaps(hMenu, menuId, 0, 0, _
CLng(picture2.Picture))
Next

End Sub

Sub Command2_Click ()

Dim i As Integer
Dim x As Long
Dim hMenu As Long
Dim hSubMenu As Long
Dim menuId As Long

'to create a dynamic menu system
hMenu = GetMenu(Me.hWnd)
hSubMenu = GetSubMenu(hMenu, 0)
For i = 0 To Number_of_Menu_Selections - 1
'Place some text into the menu.
SubMenu(i).Caption = Picture3(i).FontName & _
Str$(Picture3(i).FontSize) + " Pnt"
'1. Must be AutoRedraw for Image().
'2. Set Backcolor of Picture control to that of the
' current system Menu Bar color, so Dynamic bitmaps will appear
‘ as normal menu items when menu bar color is changed via the
‘ control panel
'3. See the bitmaps on screen, this could all be done at design time.
Picture3(i).AutoRedraw = True
Picture3(i).BackColor = CLR_MENUBAR
' You can uncomment this 
' Picture3(i).Visible = False
' Set the width and height of the Picture controls based on their
‘ corresponding Menu items caption, and the Picture controls
‘ Font and FontSize.
'DoEvents() is necessary to make new dimension
'values to take affect prior to exiting this Sub.
Picture3(i).Width = Picture3(i).TextWidth(SubMenu(i).Caption)
Picture3(i).Height = Picture3(i).TextHeight(SubMenu(i).Caption)
Picture3(i).Print SubMenu(i).Caption
'Set picture controls backgroup picture (Bitmap) to its Image.
Picture3(i).Picture = Picture3(i).Image
x = DoEvents()
Next
'Get handle to forms menu.
hMenu = GetMenu(Me.hWnd)
'Get handle to the specific menu in top level menu.
hSubMenu = GetSubMenu(hMenu, 0)
For i = 0 To Number_of_Menu_Selections - 1
'Get ID of sub menu
menuId = GetMenuItemID(hSubMenu, i)
'Replace menu text w/bitmap from corresponding picture control
x = ModifyMenu(hMenu, menuId, MF_BITMAP, menuId, _
CLng(Picture3(i).Picture))
'Replace bitmap for menu check mark with custom check bitmap
x = SetMenuItemBitmaps(hMenu, menuId, 0, 0, _
CLng(picture2.Picture))
Next

End Sub

- Guy Thompson

[ Return to Table of Contents ]

  1. How can I tile a bitmap?

There are two ways to do this, one being internally in vb and the other being with the Windows API. VB4’s internal structures to do this by far out benchmarked the API equivalent. I haven’t had time to benchmark it in VB5 but odds are with VB5 it’s even faster then it was in VB4.

In this example create a new project. On form1 place a picturebox. Set the picturebox’s picture property to a picture file, for now keep it simple just refer it to like WINDOWS\TILES.BMP later you can change it to whatever.

Private Sub Form_Load()

' Prepare the form and picturebox
Form1.ScaleMode = 3
Form1.AutoRedraw = True
Picture1.ScaleMode = 3

' Get dimensions
Dim FormHeight As Long
Dim FormWidth As Long
Dim PictureHeight As Long
Dim PictureWidth As Long

' Assign dimensions
FormHeight = Form1.ScaleHeight
FormWidth = Form1.ScaleWidth
PictureHeight = Picture1.ScaleHeight
PictureWidth = Picture1.ScaleWidth

' Tile bitmap
For y = 0 To FormHeight Step PictureHeight
For x = 0 To FormWidth Step PictureWidth
Form1.PaintPicture Picture1.Picture, x, y
Next x
Next y

End Sub

- Dave Sherman

[ Return to Table of Contents ]

  1. How can I tile a bitmap on a MDI form?

In this example create a new project. Create a MDI form and set it to be your startup form. Place a picturebox so it docks into the top part of the mdiform. Now set the mdiform’s picture property to a picture file, for instance WINDOWS\TILES.BMP.

Private Sub MDIForm_Load() 

' Prepare form
Picture1.AutoRedraw = True
Picture1.Visible = False

' Get dimensions
Dim FormHeight As Long
Dim FormWidth As Long
Dim PictureHeight As Long
Dim PictureWidth As Long

' Assign dimensions
FormHeight = MDIForm1.Height
FormWidth = MDIForm1.Width
PictureHeight = Picture1.ScaleX(MDIForm1.Picture.Height, 8, 1)
PictureWidth = Picture1.ScaleY(MDIForm1.Picture.Width, 8, 1)

'Resize picturebox
Picture1.Height = MDIForm1.Height

' Create a new tiled form of the bitmap
For y = 0 To FormHeight Step PictureHeight
For
x = 0 To FormWidth Step PictureWidth
Picture1.PaintPicture MDIForm1.Picture, x, y
Next x
Next y

‘ Copy our new bitmap to the back of the MDIFrom
MDIForm1.Picture = Picture1.Image

End Sub

- Dave Sherman

[ Return to Table of Contents ]

  1. Working With Multimedia
  1. How do I play an animation?

Playing a video on your form isn’t as difficult as it looks. Place one PictureBox on your form and size it to approximately the size of the animation you wish to play. Also put a CommandButton and a Multimedia Control on your form. I added the following code to a form: (choose an AVI file from your HDD)

Private Sub Command1_Click()

With MMControl1
.hWndDisplay = Picture1.hWnd
.filename = "c:\movies\myvideo.avi"
.DeviceType = "AVIVideo"
.Command = "open"
.Command = "play"
End With

End Sub

When you click the command button, the video (with sound, if there is any) should play in the PictureBox. If the picturebox is too small or too large, resize it to your needs at design-time.

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I play Midis?

There is an API function, documented by MS on their knowledge base, called mciSendString which allows you to play MIDI files synchronously and asynchronously.

Unfortunately, VB does not allow callbacks without an external OLE control, so there is no way for the multimedia subsystem to notify you when a particular .MID file is done playing. This means that you can play a MIDIfile once and pause until it finishes or you can start the file playing and continue immediately, but never actually know when it finishes.

Private Declare Function mciSendString Lib "winmm.dll" Alias _
"mciSendStringA" (ByVal lpstrCommand As String, _
ByVal lpstrReturnString As String, ByVal uReturnLength As Long, _
ByVal hwndCallback As Long) As Long

Dim ret As Long

ret = mciSendString("open c:\midi\myfile.mid type sequencer alias myfile", 0&, 0, 0)
ret = mciSendString("play myfile wait", 0&, 0, 0)
ret = mciSendString("close myfile", 0&, 0, 0)

An alternative is to use the Microsoft Multimedia Control. With it you can play AVI files, MIDI files, Wave files, etc.

In the example below, I added one Multimedia Control and one command button to a form. Then I added the following code (choose a MIDI file from your HDD) …when the button is clicked, the MIDI file will start to play and will repeat until the program is closed. It does this by using the MMControl's _Done event.

Private Sub Command1_Click()

With MMControl1
.filename = "c:\midi\myfile.mid"
.DeviceType = "sequencer"
.Command = "open"
.Command = "play"
End With

End Sub

Private Sub MMControl1_Done(NotifyCode As Integer)

If NotifyCode = 1 Then ' Successful
MMControl1.Command = "play"
End If

End Sub

Private Sub Form_Unload(Cancel As Integer)

MMControl1.Command = "Close"

End Sub

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I play Waves?

The easiest way I have found to play .WAV files is to use the API function sndPlaySound().

Private Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" _
(ByVal lpszSoundName As String, ByVal uFlags As Long) As Long
Private Const SND_ASYNC = &H1

sndPlaySound "c:\windows\chime.wav", SND_ASYNC

There are many other options to playing wave files, including looping, synchronous playing, asynchronous playing, playing from memory and playing system wave sounds like SystemExit.

If you would like to play a wave file using the Microsoft Multimedia Control, drop one a form along with a command button and add this code (choose a wave file from your HDD):

Private Sub Command1_Click()

With MMControl1
.filename = "c:\waves\mywave.wav"

.DeviceType = "waveaudio"
.Command = "open"
.Command = "play"
End With

End Sub

- Robert Houghtaling

[ Return to Table of Contents ]

  1. Miscellaneous
  1. How do I generate random numbers?

From the Visual Basic 5.0 Help file:

The Rnd function returns a value less than 1 but greater than or equal to zero. The value of number determines how Rnd generates a random number:For any given initial seed, the same number sequence is generated because each successive call to the Rnd function uses the previous number as a seed for the next number in the sequence. Before calling Rnd, use the Randomize statement without an argument to initialize the random-number generator with a seed based on the system timer.

To produce random integers in a given range, use this formula:

Int((upperbound - lowerbound + 1) * Rnd + lowerbound)

Here, upperbound is the highest number in the range, and lowerbound is the lowest number in the range.

Note To repeat sequences of random numbers, call Rnd with a negative argument immediately before using Randomize with a numeric argument. Using Randomize with the same value for number does not repeat the previous sequence.

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I center a form on the display?

Visual Basic 5 now provides a StartUpPosition property for all forms. By setting the StartUpPosition property, you can have the form automatically be centered. StartUpPosition may be one of four values:

vbStartUpManual 0 No initial setting specified.
vbStartUpOwner 1 Center on the item to which the UserForm belongs.

vbStartUpScreen 2 Center on the whole screen.
vbStartUpWindowsDefault 3 Position in upper-left corner of screen.

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I pause a program?

If you want to pause for a certain amount of time, the best way is to use the Sleep() API function.

Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)

Call Sleep(5000)

This will pause your program for 5 seconds, while still allowing other windows events to continue.

- Robert Houghtaling

Personally I don’t much care for the Sleep() API Function because it completely freezes your application, not allowing for any part of the program to function while paused. What this means? Well it means that your program won’t be updated visually.. if another program covers it and then uncovers it you’ll see pretty much nothing but a white box if that because the program can’t refresh it’s screen or the way it looks. What else this means is the user can’t click or do anything to get the program to unpause or to perform some other task. Also if you have any timers or any routines that are supposed to function on a certain timed basis like a program event of some sort it can also not execute.

The way I get around this is using my own pause techniques. In Visual Basic, when you call one function while another is in execution they new one will take precedence and will execute not returning control to the original one until the new function is done. In the example below, I made compensation for if it goes over the midnight mark once, and I used an integer for the pause seconds because there is no reason why you should ever need more then a 32,767 second pause during a procedure's execution.

Public Sub PauseProg(PauseTime As Long)

StartTime = Timer
If StartTime + PauseTime > 86399 Then
PauseTime = StartTime + PauseTime - 86400
Do While Timer <= 36399
DoEvents
Loop
Do While Timer < PauseTime
DoEvents
Loop
Else
Do While Timer < StartTime + PauseTime
DoEvents
Loop
End If

End Sub

Just as a note many programs that report the processor in use will report back saying that 100% processor is in use when using this pause technique. Our program is the program being credited with all of that processing time, this is rather misleading however because well over 90% of the processor usage in our routine is being pumped right back into the os for it to do with as it wants and for it to handle it’s events. So don’t pay that much attention it’s the monitors that are detecting processor usage can’t compensate for that.

You can now pause your program by doing a call to this routine.

Call PauseProg(20) ‘ Would pause the program for 20 seconds

A small word of warning, you have to be careful when using this technique because it uses the DoEvents Function. What I mean by this is there are some events you won’t want executed while pausing in this method. For example if you execute this in a click event of a button you don’t want the same button pushed during this time. A couple ways to get around this is to make a global variable that you set in the pause routine and check for at the beginning of the click event routine. If the variable has a value you would have the click event automatically exit the sub returning control back to the pause routine. If this sounds to complicated just set the buttons you don’t want the user to click on, while this is executing, to false.

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I run a program from VB?

To launch another application from Visual Basic you use the Shell statement.

Shell "program name", 1

The one problem with the Shell statement is that unless the file is in the path, you need to include it with the call to shell.

An alternative is the ShellExecute API function. With ShellExecute, you can launch everything from programs to text files to internet web pages.

Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, _
ByVal nShowCmd As Long) As Long

' Launch notepad.exe
ShellExecute Me.hWnd, "Open", "notepad.exe", "", "", 1

' Launch win.ini with the default editor
ShellExecute Me.hWnd, "Edit", "win.ini", "", "", 1

' Launch www.microsoft.com with the default WWW browser.
' Notice you do not need the http:// qualifier.
ShellExecute Me.hWnd, "Open", "www.microsoft.com", "", "", 1

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I pause until it’s done?

Now here is where it starts to get complicated, so pay close attention. There are, by necessity, two different methods to pause a program until a shelled process completes; one for 16-bit Windows and one for 32-bit Windows.

There are two comprehensive Microsoft Knowledge Base articles that deal with this task.

16-Bit Article (this article will need a little adjustment to work as a pause routine. It uses the VB Shell statement to launch an app and then immediately displays the window caption of the shelled app. You simply loop at that point, testing for the existence of the shelled app’s window. All the code necessary to do so is included with the KB article)

How to Find a Window Handle Based on an Instance Handle
http://www.microsoft.com/kb/articles/q127/0/30.htm

32-Bit Article

How a 32-Bit App Can Determine When a Shelled Process Ends
http://www.microsoft.com/kb/articles/Q129/7/96.htm

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I shut it down now that it’s done?

To preemptively shut down a shelled app in 32-bit Windows, you should use the CreateProcess() API function documented in the 32-bit Knowledge Base article above to shell to the application and then use the TerminateProcess() API function to shut that process down.

Dim rVal As Long
rVal = TerminateProcess(hProcess, 0)

hProcess is a Long value representing a handle to a process that was returned from the CreateProcess() API function.

Note: at this time, I have no firm documentation on shutting down external 16-bit applications (other than the remote possibility of using Visual Basic’s SendKeys statement to send the Alt-F4 keystroke combination…which is chancy at best)

A side issue here is how to shell to a DOS application and have it shut down the Finished DOS window when the application has finished. To do this, specify "/C" on the command line to which you are shelling:

h = Shell("COMMAND.COM /C <pathname>")

By default, Windows95 leaves finished DOS windows open, waiting for you to close them. This will bypass that procedure and close the DOS window for you.

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How can I use a variable on more then one form?

In order to create a variable so it can be accessed and modified by all of your forms, you must first add a module to your project. In the declaration section of the module, VB4 & VB5, you must declare the variable as a "Public" variable. Below are a few different global declare examples.

Public MyStr As String
Public MyInt As Integer
Public Temp As Variant

- Dave Sherman

[ Return to Table of Contents ]

  1. Is there any way to pass a variable to another form apart from using global variables?

On the target form, place a text box, label, or any other control that you prefer and set that control's visible property to False. Then place the value of your variable in the text (or caption, etc.) property of this control. Another method is to use the .Tag property which is never used by VB itself. You can place any string you want into the .Tag property of a form or control.

- Guy Thompson

[ Return to Table of Contents ]

  1. Why does my program not close correctly when the user closes it? ( It still shows up when I press Ctrl-alt-del. )

This occurs when some part of the program is still running or loaded. For instance if you have two forms, you hide the first and close the second. While you see nothing the first form is still there just invisible. Same goes for modules, modules operate without a visible form so just because you see nothing doesn’t mean it’s not loaded or processing something.

The way to correct this is to use the end statement. The end statement will terminate the entire program and unload it from memory. On a quit button or in your main forms unload event place the statement

End

That one little word will completely terminate your program and unload all of it’s components.

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I change the size of an array?

Create a new project and on Form1 place a command button. Now add the below code to the form.

Private Sub Command1_Click()

Dim FooBar() As String ‘ Declare our array open ended
ReDim FooBar(1 To 15) ‘ Give our array boundries
For x = LBound(FooBar) To UBound(FooBar)
FooBar(x) = "I Am #" & x
‘ Loop through and assign each item in our array a value
Next x
ReDim FooBar(1 To 16) ‘ Redimension our array’s boundries
FooBar(16) = "I Am #16" ‘ Give our new item a value
For x = LBound(FooBar) To UBound(FooBar)
Debug.Print x & ": " & FooBar(x)
‘ Loop through and print all the items to the debug box
Next x

End Sub

When you click on the button you see a bunch of blank spaces being listed for items 1-15 and then our actual value we assigned listed for 16. This is because when we redimensioned our array we didn’t’ preserve all the existing data in it, so all the data was lost. Since we added the data for 16 after we changed the array’s size it was never erased. See the following question if you wish to keep all the data in your array when you resize it.

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I change the size and not lose my data?

When you change the size of an array, you normally lose all of the data in it. However, VB has a keyword, called Preserve, that you can add to your ReDim statement so that you may keep all your data and only resize the actual array. Create a new project and on Form1 place a command button, then insert the code below.

Private Sub Command1_Click()

Dim FooBar() As String ‘ Declare our array open ended
ReDim FooBar(1 To 15) ‘ Give our array boundries
For x = LBound(FooBar) To UBound(FooBar)
FooBar(x) = "I Am #" & x
‘ Loop through and assign each item in our array a value
Next x
ReDim Preserve FooBar(1 To 16)
‘ Redimension our array’s boundries preserving the data contained within
FooBar(16) = "I Am #16" ‘ Give our new item a value
For x = LBound(FooBar) To UBound(FooBar)
Debug.Print x & ": " & FooBar(x)
‘ Loop through and print all the items to the debug box
Next x

End Sub

When you run it this time the output lists all the items with their data still intact. So the key to remember here is when you want to keep the data in all your items make sure you use the Preserve keyword in the ReDim statement.

- Dave Sherman

[ Return to Table of Contents ]

  1. How to find the path of the executable?

To find the path of the application use the path property of the app.

Debug.Print App.Path

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I call a help file from my program?

Most professional programs have an on-line help option on one of their pull down menus. There are a few ways to do this from using the common controls, to the API, to the simple shell command. Personally i prefer the shell command because it’s the fastest of the 3 doesn’t require any extra dlls/ocxes or ram allocation for the call.

Private Sub HelpMe_Click()

RetVal = Shell("winhelp.exe " & App.Path & "\HELPFILE.HLP", 3)

End Sub

- Dave Sherman

[ Return to Table of Contents ]

  1. Why does my program say wrong version of runtime DLL?

We have only seen this problem with VB 4. With Visual Basic 4.0 the runtime dll names were VB40032.DLL and VB40016.DLL. Microsoft then released an update version that many developers upgraded to, Visual Basic 4.0a. The problem occured when Microsoft kept the dll names the same. They strived very hard to keep the DLL backwards compatible, in terms of the VB4.0 programs could use the VB4.0a DLL without a problem. What they didn’t take into consideration was that programs when installed would overwrite the 4.0a version with the 4.0 version since they were the same name. Now while 4.0 programs could use the 4.0a dll it was not the other way around, 4.0a programs couldn’t use the 4.0 dll because the structures were slightly changed. So if you get this error message the solution is to replace the VB400xx.DLL in your WINDOWS/SYSTEM dir with the 4.0a version.

- Dave Sherman

[ Return to Table of Contents ]

  1. Why can't my friend run the program I wrote?

One of major disadvantages of using a bunch of external custom controls or references in your program, is that your program will need all of them when you distribute it. Many new programmers overlook that all dlls and ocxes you use in your program might not be on the end user’s computer. This is why when you distribute your application you should probably include all of the dlls and ocxes with it just incase the user don’t have them.

Merley including some of them isn’t good enough in some cases though. Some ocxes and dlls require registration in the system registry before they can be used. There are a couple ways to register them but the easiest way is to create a setup program that will install and register the dlls and ocxes.

Visual Basic comes with a program called Setup Wizard that creates custom installations. While it does work it has a very bad habit of including MANY dlls that just aren’t needed. For those that need a better solution then what Setup Wizard provides you may with to make your own installation program or use a commercial program such as Wise, or Install Shield.

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I copy/retrieve things to/from the clipboard?

Using the clipboard is very simple. There are four basic methods you will use. For working with text, those methods are SetText and GetText, and for binary data, SetData and GetData. SetText and GetText are the easiest of the two. The following example copies a simple string to the clipboard, then puts that clipboard text into a text box.

Clipboard.SetText "Hello World"
MyTextBox.Text = Clipboard.GetText

Working with binary data is a bit more complicated, because the clipboard can support different data formats. Consult the Visual Basic Help files on the two topics, SetData and GetData, for an overview of use of the clipboard for working with binary data.

- Charles Haeberle

[ Return to Table of Contents ]

  1. When I use the IIF function, I am getting errors in my program when I give the program to a friend...why?

Microsoft placed financial functions in a separate DLL called, MSAFINX.DLL. If you include that dll with your source you shouldn't have any problems. Why is IIF considered a financial function? Who knows?...if you find an answer tell me...

Personally, I do not use IIF because it is slow (extremely, when compared to the equivalent IF statement) and I don't really want to give out another DLL.

- Robert Houghtaling

[ Return to Table of Contents ]