Sie sind auf Seite 1von 415

Amazing VB Programs

1: Determining If a File Already Exists


Abstract
The Windows® function OpenFile provides a reliable method of determining whether or
not a specific filename or path name exists.
The information in this article applies to:

• Standard and Professional Editions of Microsoft® Visual Basic® for Windows,


versions 2.0 and 3.0.

• Microsoft Visual Basic programming system for Windows, version 1.0.

OpenFile Function
The OpenFile function can be used to perform several file operations, such as creating a
new file, deleting a file, or determining if a file exists. To declare this function within
your program, include the following Declare statement in the Global Module or General
Declarations section of a Visual Basic® for Windows® form:
Declare Function OpenFile% Lib "Kernel" (ByVal lpFileName$, lpReOpenBuff
As OFSTRUCT, ByVal wStyle%)
Note that this Declare statement must be typed as one single line of text.
The OpenFile function takes the following arguments, described as follows:
Argument Description
lpFileName A string containing the name, which may or may not include a path
name, to test.
lpReOpenBuff An OFSTRUCT structure that will contain information about the file
after the OpenFile function has been called.
wStyle This combination of one or more flags specifies the type of operation
that is to be performed on the file.

In our demonstration program, we need only specify wStyle as the constant OF_EXIST.
After calling the OpenFile function, an integer value is returned. If a negative number is
returned, the nErrCode value in the OFSTRUCT structure can be examined to find out if
the file exists already.

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The following program demonstrates how to determine if a file already exists.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Global Module:


3. 'OFSTRUCT structure used by the OpenFile API function
4. Type OFSTRUCT '136 bytes in length
5. cBytes As String * 1
6. fFixedDisk As String * 1
7. nErrCode As Integer
8. reserved As String * 4
9. szPathName As String * 128
10. End Type
11. Within the Global Declaration section of Form1, add the following Windows
function declaration (note that this Declare statement must be typed as one single
line of text):
12. Declare Function OpenFile% Lib "Kernel" (ByVal lpFileName$, lpReOpenBuff As
OFSTRUCT, ByVal wStyle%)
13. Next, add the following lines of code to the Global Declaration section of Form1:
14. Dim wStyle As Integer
15. Dim Buffer As OFSTRUCT
16. Dim IsThere As Integer
17. Dim TestFile As String
18. Add the following code to the Form_Load() event procedure:
19. Sub Form_Load()
20. TestFile = "c:\testfile.dat"
21. IsThere = OpenFile(TestFile, Buffer, OF_EXIST)
22. If IsThere < 0 Then
23. GoTo CheckForError
24. Else
25. Debug.Print "This file already exists"
26. End If
27. CheckForError:
28. IsThere = Buffer.nErrCode
29. If IsThere = 3 Then
30. Debug.Print "Pathname not found"
31. End If

2: Detecting the Cancel Button When Using


the Common Dialog Box
Abstract
To give your programs a professional look and to make them consistent with other
Microsoft® Windows®-based applications, Visual Basic® provides the common dialog
control. This control allows you to include standard dialog boxes (Open, Save As,
Printer, Font, Color and Help) in your application programs.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


When the dialog box is displayed, users can make various selections, depending on which
dialog box is displayed. After users make their decisions, they click the OK command
button, which returns control to your application program. Your program can then
retrieve the specific values the users specified in the dialog box and use these in your
program to perform some action.

However, if a user clicked the Cancel command button in the dialog box, your program
must be able to process this condition appropriately so that special error conditions can be
monitored. The CancelError property of the dialog box provides a method of error-
checking in your applications.

Common Dialog Box Control


Using the common dialog box control is straightforward. It provides easy access to six of
the most commonly used dialog boxes. Setting the Action property of the common dialog
control tells Visual Basic which dialog box you want to display in your application. As
shown below, there are six dialog boxes that are considered to be standard controls.
DLG_FILE_OPEN Open
DLG_FILE_SAVE Save As
DLG_COLOR Color
DLG_FONT Font
DLG_PRINT Print
DLG_HELP Help

Let's assume that you want to include the Color dialog box in your own application
program. First, you would draw a common dialog control on your form. When your
program is executed, the common dialog control is invisible. Therefore, you can place the
control anywhere on your form because it won't interfere with your program's
appearance. Also, note that you need only place one common dialog control on your form
to call any or all of the six dialog boxes—you don't need a separate control for each type
of dialog box you want to display.
Next, you set the Action property of the common dialog control to specify which dialog
box you want to use. If you want to call up the Color dialog box, for example, you would
set the Action property to a value of 3.
When you execute your application program, you set values for the various properties of
the dialog box you are using. Once the Action property statement is executed at run time,
the dialog box will be displayed on your form. After the user has clicked the dialog box's
OK command button, your program regains control and you can test the various options
the user selected in the dialog box.
To prevent errors from occurring in your application, such as specifying a nonexistent
color in the Color dialog box, you can use the CancelError property. This property lets
you know if the user clicked the Cancel button on the dialog box. Each of the six dialog

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


boxes uses the CancelError property. The CancelError property lets you set a trap for
the Cancel button. When this property is set to True, Visual Basic generates an error
(CDERR_CANCEL or 32755) that you can trap in your program. If CancelError is set to
False, no error occurs—the dialog box simply closes and returns a NULL value.

Example Program
The following program demonstrates how you can set a trap to find out if the user pressed
the Cancel button of a dialog box.

1. Start a new project. Form1 is created by default.

2. Draw a common dialog control on Form1.

3. Draw a command button on your form. Command1 is created by default.

4. Add the following lines of code to the click event of Command1:


5. Sub Command1_Click()
6. CMDialog1.CancelError = True
7. On Error GoTo ErrButton
8. CMDialog1.Flags = CC_RGBINIT Or CC_FULLOPEN
9. CMDialog1.Action = 3
10. On Error GoTo 0
11. Form1.BackColor = CMDialog1.Color
12. ErrButton:
13. Exit Sub
This program displays a single command button on your form. When you click this
command button, the Color dialog box is displayed. You can select a new color for the
form's background color and press the OK command button. In this case, the color of
Form1 will be changed to the color you selected.
On the other hand, if you click the Cancel button in the Color dialog box, the program
does nothing. The trap set by CancelError allows your application to detect when the
Cancel button is pressed and to process this condition accordingly.

3: Retrieving the Name of the Temporary


Directory
Abstract
Starting with version 2.0 of MS-DOS®, the operating system uses an environment block
to store information about the user's computer system. The environment block consists of
one or more ASCIIZ strings (strings terminated with a NULL character) that provide
information to MS-DOS or application programs about the operating system. One entry
in the environment block, for example, tells MS-DOS where to find files (for example,
the PATH statement, included in the AUTOEXEC.BAT file). This article explains how

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


you can retrieve a specific variable from the environment block from within your own
Visual Basic® application.

Retrieving the Name of the Temporary Directory


MS-DOS® and most applications you execute use the SET statement in the
AUTOEXEC.BAT file to store their variables in the environment. When you installed
Visual Basic®, you may have specified that it is to use a directory on drive C called
JUNK to store its temporary working files. In that case, you would insert the statement
SET TEMP=C:\JUNK in your AUTOEXEC.BAT file. From now on, each time Visual
Basic needs to create a temporary file, it will look for the variable TEMP in the
environment and use the specified directory to store the files it needs to create.
The ENVIRON statement in Visual Basic allows you to retrieve a string that is
associated with a variable stored in the MS-DOS environment. The ENVIRON statement
has the following syntax:
Environ[$](environmentstring)

where "environmentstring" is a string expression that contains the name of the


environment variable you want to retrieve.
We can use the ENVIRON statement in our own program to retrieve the name of the
directory used by Visual Basic for its temporary files. To do this, simply add the
following statement to a code module in your Visual Basic program:
D$=Environ$("TEMP")
This statement tells Visual Basic to assign the value of the variable TEMP to D$. The
environment variables are always returned as uppercase characters, despite the fact that
they may have been entered as lowercase. In addition, the string returned in D$ will be
terminated with a NULL character. If you ask the ENVIRON statement to return a string
associated with a variable that does not exist, an empty string will be returned.

4: Adding Access Keys to Controls That


Don't Have a Caption Property
Abstract
When you assign an access key to a control, you allow your user to move quickly
between one control and another control on the same form. This article tells you how to
assign access keys in Visual Basic®.

Assigning Access Keys


In Visual Basic®, a user presses the ALT key and the access letter character at the same
time to move the focus to the designated control. Access keys can be assigned to a
control through the control's Caption property. If, for example, the Caption property is

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


set to the text "Name", you can assign an access key to the control by specifying the
Caption property as "&Name". The ampersand character informs Windows® that the
ALT key plus the (in this case) "N" key will move the focus to this control. The access
key is displayed underlined in the control's caption when your application is executed.
The catch is that access keys can only be assigned to controls that have a Caption
property.
There is, however, a technique you can use to quickly move the focus to a control that
does not have a Caption property.

When you place controls on a form, the TabIndex property determines which control
will receive the focus next. The control with the next highest TabIndex value
automatically receives the focus.
You can place a Label control, which has a Caption property, in front of a control that
cannot receive the focus, such as a Text Box. Because a Label control cannot receive the
focus, the next control in the TabIndex order receives the focus.
Let's assume that you want to assign an access key to a Text Box. First, you would draw
a Label control on your form and set its Caption property so it is assigned an access key,
for example, &Name. Next, you would add a Text Box control beside or near the Label
control. Make sure the TabIndex value of the Text Box is one greater than that of the
Label control.
When your application program is running, you can move the focus to the Text Box by
pressing the access key assigned to the Label control.
You must remember that this technique will only work as long as the TabIndex property
of the Label control is always one less than the control you want to actually move the
focus to.

5: Removing the Desktop Wallpaper


Abstract
Within your own application, you can change various Windows® settings that are usually
modified by using the Control Panel. One of these settings allows you to change the
Desktop wallpaper. The SystemParametersInfo function can be used to select a new
wallpaper for the Desktop from within your application.

The SystemParametersInfo function can also be used to remove the Desktop wallpaper
so that no wallpaper will be used at all.

How to Remove the Desktop Wallpaper


The following code shows how to remove the Desktop wallpaper and set the default to
None.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Create a new Visual Basic project. Form1 is created by default. Add a command
button to the form (Command1) and set its Caption property to "Remove
Wallpaper".

2. Add the following code to the general declarations section of Form1:


3. Const SPIF_UPDATEINIFILE = &H1
4. Const SPI_SETDESKWALLPAPER = 20
5. Const SPIF_SENDWININICHANGE = &H2
6. Next, add the following Declare statement to the General Declarations section
(type this statement as one single line of text):
7. Declare Function SystemParametersInfo Lib "User" (ByVal uAction As Integer, ByVal uparam
As Integer, ByVal lpvParam As String, ByVal fuWinIni As Integer) As Integer
8. Add the following code to the Command1_Click event:
9. Sub Command1_Click()
10. filenm$ = "(none)"
11. x% = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0&, filenm$,
SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)
12. End Sub
After executing this program, the current wallpaper should be removed from the Desktop.

6: Displaying a File's Contents Correctly in a


Text Box
Abstract
When trying to display text in a multiline text box, the carriage return and linefeed
characters pose a special problem. Microsoft® Windows® version 3.0 deletes the
character following Chr$(13), while version 3.1 substitutes a pipe "|" character for the
Chr$(13) character. This poses a problem when reading ASCII data from a file into a text
box.

Wrapping Lines in Text Boxes Correctly


To alleviate missing or replaced characters when reading ASCII data from a file, you
must add both the linefeed and the carriage return characters to the end of each line of
text you process.

The following program retrieves the contents of the AUTOEXEC.BAT file and displays
it in a multiline text box.

Example Program
1. Start a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add a text box control to Form1. Set its MultiLine property to True and its
ScrollBars property to 3-Both.

3. Add the following code to Form1's Form_Load event:


4. Form_Load()
5. crlf$ = Chr(13) & Chr(10)
6. Text1.Text = ""
7. open "C:\AUTOEXEC.BAT" for input as #1
8. while not eof(1)
9. line input #1, file_data$
10. Text1.Text = Text1.Text & file_data$ & crlf$
11. wend
12. close #1
13. End Sub
When you execute this program, each line is read correctly from the AUTOEXEC.BAT
file and added to the text already stored in the Text1 text box. If you don't specifically
add the CRLF$ to the end of the string just read from the file, the text appears as one
continuous line of characters when displayed in the text box.
You should also be aware of another problem: Text boxes do not process the carriage
return/linefeed characters properly unless you first write the Chr(13) character and then
the Chr(10) character to the file or append it to the end of a string. Therefore, you must
make sure that you specify CRLF$ as containing first the Chr(13) character and then the
Chr(10) character.

7: Minimizing and Maximizing Program


Manager
Abstract
When writing an application in Visual Basic®, you may want to keep your desktop
window as free of clutter as possible. This article explains how to minimize Program
Manager so that it appears as an icon on your desktop. The article also demonstrates how
you can restore Program Manager's window to its original size.

Using the FindWindow and ShowWindow Functions


The Windows ShowWindow function is used to set a window's visibility status. You can
tell the function to display the window as minimized, maximized, or any number of other
states.
To declare this function within your program, include the following Declare statement in
the Global Module or General Declarations section of your form:
Declare Function ShowWindow Lib "User" (ByVal hWnd As Integer, ByVal nCmdShow As Integer) As
Integer
Note that this Declare statement must be typed as one single line of text.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To call the ShowWindow function, you pass the window's handle and the visibility status
you want to apply to the window. The visibility status may be one of the following
constants:
SW_HIDE Window is hidden.
SW_MINIMIZE Window is minimized.
SW_RESTORE Window is restored to original size/position.
SW_SHOW Window is restored to original size/position and
activated.
SW_SHOWMAXIMIZED Window is maximized and activated.
SW_SHOWMINIMIZED Window is minimized and activated.
SW_SHOWMINNOACTIVE Window is minimized but not activated.
SW_SHOWNA Window is shown at current size/position but not
activated.
SW_SHOWNORMAL Window is restored to original size/position.

To change the visibility status of a window with the ShowWindow function, you must
first determine the window's handle. In our case, we need to retrieve the handle for
Program Manager. This can be accomplished by calling the FindWindow function.

To declare the FindWindow function within your program, include the following
Declare statement in the Global Module or General Declarations section of your form:
Declare Function FindWindow Lib "User" (ByVal lpClassName As Any, ByVal lpWindowName As Any)
As Integer
To call the FindWindow function, you must pass it two arguments, as follows:
lpClassName A string (or long pointer to a string) that contains the window's class
name. A value of zero is used to accept any class.
lpWindowName A string (or long pointer to a string) that contains the window's title bar
text. A value of zero is used to accept any window title.

Because we want to minimize (or maximize) Program Manager to an icon, we would call
the FindWindow function with the following statement:
hWnd = FindWindow(0&, "Program Manager")
The window's handle will be returned in the hWnd variable. We can then call the
ShowWindow function to minimize or to maximize Program Manager with these two
statements:
I = ShowWindow(hWnd, SW_SHOWMINNOACTIVE)
I = ShowWindow(hWnd, SW_RESTORE)

Example Program
The following program shows how to minimize and maximize Program Manager from
within a Visual Basic application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of text):
3. Declare Function ShowWindow Lib "User" (ByVal hWnd As Integer, ByVal nCmdShow
4. As Integer) As Integer
5. Declare Function FindWindow Lib "User" (ByVal lpClassName As Any, ByVal
6. lpWindowName As Any) As Integer
7. Const SW_SHOWMINNOACTIVE = 7
8. Const SW_RESTORE = 9
9. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Minimize PM".

10. Add the following code to the Click event for Command1:
11. Sub Command1_Click()
12. Dim hWnd As Integer, I As Integer
13.
14. hWnd = FindWindow(0&, "Program Manager")
15. If hWnd <> 0 Then
16. I = ShowWindow(hWnd, SW_SHOWMINNOACTIVE)
17. End If
18. End Sub
19. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Maximize PM".

20. Add the following code to the Click event for Command2:
21. Sub Command2_Click()
22. Dim hWnd As Integer, I As Integer
23.
24. hWnd = FindWindow(0&, "Program Manager")
25. If hWnd <> 0 Then
26. I = ShowWindow(hWnd, SW_RESTORE)
27. End If
28. Form1.SetFocus
29. End Sub
When you execute this demonstration program, click on the "Minimize PM" command
button to make Program Manager a minimized icon on the desktop. Click on the
"Maximize PM" command button to restore Program Manager to its default size.

8: Deleting Sections from .INI Files


Abstract
Microsoft® Windows® and other Windows-based applications use initialization (.INI)
files. These special files contain information about the Windows operating environment
or configuration information used by a specific application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


There are several Windows application programming interface (API) functions that can
be used to create or modify .INI files. One of the more confusing tasks is using the
WritePrivateProfileString() function to delete an entire section from an .INI file.

This article explains how you can delete a specific entry from an .INI file. For a detailed
description of Windows initialization files and the API functions you can use to modify
them, see the "Additional References" section at the end of this article.

Deleting a Section from an .INI File


An initialization (.INI) file is an ASCII text file that follows a specific format. The file is
divided into sections where the name of the section is enclosed in brackets. Directly
below the section headings are one or more entries. Each entry (or key name) is the name
you want to set a value for. This is followed by an equal sign. Next, the value to be
assigned to the key name is specified.
To modify an .INI file, you use the Windows WritePrivateProfileString() and
WriteProfileString() functions. The WriteProfileString() function is used to modify the
Windows WIN.INI initialization file, while all other .INI files are modified by calling the
WritePrivateProfileString() function.
The following is an example of an .INI file's contents:
[progsetup]
Date=10/10/95
Datafile=c:\temp.dat
In this example, the section name is "progsetup", the key names are Date and Datafile,
and the values to be given to the key names are 10/10/95 and c:\temp.dat.
To delete a specific entry from an initialization file, call the WritePrivateProfileString()
function with the statement:
x = WritePrivateProfileString(lpAppName, 0&, 0&, FileName)
specifying the following parameters:
lpAppName \The name of the section you want to remove from the INI file
lpKeyName \The entry you want to delete. This must be set to a NULL string
\to delete the entire section.
lpString \The string to be written to the entry. When set to an empty string,
\this causes the lpKeyName entry to be deleted.
lpFileName \The name of the INI file to modify.
In our example above, we would set lpAppName to "progsetup", lpFileName to
"C:\DEMO.INI", and both lpKeyName and lpString to 0& (zero). After you call this
function, the entire "progsetup" section of the DEMO.INI file will be deleted.
The lpKeyName and lpString variables are of type Any. If you use the type String, the
function may or may not work properly, so be sure to specify these as type Any when
deleting entries from initialization files. The same rule applies when using the
WriteProfileString() function.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
The following program shows how to delete an entire section from an initialization file:

1. Using the Windows Notepad application, create a new text file called DEMO.INI.
Save the file to the root directory on drive C. Add the following lines to this text
file:
2. [progsetup]
3. Date=10/10/95
4. Datafile=c:\temp.dat
5. [colors]
6. Background=red
7. Foreground=white
8. Start a new project in Visual Basic. Form1 is created by default.

9. In the general declarations section of Form1, type the following Declare


statement (note that this should be typed as a single line of text):
10. Declare Function WritePrivateProfileString% Lib "Kernel" (ByVal lpAppName
11. As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName
12. As String)
13. Add the following code to Form1_Load():
14. Sub Form_Load()
15. crlf$ = Chr(13) & Chr(10)
16. Text1.Text = ""
17. Open "c:\demo.ini" For Input As #1
18. While Not EOF(1)
19. Line Input #1, file_data$
20. Text1.Text = Text1.Text & file_data$ & crlf$
21. Wend
22. Close #1
23.
24. End Sub
25. Add a text box control to Form1. Set its MultiLine property to True and its
ScrollBars property to 3-Both. Adjust the size of the text box so that the contents
of the C:\DEMO.INI file can be displayed in it.

26. Add a command button control to Form1. Command1 is created by default. Set its
Caption property to "Modify DEMO.INI".

27. Add the following code to the Click event of Command1:


28. Sub Command1_Click()
29. FileName = "c:\demo.ini"
30. lpAppName = "progsetup"
31. x = WritePrivateProfileString(lpAppName, 0&, 0&, FileName)
32. End Sub
When you execute this sample program, the current contents of the file C::\DEMO.INI
are displayed in the text box. Click once on the "Modify DEMO.INI" command button.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The program has now deleted the entire "progsetup" section from the DEMO.INI file.
You can verify that the file's contents were changed by running the demonstration
program a second time.

9: Creating Multiline Command Buttons


Created: March 1, 1995

Abstract
Visual Basic® allows you to add Command button controls to your application programs.
Command buttons can display a one-line caption (up to a maximum of 255 characters in
length) that tells users of your program the command that will be executed when they
click the mouse on the button. However, you can have the same functionality if you
create a multiline command button using the Picture Box control.

How to Create a Multiline Command Button


In Windows®-based applications, Command buttons are used to allow the user to
execute a specific function simply by clicking the mouse over the control. The Caption
property of a Command button is used to display a message directly on the control. This
message tells users what function will be executed if they click the mouse on the
Command button. Although Command buttons are extremely versatile controls, they do
not allow you to include more than a single one-line caption.
You can, however, create a multiline Command button by using a Picture Box control.
The Picture Box control responds to a click event just as the Command button does, and
it also lets you display as many lines of text as is needed. Instead of using the Caption
property of the Command button to display a message on the control, you use Print to
draw the text on the Picture Box.
To draw the message text on a Picture Box, you use the Print command in Visual
Basic®. The text is always printed at the location specified by the CurrentX and
CurrentY properties of the Picture Box. Therefore, you must first calculate the length of
each line of text, and the length of the Picture Box control itself. This value can then be
used to calculate the position on the Picture Box where the text is to be drawn. As each
character is drawn on the Picture Box, Visual Basic automatically increments CurrentX
to point to the next available print position. Visual Basic does not scroll the text of the
message to the next line when drawing text on a Picture Box. The text will be cut off at
the control's right-most border. Therefore, you must be certain that the Picture Box is
wide enough to accommodate the longest line you intend to display on the control.

After you have calculated the values for the CurrentX property, you must issue the Print
command to tell Visual Basic to draw the text on the Picture Box control. To do this, you
execute the statement:
Picture1.Print Msg

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To determine where the text of your message should be drawn on the Picture Box, you
use the TextWidth method. This returns the horizontal length of the text based on the
currently selected FontName and FontSize properties. Likewise, you can determine how
wide the Picture Box control itself is by using ScaleWidth.
Once you have the length of the text and the length of the Picture Box control, you
simply divide these two values by two. Next, you set CurrentX to the difference between
these two values. This is the position where the first character of the message will be
drawn on the Picture Box control by the Print command.

Example Program
The following program demonstrates how to create a multiline Command button in
Visual Basic.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Picture Box control to Form1.

3. Add the following code to the Picture1_Paint event:


4. Sub Picture1_Paint()
5. Dim Msg As String
6. Picture1.CurrentY = 0
7. Msg = "Click here to"
8. MsgWidth = TextWidth(Msg) / 2
9. BtnWidth = Picture1.ScaleWidth / 2
10. Picture1.CurrentX = BtnWidth - MsgWidth
11. Picture1.Print Msg
12. Msg = "Exit"
13. MsgWidth = TextWidth(Msg) / 2
14. BtnWidth = Picture1.ScaleWidth / 2
15. Picture1.CurrentX = BtnWidth - MsgWidth
16. Picture1.Print Msg
17.
18. End Sub
19. Add the following statement to the Picture1_Click event:
20. Picture1_Click()
21. End
22. End Sub
When you execute this program, a multiline command button will be displayed on
Form1. The first line of text displayed on the command button is "Click here to" and the
second line is "Exit". Both lines of text are centered horizontally within the left and right
border of the Picture Box. The program will end when you click the mouse button on the
Picture Box. When in design mode, you can adjust the vertical position of the Picture
Box so that the text is also centered vertically within the Picture Box control.
Alternatively, you could use the TextHeight method to calculate the vertical position
within the Picture Box, just as was done with the example above to center the text
horizontally on the control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


10: Formatting a Disk
Created: March 1, 1995

Abstract
This article explains how you can format a diskette in a floppy drive from within a Visual
Basic® application. Microsoft® Windows® does not provide any functions to format a
diskette, but it can be done by using the WinExec application programming interface
(API) function.

Formatting Disks
The Windows® WinExec application programming interface (API) function can execute
any Windows-based or non-Windows-based program. To call the WinExec function, you
must first add its Declare statement to the Global Declarations section of your Visual
Basic® application. Following is the WinExec function declaration:
Declare Function WinExec Lib "Kernel" (ByVal lpCmdFile As String, ByVal
fuCmdShow As Integer) As Integer
(Note that this Declare statement must be typed as a single line of text.)
To execute a program, you would call the WinExec function with the statement:
x = WinExec(lpCmdFile, fuCmdShow)
specifying the following parameters:
lpCmdFile \A string containing the name of the application to execute
fuCmdShow \An integer value that tells WinExec how to show the application
\when it is executed. This may be one of the following constants:
SW_HIDE \The window is hidden and activation passes to another window.
SW_MINIMIZE \The window is minimized and activation passes to another
\window.
SW_RESTORE \The window is activated and displayed in its original size and
\at its original location.
SW_SHOW \The window is activated and displayed in its current size and
\at its current location.
SW_SHOWMAXIMIZED \The window is maximized and activated.
SW_SHOWMINIMIZED \The window is minimized and activated.
SW_SHOWMINNOACTIVE \The window is minimized but the active window is not
\changed.
SW_SHOWNA \The window is displayed at its current location in its current
\size but the active window is not changed.
SW_SHOWNOACTIVATE \The window is displayed at its most recent location in
\its most recent size but the active window is not
\changed.
SW_SHOWNORMAL \The window is activated and displayed in its original
\size and at its original location.
After the WinExec function is called, it returns an integer value greater than 32 if the
application was successful. Otherwise, one of the following error codes is returned:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Error Code Description
0 Out of memory
2 File not found
3 Path not found
5 Sharing/protection error
6 Each task requires separate data segments
10 Windows version is incorrect
11 Not valid .EXE file
12 Cannot execute OS/2 application
13 Cannot execute DOS 4.0 application
14 EXE type is unknown
15 Protected memory mode not supported by Windows
16 Cannot load another instance of .EXE file
17 Cannot load second instance in large-frame EMS mode
18 Cannot load protected-mode application in real mode

The lpCmdFile argument must be a string containing the name of the application
program you want to execute, as well as any command line parameters required by the
application program itself. If the argument does not include the full path, Windows will
search for the application in the following order:
1. The current directory

2. The Windows directory

3. The Windows System directory

4. The directory that contains the current task's application file

5. All directories in the PATH environment variable

6. Network directories

As stated earlier, the WinExec function can execute any Windows-based or MS-DOS®–
based program. This includes .EXE, .COM, and .BAT files. In addition, WinExec can
also be used to execute Windows screen savers (files that have the .SRC file extension)
and program information files (files that have the .PIF file extension). Windows is
shipped with several .PIF files that you can use in conjunction with the WinExec
function. One of these files is called DOSPRMPT.PIF. This particular .PIF file contains
information that Windows needs to run an MS-DOS program. The .PIF file tells
Windows, for example, how much memory should be set aside to run the MS-DOS
program.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


We can tell Windows to execute the FORMAT command in Visual Basic with the
following statement:
x = WinExec("dosprmpt.pif /c c:\dos\format b: < c:\response.tmp", SW_HIDE)
Each time MS-DOS formats a diskette, it asks you to press the ENTER key to initiate the
procedure. After the disk has been formatted, the program asks you to type a Volume
Label name and/or press the ENTER key. Next, you are asked if you wish to format
another diskette, to which you respond with a "y" or "n" key.

This problem is easily solved. To respond to the prompts from the FORMAT command,
we first need to create a file called RESPONSE.TMP. This file contains the keystrokes
we want to pass on to the FORMAT command, just as if we had typed them at the
keyboard ourselves. DOS's redirection capabilities will allow us to pass the contents of
the RESPONSE.TMP file to the FORMAT.COM program.

Example Program
The following program formats a floppy disk in drive B.
1. Start a new project in Visual Basic. Form1 is created by default.

2. In the General Declarations section of Form1, add the following three statements:
3. Const Resp_File = "c:\response.tmp"
4. Const SW_HIDE = &H0
5. Dim ActiveApps As Integer
6. In addition, add the following two Declare statements (note that each statement
should be typed as a single line of text):
7. Declare Function WinExec Lib "Kernel" (ByVal lpCmdLine As String, ByVal nCmdShow
8. As Integer) As Integer
9. Declare Function GetNumTasks Lib "Kernel" () As Integer
10. Add a command button control to Form1. Command1 is created by default. Set its
Caption property to "Format Disk".

11. Add the following code to the Click event of Command1:


12. Sub Command1_Click()
13. Cmd = Chr(13) & Chr(10) & Chr(13) & Chr(10) & "N" & Chr(13) & Chr(10)
14. FileNum = FreeFile
15. Open Resp_File For Output As #FileNum
16. Print #FileNum, Cmd
17. Close #FileNum
18. ActiveApps = GetNumTasks()
19. X = WinExec("dosprmpt.pif /c c:\dos\format b: <c:\response.tmp", SW_HIDE)
20. Do While GetNumTasks() <> ActiveApps
21. X = DoEvents()
22. Loop
23. Kill Resp_File
24. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Execute this demonstration program by pressing the F5 function key or by selecting Run
from the Visual Basic menu. Insert a floppy diskette into drive B and click the "Format
Disk" command button. Visual Basic will format the diskette in drive B and return
control to the demonstration program.

11: Determining a Program's Name and


Path
Created: March 1, 1995

Abstract
You can determine the name of your application as well as the directory where the
program is stored on your disk. This is useful when a user has renamed your application
or when you want to determine where your application can store its own temporary files.

How to Determine a Program Name or Path


You can find out the name of your application by retrieving the EXEName property of
the App object. In the same manner, the Path property of App can be used to retrieve the
directory your program is stored in. App can only be used while the application is
running and only if that application is the currently active program.
You can use the App.EXEName property to determine if a user has renamed your
application program. App.EXEName can also be used to provide information needed to
call some Windows® application programming interface (API) functions.
The App.Path property can be used by applications that store configuration information
within their own .EXE files. If you modify such an application, and need to save a new
copy of the program to disk, the App.EXEName and App.Path can tell you where to
save the new version of your application.

Example Program
The program below shows how you can retrieve an application's filename and path in
Visual Basic®.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a command button control to Form1. Command1 is created by default. Set


the command button's Caption property to "Execute Notepad".

3. Add the following code to the Click event of the Command1 command button:
4. Sub Command1_Click()

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


5. ProgName$ = "C:\WINDOWS\NOTEPAD.EXE AUTOEXEC.BAT"
6. x = Shell(ProgName$, 2)
7. AppActivate "Notepad - AUTOEXEC.BAT"
8. SendKeys "%{ }X", -1
9. p$ = App.Path
10. j$ = App.EXEName
11. SendKeys "%{ }C", -1
12. AppActivate "Form1"
13. Text1.Text = p$ & j$
14. End Sub
15. Directly below the Command1 command button, draw a Text Box on Form1.
Text1 is created by default. Set the text box's Text property to a NULL (empty)
string.

16. Add a second command button control to Form1. Command2 is created by


default. Set the command button's Caption property to "Exit".

17. Add the following code to the Click event of the Command2 command button:
18. Sub Command2_Click()
19. End
20. End Sub
21. Save the project to disk using the filename TEXT.MAK. Create an .EXE program
file in the root directory of drive C (C:\TEST.EXE).

22. To execute this program, exit Visual Basic. Next, from Program Manager, click
on File/Run. Type the name of the program to run as C:\TEST.EXE and click the
OK command button.
After Windows launches TEST.EXE, you can click the "Execute Notepad" command
button. The Windows Notepad application will be executed and will load your
AUTOEXEC.BAT file. Next, TEST sends the ALT+SPACE+X keystroke combination
to Notepad to maximize that application's window. TEST's program name and path are
then stored in two string variables and, when the ALT+SPACE+C keystrokes are sent to
Notepad to terminate that program, TEST displays the full path of TEST.EXE in its Text
Box. Clicking the "Exit" command button terminates the demonstration program.

12: Redirecting the Output of a Shelled


DOS Program
Created: March 1, 1995

Abstract
When you execute a DOS program through the Visual Basic® shell command, the only
way you can use the redirection capabilities of DOS is to save the DOS program output to

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


a text file. Then your Visual Basic program can read the data from the text file and
perform program operations based on this same data.

Retrieving Output from a DOS Program


Saving the output of a DOS command that is executed from within your Visual Basic®
program may be necessary. For example, if you execute the dir command, you will
obviously want to save the file list so that you can use it in your Visual Basic application.
Or, if the DOS program returns an ErrorLevel value, you may need to perform tasks
depending on this value.
The redirection facility provided in DOS allows you to send the output of a DOS
command to a text file. Unfortunately, the Visual Basic shell command will not allow
you to specify DOS commands that have command line parameters telling it to redirect
the output in this manner. However, if you first create a batch file that contains the DOS
command and then use shell to execute the batch file, the desired results can be achieved.
Another problem occurs when executing DOS commands or programs via shell: You
need to know when the DOS program has finished executing. This problem can be solved
by calling the Windows® GetNumTasks application programming interface (API)
function. GetNumTasks tells you how many programs are currently being executed on
the computer system in both DOS and Windows.
Therefore, to determine when your batch file has finished executing, you simply call
GetNumTasks first, saving the value it returns in a variable. After executing the shell
statement, you call GetNumTasks again to find out if the number of tasks has decreased
by one. If it has, you know that your batch file (or other DOS program) has finished
doing its work.

To use the GetNumTasks function in your Visual Basic applications, include the
following Declare statement in the Global Module or General Declarations section of
your form:
Declare Function GetNumTasks Lib "Kernel" () As Integer
Note that this Declare statement must be typed as one single line of text.
The GetNumTasks function does not require any arguments; you simply call it. It returns
an integer value set to the number of tasks that are currently running.

Example Program
This program creates a batch file that contains the DOS command "DIR C:\*.* >
C:\DIRLIST.DAT". This batch file tells DOS to issue a dir command on drive C and
send the output of that command to the text file called DIRLIST.DAT. The Visual Basic
program then displays the contents of DIRLIST.BAT in the Text Box.
1. Start a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. In the general declarations section of Form1, add the following Declare statement
(note that this statement should be typed as a single line of text):
3. Declare Function GetNumTasks Lib "Kernel" () As Integer
4. Add a command button control to Form1. Command1 is created by default. Set its
Caption property to "Execute DIR".

5. Add the following code to the Click event of Command1:


6. Sub Command1_Click()
7. Dim Num_Apps As Integer, NewFile As Integer
8. Dim File_Data As String, DosCmd As String
9. Dim X As Integer
10. 'Create a batch file with the DIR *.EXE command
11. 'and redirect the output to a textfile DIRLIST.TXT.
12. DosCmd = "DIR C:\*.* > c:\DIRLIST.DAT"
13. NewFile = FreeFile
14. Open "C:\DIRBAT.BAT" For Output As #NewFile
15. Print #NewFile, DosCmd
16. Close #NewFile
17. 'Call the Shell command to execute DIRBAT.BAT.
18. Num_Apps = GetNumTasks()
19. X = Shell("C:\DIRBAT.BAT", 2)
20. 'Wait until DIR has finished doing its thing.
21. Do While GetNumTasks() <> Num_Apps
22. X = DoEvents()
23. Loop
24. 'Display the filenames in the Text Box.
25. NewFile = FreeFile
26. Open "C:\DIRLIST.DAT" For Input As #NewFile
27. Text1.Text = ""
28. While Not EOF(NewFile)
29. Line Input #NewFile, File_Data
30. Text1.Text = Text1.Text & File_Data & Chr(13) & Chr(10)
31. Wend
32. Close #NewFile
33. End Sub
34. Add another command button to Form1. Command2 is created by default. Set its
Caption property to "Exit".

35. Add the following code to the Click Event of Command2:


36. Sub Command2_Click()
37. End
38. End Sub
39. Add a Text Box to Form1. Text1 is created by default. Set its MultiLine property
to True. Make sure the text box is large enough to display a directory list.
Execute this demonstration program by pressing the F5 function key. Click the "Execute
DIR" command button. After a second or two, a list of the files found on drive C will be
shown in the Text Box. Click the Exit command button to terminate the application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


13: Converting a Number to Hours,
Minutes, Seconds
Created: March 1, 1995

Abstract
Assume that you are writing a program where some kind of event has to be timed. You
retrieve a time value representing the total number of seconds the event took to process
its work. The problem is that you want to convert this seconds value to its equivalent
minutes and seconds value and display that string to the user. The Visual Basic® Mod
operator can do this conversion process for you.

Using Mod to Calculate Elapsed Time Periods


Assuming that you need to time a certain event in your Visual Basic® application, the
Mod operator provided in Visual Basic can help you convert the value to an equivalent
minutes and seconds value. You can then display this string to users in a more
meaningful manner.
The Visual Basic Mod operator divides two numbers, but returns only the remainder. If
you take the number 121 (representing the number of seconds that has passed) and want
to determine how many minutes and seconds this is, you would divide 121 by 60 (60
seconds per minute). The result would be 2, with 1 remaining. Then, if you use Mod on
the original value again, you'll get 1 as the remainder. This converts 121 to two minutes
and 1 second.

Example Program
The following program demonstrates how you can use the Mod operator to convert a
number representing a time value to a string.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Label control to Form1. Label1 is created by default. Set its Caption
property to "Enter a value:".

3. Next to Label1, add a Text Box control. Text1 is created by default. Set its Text
property to a NULL (empty) string.

4. Add the following code to the LostFocus event for Text1:


5. Sub Text1_LostFocus()
6. Dim Isec As Integer
7. Isec = Val(Text1.Text)
8. BreakSec = Str$(Int(Isec / 60)) & " minutes " & Str$(Isec Mod 60) & " seconds "

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


9. Text2.Text = BreakSec
10. End Sub
11. Add a second Label control to Form1 (underneath Label 1). Label2 is created by
default. Set its Caption property to "Time passed: ".

12. Beside Label2, add a Text Box control. Text2 is created by default. Set its Text
property to a NULL (empty) string.

13. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Exit".

14. Type the following code in the Click event for Command1:
15. Sub Command1_Click()
16. End
17. End Sub
When you execute this Visual Basic application, enter a value in the first text box. Press
the TAB key to move to the second text box. The program will convert the value you
entered to a string representing that value in minutes and seconds. Click on the Exit
command button to terminate the program.

14: Copying Selected Items in a


List Box to the Clipboard
Created: March 1, 1995

Abstract
One of the nice features provided under Windows® 3.1 is its Clipboard. Data (text or
graphics) can be copied from an application program to the Clipboard. This data can then
be transferred from the Clipboard to a different application, to be processed in some way.

This article shows how you can copy selected items from a List Box control to the
Clipboard. The Clipboard.GetText command copies information from the Clipboard to an
object, such as a Text Box. Conversely, the Clipboard.SetText command copies
information from an object to the Clipboard.

Example Program
The following Visual Basic® application copies selected items from a List Box control to
the Clipboard.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Start a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the FormLoad event for Form1:


3. Sub Form_Load()
4. List1.AddItem "Vancouver, B.C."
5. List1.AddItem "Surrey, B.C."
6. List1.AddItem "White Rock, B.C."
7. List1.AddItem "Richmond, B.C."
8. End Sub
9. Add a List Box control to Form1. List1 is created by default. Set its MultiSelect
property to 1-Simple.

10. Draw a Text Box control under the List Box control. Text1 is created by default.
Set its MultiLine property to True and its ScrollBars property to 2-Vertical.

11. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Copy to Clipboard".

12. Type the following code in the Click event for Command1:
13. Sub Command1_Click()
14. Dim CopytoClip As String
15. Dim I As Integer
16. Clipboard.Clear
17. CopytoClip = ""
18. For I = 0 To List1.ListCount - 1
19. If List1.Selected(I) Then
20. CopytoClip = CopytoClip & Form1.List1.List(I) & Chr$(13) & Chr$(10)
21. End If
22. Next I
23. Clipboard.SetText CopytoClip
24. End Sub
25. Add a Command Button control to Form1. Command2 is created by default. Set
its Caption property to "Show Clipboard".

26. Type the following code in the Click event for Command2:
27. Sub Command2_Click()
28. 'display data stored in clipboard
29. Text1.Text = ""
30. Text1.Text = Clipboard.GetText(CF_TEXT)
31. End Sub
32. Add a Command Button control to Form1. Command3 is created by default. Set
its Caption property to "Exit".

33. Type the following code in the Click event for Command3:
34. Sub Command3_Click()
35. End
36. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


37. Add the following statement to the General Declarations section of Form1:
38. Const CF_TEXT = 1
After you execute this demonstration program, the List Box will contain the names of the
four cities. Click one or more of these names to select them. Then, click the "Copy to
Clipboard" command button. To verify that only the selected items were actually copied
to the Clipboard, click the "Show Clipboard" command button. You can experiment with
the program several times to verify that it works correctly. To terminate the application,
click the Exit command button.

15: Creating a List of Directories


Stored on a Disk
Created: March 1, 1995

Abstract
Visual Basic® has three file system controls: the Drive List Box, the Directory List Box,
and the File List Box. Using these three controls, a Visual Basic application can access
every file stored on a floppy, fixed, or network disk drive.

Finding All Directories on a Disk Drive


You can use the Visual Basic® file system controls to navigate up and down the
directory structure of a disk. This means that you can determine the name of each
directory, saving the name in a List Box or dynamic array, if desired. Finding the
directory names would be useful, for example, in a file-finding application.
When a Visual Basic program displays a Directory List Box control on the screen, the
user can see a list of the current directories in the selected path. The user can select a
directory by double-clicking on its name. However, the default directory is not
automatically changed to the selected directory by the Directory List Box control. Your
application must retrieve the selected directory's name from the Path property of the
Directory List Box and call a ChDir function to physically change directories on the
disk. When using the Drive List Box and the File List Box controls, your application
must also physically change to the selected drive and filename, respectively.
The Path property of a Directory List Box always returns the name of the currently
selected directory. When you change the Path property, the PathChange event is
automatically triggered. This event updates the Directory List Box to show the new
directories in the selected directory.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
The following program shows how you can determine the names of all directories stored
on a hard drive.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Place a ListBox, a DirListBox, and a CommandButton on the form.

3. Add the following code to the Form_Load event for Form1:


4. Sub FindDirectories()
5.
6. Dim i As Integer
7. On Error Resume Next
8.
9. For i = 0 To Dir1.ListCount - 1
10. Dir1.Path = Dir1.List(i)
11. List1.AddItem Dir1.List(Dir1.ListIndex)
12. FindDirectories
13. Next i
14.
15. Dir1.Path = Dir1.Path & "\.."
16.
17. DoEvents
18.
19.
20. End Sub
21.
22. Private Sub Command1_Click()
23.
24. Dir1.Path = "c:\"
25.
26. FindDirectories
27.
28. End Sub
29. Add a Directory List Box control to Form1. Dir1 is created by default. Set its
Visible property to False.

30. Add the following code to the Change event for the Dir1 Directory List Box
control:
31. Sub Form_Load()
32. Next_Dir = 0
33. Temp_Dir = "C:\"
34. Dir1.Path = Temp_Dir
35. Temp_Dir = List2.List(Next_Dir)
36. Get_Next:
37. Next_Dir = Next_Dir + 1
38. Dir1.Path = Temp_Dir
39. Temp_Dir = List2.List(Next_Dir)
40. If List2.ListCount - 1 = Next_Dir Then
41. Exit Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


42. End If
43. GoTo Get_Next
44. End Sub
45. Add a List Box control to Form1. List1 is created by default. Set its Visible
property to False and its MultiSelect property to 1-Simple.

46. Add a second List Box control to Form1. List2 is created by default. Set its
Visible property to True, its MultiSelect property to 1-Simple, and its Sorted
property to True.
When you execute this program, Visual Basic will search for all subdirectories on the
drive C. This may take a few moments to do, depending on the size of the hard disk and
the number of directories the application finds. The name of each subdirectory will be
displayed in the List Box, in alphabetical order.

16: Adding Visual Effect with


Splash Screens
Created: March 1, 1995

Abstract
You can make your Visual Basic® applications more attractive and professional looking
if you include a splash screen. Splash screens are simply forms that are displayed as soon
as your application program is executed.
Splash screens are used to display important information (such as copyright notices) to
users when the application is first executed. Sometimes, splash screens are presented to
users while the application is performing time-consuming operations.

Creating Splash Screens


To create a splash screen for an application in Visual Basic®, you add text boxes,
pictures, or any other graphic element to your form. After the form has been created, you
add it to your existing project file. To display the splash screen, you use the Visual Basic
Show command. While the splash screen is being displayed, you can perform other
operations in your program.

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The example application described below displays a splash screen to the user for a short
period of time. After the splash screen is displayed, the program's main form is displayed.
Click the Exit command button to terminate the application.
1. Create a new project in Visual Basic. Form1 is created by default. This form will
be your splash screen. Set its Caption property to "Splash Screen" and its Name
property to "Splash".

2. Add a Picture Box control to Form1. Picture1 is created by default. Set its Picture
property to a bitmap, such as that provided in
C:\VB\BITMAPS\ASSORTED\HAPPY.BMP.

3. Add a Text Box control to Form1. Text1 is created by default. Set its Text
property to "Splash Screen Demo". Set its BorderStyle property to 1-Fixed
Single.

4. Save the form under the filename SPLASH.FRM.

5. Create a new project in Visual Basic. Form1 is created by default. Set its Caption
property to "VB Splash Screen Demo".

6. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Exit".

7. Add the following code to the Click event for Command1:


8. Sub Command1_Click()
9. End
10. End Sub
11. Next, add the SPLASH.FRM form created in steps 1 through 4 to your project by
selecting Add File from the Visual Basic menu.

12. Create a New Module file and name the module SPLASH.BAS.

13. Add the following code to the SPLASH.BAS module:


14. Sub Main()
15. Dim X As Long, P As Integer
16. Splash.Show
17. For X = 1 To 100000
18. P = DoEvents()
19. Next X
20. Beep
21. Unload Splash
22. Load Form1
23. Form1.Show
24. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


25. Set the Startup Form to Sub Main. Save the entire Visual Basic project as
SPLASH.MAK.

17: Hiding the Cursor (Mouse


Pointer)
Created: March 1, 1995

Abstract
The mouse pointer, or mouse cursor, as it is often called, can be temporarily toggled on
or off within a Visual Basic® application. You might want to make the cursor invisible
while your program displays a maximized form that contains a graphic picture. Then,
after displaying the graphic, you can make the cursor visible again. The Windows®
application programming interface (API) ShowCursor function lets a Visual Basic
program hide the cursor from view.

Hiding the Cursor


To hide the cursor in your Visual Basic® applications, call the ShowCursor function. To
declare this Windows® application programming interface (API) function within your
program, include the following Declare statement in the Global Module or the General
Declarations section of a Visual Basic form:
Declare Function ShowCursor Lib "User" (ByVal bShow As Integer) As Integer
Note that this Declare statement must be typed as a single line of text.

The ShowCursor function requires only one argument. When bShow is set to TRUE, the
cursor is displayed; when bShow is set to FALSE, the cursor is hidden.
You cannot simply call the ShowCursor function to hide the cursor. You also need to be
aware that the cursor's visibility depends on the value of an internal display count that
Windows maintains. This count value is incremented by a value of one each time
ShowCursor is called with bShow set to TRUE. Conversely, each time ShowCursor is
called with bShow set to FALSE, the count value is decremented. When the count value
is greater than or equal to zero, the cursor is displayed.

Example Program
The following program demonstrates how to make the cursor invisible and how to make
it reappear in a Visual Basic application program.

1. Start a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. In the global declarations section of Form1, enter the following Windows API
function declaration (note that this Declare statement must be typed as a single
line of text):
3. Declare Function ShowCursor Lib "User" (ByVal bShow As Integer) As Integer
4. In addition, add the following two Dim statements to the general declarations
section of Form1:
5. Dim QuitFlag As Integer
6. Dim MCount As Integer
7. Add the following code to the Form_Load event for Form1:
8. Sub Form_Load()
9. MCount = ShowCursor(False) + 1
10. 'hide mousepointer
11. Do While ShowCursor(False) >= -1
12. Loop
13. Do While ShowCursor(True) <= -1
14. Loop
15. Form1.Show
16. Text1.Text = "Invisible"
17. x% = ShowCursor(False)
18. Do
19. DoEvents
20. Loop Until QuitFlag = True
21. End Sub
22. Add the following code to the Form_Unload event for Form1:
23. Sub Form_Unload(Cancel As Integer)
24. Do While ShowCursor(False) >= Mcount
25. Loop
26. Do While ShowCursor(True) <= Mcount
27. Loop
28. Unload Form1
29. End Sub
30. Add the following code to the Click event for Form1:
31. Sub Form_Click()
32. Text1.Text = "Visible"
33. x% = ShowCursor(True)
34. QuitFlag = True
35. End Sub
36. Add a Text Box control to Form1. Text1 is created by default. Set its Text
property to a NULL (empty) string.
After executing this demonstration program, Visual Basic will display the string
"Invisible" in the Text Box. The cursor is made invisible. The program loops
continuously until you click the mouse to terminate the program. After clicking the
mouse over Form1, the cursor will again be visible.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


18: Automatically Selecting Text
When Tabbing to Text Box
Created: March 1, 1995

Abstract
When moving the focus from a control to a Text Box, the text is not selected when the
Text Box receives the focus. However, you can do this automatically in your program.
When a program moves the focus to a Text Box, the caret is placed at the beginning of
the actual text in the control. In an application, you may want to select the text in some
situations, such as when you press the TAB key to move to the Text Box. This can be
accomplished by using the Windows® GetKeyState application programming interface
(API) function in conjunction with the SelLength and SelStart properties of the Text
Box.

Selecting Text with the TAB Key


You can move the focus to a Text Box in your application if you use the GetKeyState
function, which returns the state of the most recently pressed or released key on the
keyboard. To declare this Windows® function in your program, include the following
Declare statement in the Global Module or the General Declarations section of a Visual
Basic® form:
Private Declare Function GetKeyState Lib "User" (ByVal nVirtkey As Integer)
As Integer
Note that this Declare statement must be typed as a single line of text.
The GetKeyState function requires only one parameter—the key code of the virtual key
you want to test. When testing alphabetic (A-Z or a-z) or alphanumeric (0-9) keys,
specify the ASCII value for that character. When testing function keys or other special
keys, pass the virtual key code to the GetKeyState function. The CONSTANT.TXT file
contains a list of all the key codes.
By including the GetKeyState function in the GotFocus event for a Text Box, you can
determine if the TAB key was pressed. If it was pressed, the SelStart and SelLength
properties of the Text Box can be used to automatically highlight the text.

Example Program
The following program demonstrates how you can select text in a Text Box when the
TAB key is used to move the focus to that control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Start a new project in Visual Basic. Form1 is created by default.

2. In the general declarations section of Form1, add the following function


declaration (note that this Declare statement must be typed as a single line of
text):
3. Private Declare Function GetKeyState Lib "User" (ByVal nVirtkey As Integer)
4. As Integer
5. In addition, add the following constant to the general declarations section of
Form1:
6. Const VK_TAB = &H9
7. Draw two Text Box controls on Form1. Text1 and Text2 are created by default.

8. Add the following code to the Form_Load event for Form1:


9. Sub Form_Load()
10. Text1.Text = "Press TAB to select the text"
11. Text2.Text = ""
12. Text2.Text = "This is a paragraph that should be selected."
13. End Sub
14. Add the following code to the GetFocus event for Text2:
15. Sub Text2_GotFocus()
16. Dim x As Integer
17. x = GetKeyState(VK_TAB)
18. If GetKeyState(VK_TAB) And -256 Then
19. Text2.SetFocus
20. Text2.SelStart = 0
21. Text2.SelLength = Len(Text2.Text)
22. End If
23. End Sub
After executing this program, Visual Basic displays the two text boxes on the form. The
first text box has the focus. Press the TAB key to move the focus to the second text box.
The text in that control is automatically selected.

19: Changing the GraphType Property at


Run Time
Created: March 1, 1995

When using the Graph custom control in a Visual Basic® application, you can change the
GraphType property at run time.
To change the GraphType property successfully in an application, you must first execute
a DrawMode statement, and then execute the statement that changes the GraphMode
property. Therefore, you would first call the following statement in your Visual Basic
application:
Graph1.GraphType = 1

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Then, immediately after executing this statement, you would include the statement to
change the GraphMode property, as follows:
Graph1.DrawMode = 2 (or 3).

20: Detecting When


Shift+PrintScrn Is Pressed
Created: March 1, 1995

Abstract
In a Visual Basic® application, you may want to disable the SHIFT+PRINTSCRN
keystroke combination. This can be accomplished by trapping the key in a control or
form's KeyDown event.

Trapping the SHIFT+PRINTSCRN Keystroke


Combination
You can trap any key on the keyboard in any form or control. The KeyPreview property
of a form, when set to TRUE, forces all keystrokes to the form, not to the control. In this
way, the form can be the first object to capture the incoming keystrokes. When the
KeyPreview property of a form is set to FALSE, each control must be programmed
individually to trap the keystrokes.
To determine when a specific key has been pressed or released on the keyboard, use the
KeyDown and KeyUp events, respectively. Both events will be triggered by a keypress or
keyrelease for the control that has the current focus. If a form does not have any controls
on it, or if the form's KeyPreview property is set to True, the KeyDown and KeyUp
events will trap the keystroke at the form level.
The KeyDown and KeyUp events return two variables: the keycode and the shift key
status. The keycode is a unique number that is assigned to each key on the keyboard. The
CONSTANT.TXT file contains a list of all keycodes supported by Visual Basic®. The
shift variable tells you which shift key (SHIFT, ALT, or CTRL) was pressed.

In a Visual Basic program, you can determine if SHIFT+PRINTSCRN was pressed by


executing this statement in the KeyDown event:
If KeyDown = 16 And Shift=1 Then ....

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The following program displays a Text Box on the screen. When you press
SHIFT+PRINTSCRN, the program displays a message to that effect in the text box. If
you press the SHIFT+F2 function key combination, the message tells you that that
keystroke was pressed.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box to Form1. Text1 is created by default.

3. Set the KeyPreview property for Form1 to TRUE.

4. Add the following code to the KeyDown event for Form1:


5. Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
6. If KeyCode = 16 And Shift = 1 Then
7. Text1.Text = "Shift-PrintScreen pressed"
8. End If
9. If KeyCode = 113 And Shift = 1 Then
10. Text1.Text = "Shift-F2 pressed"
11. End If
12. End Sub

21: Creating an Iconized Visual


Basic Application
Created: March 1, 1995

Abstract
Many Windows®-based applications can be written so that, when executed, they are
shown on the desktop simply as an icon. These type of programs usually perform some
kind of background task and are never maximized because no user input is required.
Double-clicking an icon automatically tells Windows to maximize the application's
window to a full-screen display. This article tells you how to create iconized applications
in Visual Basic® that cannot be maximized.

Terminating Iconized Applications


When running an application as an icon, you must intercept the form's resize event to
prevent the user from maximizing its window. The WindowState property of a form
controls how a form is displayed. WindowState provides three possibilities:

• The window is displayed as normal (the default setting). This is the size you made
the window when designing the application in Visual Basic®.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


• The window is minimized. It is displayed as an icon.

• The window is maximized. It occupies the entire screen.


If we want to make a program appear as an icon on the desktop, we set the WindowState
property to a value of 1. This should be done in the Form_Load event for the startup form
in Visual Basic.
When a user double-clicks on a program's icon, Windows® automatically sets its
WindowState property to normal. The double-clicking triggers the Form_Resize event,
which in turn maximizes the program's window. Because we don't want our program's
window to be maximized at any time, we set the WindowState property to a value of 1 in
the Form_Resize event. Every time our program is double-clicked, the WindowState
property is always reset to "minimized." Thus, the program is never seen in a maximized
state.

Example Program
The following program creates a Visual Basic application that is minimized to an icon
when it is executed. To terminate the program, double-click its icon.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1.Sub Form_Load():
3. WindowState = 1
4. End Sub
5. Add the following code to the Form_Resize event for Form1:
6. Sub Form_Resize()
7. If WindowState <> 1 Then
8. WindowState = 1
9. End
10. End If
11. End Sub
12. From the Visual Basic File menu, select "Make EXE File" to create a stand-alone
.EXE program file.

13. Next, execute the program from Program Manager's Run command. The
program's icon will be displayed on the desktop. You cannot maximize this
window by double-clicking the icon; that will cause the application to be
terminated.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


22: Converting DWords, Words,
and Bytes
Created: March 1, 1995

Abstract
You can write functions in Visual Basic® that split and combine DWords, Words, and
Bytes. These functions can be used to do such things as taking two integer values and
transferring them to a long integer value, where the first integer is the high word and the
second integer is the low word.

Conversion Routines for Visual Basic


The seven functions listed below can be used to convert DWords, Words, and Bytes to
other equivalent values. These routines can be used when calling Windows® application
programming interface (API) functions that require specific parameters that Visual
Basic® itself does not provide.

Function HiByte(ByVal w As Integer) As Byte


If w And &H8000 Then
HiByte = &H80 Or ((w And &H7FFF) \ &HFF)
Else
HiByte = w \ 256
End If
End Function
Function HiWord(dw As Long) As Integer
If dw And &H80000000 Then
HiWord = (dw \ 65535) - 1
Else
HiWord = dw \ 65535
End If
End Function
Function LoByte(w As Integer) As Byte
LoByte = w And &HFF
End Function
Function LoWord(dw As Long) As Integer
If dw And &H8000& Then
LoWord = &H8000 Or (dw And &H7FFF&)
Else
LoWord = dw And &HFFFF&
End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End Function
Function LShiftWord(ByVal w As Integer, ByVal c As Integer) As Integer
Dim dw As Long
dw = w * (2 ^ c)
If dw And &H8000& Then
LShiftWord = CInt(dw And &H7FFF&) Or &H8000
Else
LShiftWord = dw And &HFFFF&
End If
End Function
Function RShiftWord(ByVal w As Integer, ByVal c As Integer) As Integer
Dim dw As Long
If c = 0 Then
RShiftWord = w
Else
dw = w And &HFFFF&
dw = dw \ (2 ^ c)
RShiftWord = dw And &HFFFF&
End If
End Function
Function MakeWord(ByVal bHi As Byte, ByVal bLo As Byte) As Integer
If bHi And &H80 Then
MakeWord = (((bHi And &H7F) * 256) + bLo) Or &H8000
Else
MakeWord = (bHi * 256) + bLo
End If
End Function
Function MakeDWord(wHi As Integer, wLo As Integer) As Long
If wHi And &H8000& Then
MakeDWord = (((wHi And &H7FFF&) * 65536) Or (wLo And &HFFFF&)) _Or &H80000000
Else
MakeDWord = (wHi * 65535) + wLo
End If
End Function

23: Converting Fractional Values


to Two-Decimal-Place Currency Values
Created: March 1, 1995

Abstract
Visual Basic® offers many functions that can convert values from one type to another.
One of these functions is CCur. This function will take a string variable containing a

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


value with four digits after the decimal place and convert the fractional portion of the
value to the next highest (rounded) two-digit decimal value.

Rounding Fractional Values to Two Decimal Places


Let's assume that your Visual Basic® program has a string variable that contains the
result of some kind of percentage calculation and that value represents a dollar amount.
After the calculation has been done, that value is returned as 14.2399. However, you need
to round this dollar amount to only two decimal places so your final value is 14.24.

The CCur function converts a number to a Currency type number—that is, a number that
can contain, at maximum, four digits after the decimal place. The variable passed to the
CCur function must be no larger than 8 bytes in length and must contain a fixed decimal
point.
By combining the Format and CCur functions, you can easily convert the number to a
rounded dollar amount with only two digits after the decimal point.

Example Program
The program below shows how to use the CCur function in a Visual Basic program.
When it is executed, this program displays the value 14.2399 in the first Text Box and its
properly formatted currency value, 14.24, in the second Text Box.
1. Start a new project in Visual Basic. Form1 is created by default.

2. In the General Declarations section of Form1, add the following two statements:
3. Dim Amount As Currency
4. Dim Total As String
5. Add the following code to the Form_Load event for Form1:
6. Sub Form_Load()
7. Amount = 14.2399
8. Text1.Text = Str$(Amount)
9. Total = CCur(Format(Amount, "#,##0.00"))
10. Text2.Text = Total
11. End Sub

24: Avoiding Errors When


Removing Items from a List Box
Created: March 1, 1995

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Abstract
The Visual Basic® List Box control lets you create an array of items that are shown in
the control at run time. Your program can add new items to the List Box or delete items
from the List Box. However, care must be taken when searching through the entire list to
remove items that are selected (highlighted).

Deleting Items from a List Box


When using the List Box control, you can add new items to the array by using the
AddItem method. For example, to add the text "Item #1" to the List Box, you would
execute the statement:
List1.AddItem "Item #1"
This item is added after the last entry in the List1 List Box. The ListCount property,
which keeps track of the total number of items in the List Box, is incremented by a value
of one each time the AddItem method is used.
To delete an item from a List Box, you would use the RemoveItem method. The
following statement deletes the first element, "Item #1", from the List Box:
List1.RemoveItem (0)
After executing the RemoveItem method, the count value stored in ListCount is
automatically decremented by a value of one.
As you can see, the ListCount variable keeps track of how many items are actually
stored in the List Box at any given moment. ListCount numbers each item starting with
zero, not with one. Therefore, if you have three items in the List Box, they are numbered
0, 1, and 2.
In a Visual Basic® application, you can select (highlight) an individual item in a List Box
by clicking on it. Later on in your program, you can find out which item or items were
selected by issuing a statement such as:
If List1.Selected(1) = True Then
'do something with selected item here
Else
'do something else
End If
In other words, the Selected property is set to TRUE if the item was selected, or FALSE
if the item was not selected.
If you are using a MultiSelect List Box, your user can select more than one item at a time.
When your program wants to delete all the selected items from a List Box, you simply
loop through each entry in the control and issue the RemoveItem method.
You might use a For-Next loop like the following to accomplish this task in Visual Basic:
For X = 0 to ListCount -1
If List1.Selected(X) = True Then
List1.RemoveItem X

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End If
Next X
However, this For-Next loop will produce an "Invalid property array index" error. This
error occurs because each time the loop is executed, ListCount is reduced by one.
Eventually, the value in ListCount is actually higher than the number of items stored in
the List Box. Remember, each time you delete an item from a List Box, ListCount gets
decremented. In the For-Next loop above, X is set to the total number of items in the List
Box, but this value is not adjusted to reflect the actual number of items as each item is
removed from the list. This is why Visual Basic generates the "Invalid property array
index" error message.

You can avoid this error in your application program if you use a Do-While loop instead
of a For-Next loop. Each time through the loop, the X variable should be incremented by
a value of one. If this is done, the code will successfully remove all selected items from
the List Box.

Example Program
The program below shows how you can successfully remove all items that are selected
(highlighted) from a List Box. When you execute this program in Visual Basic, the List
Box will be filled with 16 items. Select several items to delete by clicking the mouse on
the items. When you're ready to actually remove these items from the List Box, click the
"Delete Selected Items" command button. After a second or two, the List Box will
display only those items that were not previously selected.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a List Box control to Form1. List1 is created by default. Set its MultiSelect
property to 1-Simple.

3. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Delete Selected Items".

4. In the General Declarations section of Form1, type the following Dim statement:
5. Dim X As Integer
6. Add the following code to the Form_Load event for Form1:
7. Sub Form_Load()
8. For X = 0 To 15
9. List1.AddItem "Item #" & Str$(X)
10. Next X
11. End Sub
12. Add the following code to the Click event for Command1:
13. Sub Command1_Click()
14. X=0
15. Do While X < List1.ListCount
16. If List1.Selected(X) = True Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


17. List1.RemoveItem X
18. Else
19. X=X+1
20. End If
21. Loop
22. End Sub

25: Retrieving the Drive Letter for


Temporary Files
Created: March 1, 1995

Abstract
When you install a Windows®-based application on your hard disk, it creates its own
directory and copies its files to that directory. However, when you run the application, it
may need to create additional temporary data files of some sort. Where does the
application store these temporary files and how can your program retrieve this drive
number and/or path?

The GetTempDrive and Environ$ Functions


A temporary file can be created in any directory on the hard drive, but a Windows®-
based application needs to know where you would like these files stored. To determine
which disk drive should be used to hold temporary files, you can call the Windows
application programming interface (API) GetTempDrive function . Include the
following Declare statement in the Global Module or the General Declarations section of
a Visual Basic® program:
Declare Function GetTempDrive Lib "Kernel" (ByVal cDriveLetter As Integer)
As Integer
Note that this Declare statement must be typed as a single line of text.
The GetTempDrive function does not require any arguments—you simply call it. After
you do so, it will return an integer value that represents the disk drive you can use to store
temporary files. However, you will need to convert the drive number to an ASCII drive
letter.
To convert the drive number to a drive letter, execute the following code:
DriveLetter = Chr$(Drive And &HFF)
As stated earlier, the GetTempDrive function retrieves the drive that can be used to store
temporary files while your application is executing. GetTempDrive will return the drive
number of the first hard disk it finds, which is usually the C: drive. However, this is not

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


necessarily the same disk drive that is returned by retrieving the TEMP environment
variable.
The TEMP environment variable tells you the drive and/or directory that can be used to
store temporary files as well. You retrieve the TEMP environment variable in a Visual
Basic application by issuing the statement:
X$ = Environ$("TEMP")
Environ$ will retrieve the full path as stored in the DOS environment's TEMP variable. If
you are creating a temporary file, you will need to append the filename to this variable's
string (that is, X$ = X$ + FileName$).

Example Program
The following program will retrieve the drive letter from the computer system.
1. Start a new project in Visual Basic. Form1 is created by default.

2. In the general declarations section of Form1, enter the following three statements
(note that the Declare statement must be typed as a single line of text):
3. Dim Drive As Integer
4. Dim DriveLetter As String
5. Declare Function GetTempDrive Lib "Kernel" (ByVal cDriveLetter As Integer)
6. As Integer
7. Add the following code to the Form_Load event for Form1:
8. Sub Form_Load()
9. Drive = GetTempDrive(0)
10. DriveLetter = Chr$(Drive And &HFF)
11. Text1.Text = DriveLetter
12. End Sub
13. Add a Text Box control to Form1. Text1 is created by default.

26: Enumerating Screen and


Printer Fonts
Created: March 1, 1995

Abstract
When displaying text in a Visual Basic® program, you have the option of specifying that
the text be shown in different screen fonts. This also applies to text sent to the printer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


device. However, you may need to determine which fonts are common to both the screen
and printer so that you can use a font available to both devices in your application.

Determining Available Fonts


The FontName property is used by many controls in Visual Basic®, as well as the
printer. In an application, you can change the default font to one more suitable for your
program by setting the FontName property to one of the fonts available in Windows®.
You can easily find out which fonts are available for the screen or printer by using the
FontCount property in conjunction with the Fonts property. FontCount tells you how
many fonts are available for the specified device, while Fonts tells you the name of the
actual font.
If you need to determine which fonts are common to both the screen and printer, you can
simply loop through both font lists and create a list of those fonts that are the same.

Example Program
The program below displays three List Box controls on a Visual Basic form. Printer fonts
are listed in the first List Box, screen fonts in the second List Box, and those fonts that
are common to both the printer and screen in the third List Box.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Add three List Box controls, side by side, to Form1.

3. For each list box, set its Sorted property to True.

4. Add the following code to the Form_Load event for Form1:


5. Sub Form_Load()
6. Dim X As Integer
7. Dim Y As Integer
8. For X = 0 To Screen.FontCount - 1
9. For Y = 0 To Printer.FontCount - 1
10. If Screen.Fonts(X) = Printer.Fonts(Y) Then
11. List3.AddItem Printer.Fonts(Y)
12. End If
13. Next Y
14. Next X
15. For X = 0 To Printer.FontCount - 1
16. List1.AddItem Printer.Fonts(X)
17. Next X
18. For X = 0 To Screen.FontCount - 1
19. List2.AddItem Screen.Fonts(X)
20. Next X
21. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


27: Determining When a Key or
Mouse Button Is Pressed
Created: March 1, 1995

Abstract
When developing an application in Visual Basic®, you may need to interrupt a time-
consuming function or react to a certain keypress. It is always a user-friendly option if
you allow your user to exit out of these long processes gracefully. The Windows®
GetAsyncKeyState application programming interface (API) function can be used to
detect when the user has pressed a certain key on the keyboard or clicked a mouse button.

Monitoring the Keyboard and Mouse Activities


The Windows® GetAsyncKeyState application programming interface (API) function
can tell you if a user has clicked a mouse button or pressed a specific key on the
keyboard. To declare this function within your program, include the following Declare
statement in the Global Module or General Declarations section of a Visual Basic® form:
Declare Function GetAsyncKeyState Lib "User" (ByVal Key As Integer) As Integer
Note that this Declare statement must be typed as one single line of text.

The GetAsyncKeyState function determines if the specific key was pressed since the last
call to this function. It can also tell you if one of the mouse buttons was pressed. This
function takes only one argument—an integer value that represents the key code of the
virtual key or mouse button you want to test.
GetAsyncKeyState returns a non-zero value if the specified key or button is currently
pressed. It will also return a non-zero value if the key or button was pressed since the last
call to this function. The CONSTANT.TXT file contains a list of the virtual key
constants.

Example Program
The following Visual Basic program shows how you can perform a function within your
program until a specific keystroke is detected on the keyboard. In this example program,
the DoEvents function is called to put the program in a constant loop. When you press
the ESC (escape) key, the program is terminated.
1. Start a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as one single line
of text):
3. Declare Function GetAsyncKeyState Lib "User" (ByVal Key As Integer) As Integer
4. Const KEY_ESCAPE = &H1B
5. Add a Text Box control to Form1. Text1 is created by default.

6. Add a Command Button control to Form1. Command1 is created by default.

7. Add the following code to the Click event for Command1:


8. Sub Command1_Click()
9. Text1.Text = "Press ESCAPE to quit"
10. Do While DoEvents()
11. If GetAsyncKeyState(KEY_ESCAPE) Then
12. Text1.Text = "ESCAPE pressed"
13. Exit Sub
14. End If
15. Loop
16. End Sub

28: Determining Which Forms Are


Loaded
Created: March 1, 1995

Abstract
A program created in Visual Basic® may contain many different forms. Forms can be
loaded into memory whenever they are required and removed from memory when no
longer needed. Sometimes you need to find out if a specific form is currently loaded. This
article provides a technique for determining if a form is loaded or not.

Which Form Is Loaded?


Depending on the application you are developing, you can selectively display a Visual
Basic® form on the computer screen at any time. To do this, you use the Load statement:
Load Form2
You can also remove a form from the program by using the Unload statement:
Unload Form2
It is important to realize that you can only load a form once—two copies of the same
form cannot be in memory at the same time.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Because a Visual Basic application may consist of many different forms, you need a
method of determining which form or forms are currently loaded. The Count property
can be used to find out how many forms are actually loaded into memory at any given
time. The statement:
X = Forms.Count
sets the variable X equal to the number of forms in this application. Once you know this
count value, its easy to develop a function to search through the index of forms in
memory and find out if a specific form is loaded.

Example Program
1. Start a new project in Visual Basic. Form1 is created by default. Set its Caption
property to "First Form".

2. Create a second form. Form2 is created by default. Set its Caption property to
"Second Form".

3. Create a third form. Form3 is created by default. Set its Caption property to
"Third Form".

4. Add a Text Box control to Form1. Text1 is created by default.

5. Add the following code to the Form_Load event for Form1:


6. Sub Form_Load()
7. Dim F As Integer
8. Load Form2
9. F = IsLoaded(Form2)
10. Text1.Text = "Form" + Str$(F)
11. End Sub
12. Create a new function called IsLoaded:
13. Function IsLoaded(F_Form As Form) As Integer
14. Dim X As Integer
15. For X = 0 To Forms.Count
16. If Forms(X) Is F_Form Then
17. IsLoaded = X + 1
18. Exit Function
19. End If
20. Next X
21. IsLoaded = 0
22. End Function
Execute this demonstration program by pressing the F5 function key. The first form is
displayed with a text box. The string "Form 2" should be displayed in the Text Box. The
IsLoaded function was passed the index number of the form to be checked. If that form
was indeed loaded into memory, the text box will display the form's number. You can
change the example program to display the third form's status by changing the "Load

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Form2" statement to "Load Form3" and the "F=IsLoaded(Form2)" statement to
"F=IsLoaded(Form3)" to see if the third form is reported as being loaded.

29: Displaying MDI Child


Windows as Icons Without Showing Child
Windows First
Created: March 1, 1995

Abstract
When designing a MDI application in Visual Basic®, you first create the primary MDI
form and add secondary child forms to the MDI form. After your program is executed,
you can initially display the child forms as minimized icons. These icons will appear at
the bottom of the primary MDI form's window.
However, when the icons are displayed on the form, you can actually see each child form
quickly displayed in its normal size on the MDI form. This article explains how you can
prevent Windows® from flashing these child windows before they are shown as
minimized icons. The icons will be minimized immediately without this side effect.

Minimizing and Displaying Child Icons Immediately


The key to minimizing child windows immediately, without first having them displayed
in their normal size and at their normal positions within the MDI form, is to set each child
window's WindowState property to 1-Minimized. Next, call the Windows® application
programming interface (API) ShowWindow function to display the child windows as
minimized icons.
The ShowWindow function is used to set a window's visibility status. You can tell the
function to display the window as minimized, hidden, or any number of other states. To
declare this function within your program, include the following Declare statement in the
Global Module or General Declarations section of your MDI form:
Declare Function ShowWindow Lib "User" (ByVal hWnd%, ByVal nCmdShow%) As Integer
Note that this Declare statement must be typed as one single line of text.

To call the ShowWindow function, you pass the window's handle and the visibility status
you want to apply to the window. The visibility status may be one of the constants in the
following table.
SW_HIDE Window is hidden.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


SW_MINIMIZE Window is minimized.
SW_RESTORE Window is restored to original size/position.
SW_SHOW Windows is restored to original size/position and
activated.
SW_SHOWMAXIMIZED Window is maximized and activated.
SW_SHOWMINIMIZED Window is minimized and activated.
SW_SHOWMINNOACTIVE Window is minimized but not activated.
SW_SHOWNA Window is shown at current size/position but not
activated.
SW_SHOWNOACTIVATE Window is shown at most recent size/position but not
activated.
SW_SHOWNORMAL Window is restored to original size/position.

To change the visibility status of a window with the ShowWindow function, you must
first determine the window's handle. In our case, we need to retrieve the handle for the
child window. This can be done with the statement:
X% = Form2.hWnd
where Form2 is the name of the form whose handle we want to retrieve and the X
variable will contain the handle number. Every Visual Basic form has an hWnd property
that is set to the form's handle. Once the ShowWindow function is called, the child
window will be immediately iconized on the MDI form.

Example Program
The following program shows how you can display a MDI form's child windows as icons
without first having the child windows flashed on the MDI form in their normal size and
position.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Create an MDI form. MDIForm1 is created by default.

3. Add a child form to MDIForm1. Form2 is created by default. Set its


WindowState property to 1-Minimized. Set its MDIChild property to True.

4. Add a second child form to MDIForm1. Form3 is created by default. Set its
WindowState property to 1-Minimized. Set its MDIChild property to True.

5. Set the application's StartUp form to MDI_Test.

6. Add the following Constant and Declare statement to the General Declarations
section of MDIForm1 (note that the Declare statement must be typed as one
single line of text):

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


7. Declare Function ShowWindow Lib "User" (ByVal hWnd%, ByVal nCmdShow%) As Integer
8. Const SW_MINIMIZE = &H6
9. Add the following code to the Form_Load event for MDIForm1:
10. Sub MDIForm_Load()
11. Dim x As Integer
12. x = ShowWindow%(Form2.hWnd, SW_MINIMIZE)
13. x = ShowWindow%(Form3.hWnd, SW_MINIMIZE)
14. End Sub
To execute this demonstration program, press the F5 function key. The two child forms
will be immediately displayed as icons near the bottom of the MDIForm1 form.

30: Retrieving the Default


Printer's Name from WIN.INI
Created: March 1, 1995

Abstract
The WIN.INI initialization file contains many settings used by Windows® when it is first
loaded into memory. These settings tell Windows, among many other things, what type
of printer you have attached to the computer system. You can retrieve the name of the
printer from the WIN.INI file and use it in your Visual Basic® application. This article
explains how you can determine the printer's name.

Using GetProfileString to Determine Printer Name


The WIN.INI initialization file is divided into sections. Each section's name is
surrounded by bracket ([ ]) characters. Directly below the section name are one or more
entries and their parameters, separated by an equal sign. Windows® stores the name of
the printer in the WINDOWS section of the WIN.INI file. The "device=" entry contains
the printer's name, as follows:
[windows]
device=Canon Bubble-Jet BJ-300,CANON330,LPT1:
In a Visual Basic® application, the Windows application programming interface (API)
GetProfileString function can be used to retrieve the printer's name from the WIN.INI
file. In the example above, the printer's name is Canon 330.
To declare this function within your program, include the following Declare statement in
the Global Module or General Declarations section of your Visual Basic program:
Declare Function GetProfileString Lib "Kernel" (ByVal lpAppName As String, ByVal
lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString
As String, ByVal nSize As Integer) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Note that this Declare statement must be typed as one single line of text.

To call the GetProfileString function, you must pass five arguments that describe which
section and entry of the WIN.INI file you want to retrieve information from. These
parameters are as follows:
lpApplicationName A string containing the section name. Not case-sensitive.
lpKeyName A string containing the entry name to retrieve. Not case-sensitive. If
this is a long value set to zero, a list of all entries found in the
specified section will be returned in lpReturnedString.
lpDefault A string containing the default value to return if no entry is found.
lpReturnedString A string buffer that will hold the information the function retrieves.
nSize An integer value set to the maximum number of characters to be
stored in lpReturnedString

After calling the GetProfileString function, an integer value is returned that is set to the
number of characters that were stored in the lpReturnedString buffer. This count value
does not include the terminating NULL byte. Each entry returned in lpReturnedString is
terminated by a NULL character. When retrieving more than one entry at a single time,
The last entry in the buffer is marked with two NULL bytes to signal the end of the list.
In our case, we want to call the GetProfileString function to retrieve the printer's name.
Therefore, we execute the following statement:
RC = GetProfileString("windows", "device", "", Temp, 255)
This statement tells GetProfileString that we want to retrieve the parameter for the
DEVICE entry in the WINDOWS section of the WIN.INI initialization. It also tells the
function to store the information in the Temp string buffer and that this buffer is set to a
maximum length of 255 characters.

Example Program
The following Visual Basic program shows how you can retrieve the name of the printer
from the WIN.INI initialization file.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default.

3. Add the following Declare statement to the general declarations section of Form1
(note that this Declare statement must be typed as one single line of text):
4. Declare Function GetProfileString Lib "Kernel" (ByVal lpAppName As String, ByVal
5. lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString
6. As String, ByVal nSize As Integer) As Integer
7. Add the following code to the Form_Load event for Form1:
8. Sub Form_Load()

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


9. Dim Temp As String
10. Dim RC As Integer
11. Dim P1 As Integer, P2 As Integer 'Get current printer device name
12. Temp = Space$(255)
13. RC = GetProfileString("windows", "device", "", Temp, 255)
14. Temp = Left$(Temp, RC)
15.
16. If RC = 0 Then 'no printer
17. Text1.Text = "No default printer"
18. Exit Sub
19. End If 'Extract just the driver name
20. P1 = InStr(Temp, ",")
21. P2 = InStr(P1 + 1, Temp, ",")
22. Text1.Text = Mid$(Temp, P1 + 1, P2 - P1 - 1)
23. End Sub
To execute this demonstration program, press the F5 function key. Visual Basic will
display the name of your printer in the text box.

31: Creating the Windows


Wallpaper Effect
Created: March 1, 1995

Abstract
Windows® displays an image as its wallpaper. You can add this feature to your own
Visual Basic® programs. This article explains how you can use the Windows application
programming interface (API) BitBlt function to copy a single icon multiple times. The
icons are copied so that they cover the entire area of the form, giving it a "wallpaper"
look.

Using BitBlt to Copy an Icon


The Windows® application programming interface (API) BitBlt function can be used to
copy an icon within a Visual Basic® program. This function copies the specified bitmap
from the source device to the destination device. In order for this function to work
correctly, the ScaleMode property of both devices must be set to Pixel.

To declare this function within your program, include the following Declare statement in
the Global Module or General Declarations section of your form:
Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X As Integer,
ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal
hSrcDC As Integer, ByVal XSrc As Integer, ByVal YSrc As Integer, ByVal dwRop
As Long) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Note that this Declare statement must be typed as one single line of text.

The BitBlt function requires that nine arguments be passed to it. These arguments are as
follows:
hDestDC An integer value that contains the device context (hDC property) of the
destination device
hSrcDC An integer value that contains the device context (hDC property) of the
source device
X, Y Integer values that contain the upper-left corner in the destination where
the bitmap is to be placed. Specified in logical coordinates.
nWidth, Integer values that contain the logical dimensions of the bitmap.
nHeight
Xsrc, Ysrc Integer values that contain the logical coordinates of the source bitmap's
upper-left corner
dwRop A long value that specifies how the BitBlt function is to copy the bitmap.

The dwRop argument may be specified as one of the constants in the following table
(these values are stored in the CONSTANT.TXT file).
BLACKNESS All output is black.
DSTINVERT The destination bitmap is inverted.
MERGECOPY The Boolean AND operator combines the pattern and the source
bitmap.
MERGEPAINT The source bitmap is inverted and the Boolean OR operator is used to
combine it with the destination bitmap.
NOTSRCCOPY The source bitmap is inverted and copied to the destination bitmap.
NOTSRCERASE The Boolean OR operator is used to combine the source and
destination bitmaps. The resulting bitmap is then inverted.
PATCOPY The pattern is copied to the destination bitmap.
PATINVERT The Boolean XOR operator is used to combine the destination
bitmap with the pattern.
PATPAINT The Boolean OR operator is used to invert the source bitmap and
combine it with the pattern. The Boolean OR operator is then used to
combine the result with the destination bitmap.
SRCAND The Boolean AND operator is used to combine the pixels of the
source and destination bitmaps.
SRCCOPY The source bitmap is copied to the destination bitmap.
SRCERASE The Boolean AND operator is used to invert the destination bitmap.
The result is then combined with the source bitmap.
SRCINVERT The Boolean XOR operator is used to combine the pixels of the
source and destination bitmaps.
SRCPAINT The Boolean OR operator is used to combine the pixels of the source

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


and destination bitmaps.
WHITENESS All output is turned white.

We want to copy a bitmap many times so that the entire form is covered in bitmaps, so
we execute the following statement in a Visual Basic program:
D = BitBlt(hDC, X, Y, PatternWidth, PatternHeight, hDC, 0, 0, SRCCOPY)
The SRCCOPY constant tells the BitBlt function to copy the bitmap to the destination;
in this case we are copying it to the form.

Example Program
The following Visual Basic application displays a pattern over the entire area of a form.
This example program uses the ARGYLE.BMP bitmap file shipped with Windows. You
can substitute any other bitmap of your choice, as long as it is 32-by-32 pixels. This is
hard-coded into the demonstration program in the PatternWidth and PatternHeight
variables.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Set the Picture property of Form1 to the ARGYLE.BMP bitmap file (this file will
be located in the WINDOWS directory).

3. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as one single line
of text):
4. Const SRCCOPY = &HCC0020
5. Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X As Integer,
6. ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal
7. hSrcDC As Integer, ByVal XSrc As Integer, ByVal YSrc As Integer, ByVal dwRop
8. As Long) As Integer
9. Add the following code to the Paint event for Form1:
10. Sub Form_Paint()
11. Dim X As Integer
12. Dim Y As Integer
13. Dim D As Integer
14. Dim PatternHeight As Integer
15. Dim PatternWidth As Integer
16. Dim SM As Integer
17. SM = ScaleMode 'save current value
18. ScaleMode = 3 'pixel
19. PatternHeight = 32 'hard-coded value
20. PatternWidth = 32 'hard-coded value
21. For X = 0 To ScaleWidth Step PatternWidth
22. For Y = 0 To ScaleHeight Step PatternHeight
23. D = BitBlt(hDC, X, Y, PatternWidth, PatternHeight, hDC, 0, 0, SRCCOPY)
24. Next Y
25. Next X

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


26. ScaleMode = SM 'reset to previous value
27. End Sub
To execute this program, press the F5 function key. After a few seconds, the ARGYLE
bitmap will be displayed over the entire area of the form.

32: Creating Custom Cursors


(Mouse Pointers)
Created: March 1, 1995

Abstract
When you are designing an application, Visual Basic® lets you assign the MousePointer
property of many controls to one of the twelve predefined mouse cursors. Typically you
would change the MousePointer property of a control to tell your program's user that
some kind of action has been invoked. For example, the hourglass cursor is used to
indicate the passage of time. At other times, you may just want to inform the user that she
or he positioned the mouse pointer over a specific control, such as a File List Box. To do
this, you would simply change the MousePointer property of that control at the
appropriate time in your Visual Basic application.

However, you may want to display a cursor shape not included in the twelve predefined
shapes. This article explains how you can create a different mouse pointer (cursor), even
for controls that do not have a MousePointer property.

Changing the Cursor (Mouse Pointer)


To change the cursor (mouse pointer) to a different shape within your Visual Basic®
application, you add code to change the MouseMove and DragOver events for the control
you want to monitor.
The MouseMove event contains code that triggers a Drag method for the control. This in
turn displays the new mouse pointer when the cursor is moved over the selected control.
When the mouse pointer is moved off the control, the DragOver event is triggered. In
your Visual Basic program, you reset the Drag property so that the original mouse
pointer is again displayed.

Example Program
The following program changes the mouse pointer to a different shape when the pointer
is moved over a File List Box control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a File List Box control to Form1. File1 is created by default.

3. Set the DragIcon property of the File1 control to an .ICO file of your choice.

4. Add the following code to the MouseMove event for File1:


5. Sub File1_MouseMove(Button As Integer, Shift As Integer, X As Single,
6. Y As Single)
7. File1.Drag 1 'icon on
8. End Sub
Note that the "Sub File1_MouseMove" line must be typed as a single line of code.
9. Add the following code to the DragOver event for Form1:
10. Sub Form_DragOver(Source As Control, X As Single, Y As Single, State As Integer)
11. File1.Drag 0 'icon off
12. End Sub
Run this demonstration program by pressing the F5 function key. Whenever you move
the mouse pointer over the File List Box control, the program will display your selected
.ICO file as the default mouse pointer. Move the mouse pointer off the control, and the
cursor changes back to its default shape.

33: Capitalizing Words in a String


Created: March 1, 1995

Abstract
Visual Basic® offers many functions that can be used to manipulate text strings. Using
the LCase and UCase functions, you can change the characters in a string to all
lowercase or all uppercase letters. This article shows how you can convert the first letter
of each word in a string to a capital letter.

Converting Characters to Uppercase


When you want to convert a string to all lowercase or all uppercase letters, you can use
the LCase and UCase functions, respectively. LCase converts the text in the specified
string to all lowercase characters, while UCase converts the text to all uppercase letters.
The Mid function can be used to examine a particular string within a larger string. You
can use Mid to extract one or more characters from a larger string, manipulate the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


characters in some manner, and the old characters are replaced with the newly modified
letters.
Another Visual Basic® function used to manipulate text strings is the InStr function,
which can be used to find a specific character within a string.
By combining the InStr, Mid, and UCase functions, you can selectively convert parts of
a string to uppercase letters.

Example Program
The following Visual Basic program converts the sentence in the first text box so that
each word is capitalized.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

3. Add a second Text Box control to Form1. Text2 is created by default. Set its
MultiLine property to True.

4. Add the following code to the Form_Load event for Form1:


5. Sub Form_Load()
6. Text1.Text = "microsoft visual basic is a fun"
7. Text1.Text = Text1.Text + " programming language."
8. Text2.Text = CapAllWords(Text1.Text)
9. End Sub
10. Create the new function shown below:
11. Function CapAllWords(ByVal MyString As String) As String
12. Dim PosSpc As Integer
13. Mid(MyString, 1, 1) = UCase(Mid(MyString, 1, 1))
14. PosSpc = InStr(MyString, " ")
15. While PosSpc <> 0
16. Mid(MyString, PosSpc + 1, 1) = UCase(Mid(MyString, PosSpc + 1, 1))
17. PosSpc = InStr(PosSpc + 1, MyString, " ")
18. Wend
19. CapAllWords = MyString
20. End Function
When you execute this sample program, Visual Basic displays a lowercase string in the
first text box. The first character of each word in this sentence is then converted to a
capital letter. The converted string is shown in the second text box.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


34: Displaying Characters in
Different Colors
Created: March 1, 1995

Abstract
In a Visual Basic® application, you have several methods available to display printed
text to your user. For example, you can use the Text property of a Text Box. However,
the text you display in the Text Box is limited to a specific color. This article shows how
text can be displayed with any number of color combinations.

Displaying Text in Color


Each time a form's size is changed, or a window has covered a form, the Paint event is
triggered. The Paint event is used to redraw the contents of a form or Picture Box
whenever the contents of that control have been changed.

Therefore, by using the Line method to draw text on a form, which in turn causes a Paint
event to be triggered, you can display text in whatever color you desire.
The Line method is used to draw lines, boxes, and filled boxes on the printer, the Picture
Box, and the form controls. To draw a line, you specify the starting position, the ending
position, and an optional color to be used. The CurrentX and CurrentY properties can be
used to set or retrieve the current print position within the control where printing takes
place.
When drawing text to a control, you must first calculate the space required to draw the
character. You do this using the TextWidth and TextHeight methods. Once you have
drawn the character on the control with the Line method, you can then use the ForeColor
property to change the color of the drawn character.

Example Program
The following Visual Basic program displays the letters A–Z in different colors on the
program's form.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Paint event for Form1:


3. Sub Form_Paint()
4. Dim I As Integer, X As Integer, Y As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


5. Dim C As String
6. Cls
7. For I = 65 To 91
8. X = CurrentX
9. Y = CurrentY
10. C = Chr(I)
11. Line -(X + TextWidth(C), Y = TextHeight(C)), QBColor(Rnd * 16), BF
12. CurrentX = X
13. CurrentY = Y
14. ForeColor = RGB(Rnd * 256, Rnd * 256, Rnd * 256)
15. Print C;
16. Next
17. End Sub

35: Determining a Window's Size


and Position
Created: March 1, 1995

Abstract
You can use two Windows® application programming interface (API) functions to
determine the exact size and position of a window within your Visual Basic®
application: The GetWindowRect function returns the screen coordinates of the
window's normal position, and the GetWindowPlacement function reports the window's
position both when minimized or maximized, in addition to the window's normal
position. This article explains how you can use these two functions in a Visual Basic
application.

Retrieving a Window's Size and Position


The Windows® GetWindowRect application programming interface (API) function
returns the specified window's position. You can also determine the exact size, in screen
coordinates, of the window by using this function.

To declare this function within your program, include the following Declare statement in
the Global Module or General Declarations section of your application's form:
Declare Sub GetWindowRect Lib "User" (ByVal hWnd As Integer, lpRect As RECT)
Note that this Declare statement must be typed as one single line of text.
The GetWindowRect function can be called by passing it two arguments. The hWnd
argument must contain the handle of the window that you want to retrieve information
for. After calling GetWindowRect, the function stores the window information in the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


second argument—a rectangle (RECT) structure. Call the GetWindowRect function
with the following statement:
Call GetWindowRect(Form1.hWnd, lpRect)
This function stores its results in the RECT structure (in this case, lpRect). The RECT
structure is defined as follows:
Type RECT '8 bytes
Left As Integer
Top As Integer
Right As Integer
Bottom As Integer
End Type
These values represent the position of the window, in screen coordinates. To calculate the
exact size of the window (including the borders, title bars, and so forth), you would issue
the following statements:
FormWidth = lpRect.Right - lpRect.Left
FormHeight = lpRect.Bottom - lpRect.Top
On the other hand, the GetWindowPlacement function is a little more involved. Its
Declare statement is:
Declare Sub GetWindowPlacement Lib "User" (ByVal hWnd As Integer, lpWnd As
WINDOWPLACEMENT)
You specify the first argument as the window's handle, and the second argument as a
pointer to the WINDOWPLACEMENT structure. This structure will be filled with the
window's minimized, maximized, and normal position coordinates. The
WINDOWPLACEMENT structure uses the RECT and POINTAPI structures as well,
and looks like this:
Type WINDOWPLACEMENT '22 bytes
Length As Integer
Flags As Integer
ShowCmd As Integer
PtMinPosition As POINTAPI
PtMaxPosition As POINTAPI
RcNormalPosition As RECT
End Type
where the POINTAPI structure is:
Type POINTAPI '4 bytes
X As Integer
Y As Integer
End Type
The following table describes each field in the WINDOWPLACEMENT structure.
Length An integer value that must be set to 22, the length of the structure.
Flags An integer value containing either WPF_SETMINPOSITION (the
ptMinPosition specifies the X, Y location of the window when
minimized) or WPF_RESTORETOMAXIMIZED (the
SW_SHOWMINIMIZED constant must be specified in the
ShowCmd parameter. It indicates the window should be maximized

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


the next time it is restored).
ShowCmd An integer value that describes the visibility flags.
ptMinPosition A POINTAPI structure containing the X, Y location of the window
when it is minimized.
ptMaxPosition A POINTAPI structure containing the X, Y location of the window
when it is maximized.
RcNormalPosition A RECT structure containing the position of the window when it is
restored (set to its normal size).

Example Program
The program below uses the GetWindowRect function to display the window's size in
the first text box and the GetWindowPlacement function to display the window's left,
right, top, and bottom coordinates.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

3. Add a second Text Box control to Form1. Text2 is created by default. Set its
MultiLine property to True.

4. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to Get Window Info.

5. Add the following code to the Click event for Command1:


6. Sub Command1_Click()
7. Dim lpRect As RECT
8. Dim lpWnd As WINDOWPLACEMENT
9. Dim nPoint As POINTAPI
10. Dim hWnd As Integer
11. Dim FormWidth As Integer
12. Dim FormHeight As Integer
13. Call GetWindowRect(Form1.hWnd, lpRect)
14. FormWidth = lpRect.Right - lpRect.Left
15. FormHeight = lpRect.Bottom - lpRect.Top
16. Text1.Text = "Form Width = " + Str$(FormWidth) + Chr(13) + Chr(10)
17. Text1.Text = Text1.Text + "Form Height = " + Str$(FormHeight)
18. lpWnd.Length = 22
19. Call GetWindowPlacement(Form1.hWnd, lpWnd)
20. FormWidth = lpWnd.RcNormalPosition.Right - lpWnd.RcNormalPosition.Left
21. FormHeight = lpWnd.RcNormalPosition.Bottom - lpWnd.RcNormalPosition.Top
22. Text2.Text = "Form Width = " + Str$(FormWidth) + Chr(13) + Chr(10)
23. Text2.Text = Text2.Text + "Form Height = " + Str$(FormHeight)
24. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


25. Add a new BAS module to the project. Module1.Bas is created by default.

26. Add the following Declare statements and structures to Module1.Bas (note that
each Declare statement must be typed as a single line of text):
27. Declare Sub GetWindowRect Lib "User" (ByVal hWnd As Integer, lpRect As RECT)
28. Declare Sub GetWindowPlacement Lib "User" (ByVal hWnd As Integer, lpWnd
29. As WINDOWPLACEMENT)
30. Type RECT '8 bytes
31. Left As Integer
32. Top As Integer
33. Right As Integer
34. Bottom As Integer
35. End Type
36. Type POINTAPI '4 bytes
37. X As Integer
38. Y As Integer
39. End Type
40. Type WINDOWPLACEMENT '22 bytes
41. Length As Integer
42. Flags As Integer
43. ShowCmd As Integer
44. PtMinPosition As POINTAPI
45. PtMaxPosition As POINTAPI
46. RcNormalPosition As RECT
47. End Type

36: Terminating a Running


Application from Within Visual Basic
Created: March 1, 1995

Abstract
From within a Visual Basic® application, you can terminate another Windows®-based
application that is currently running. To do this, you send a WM_CLOSE command to
the running application. This terminates the program just as if you had clicked on the
Close command in the application's control menu. This article explains how you can
terminate a program from within a Visual Basic application.

Terminating Running Applications


There are several steps you need to perform in your Visual Basic® program before you
can successfully terminate a running application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


First, you need to determine the running application's window handle. This can be done
by calling theWindows® application programming interface (API) FindWindow
function. Next, you must use the GetWindow function to make sure that you are not
trying to terminate your own Visual Basic program. Second, you must be certain the
window handle does not refer to a window that is disabled or otherwise not able to be
terminated. As long as the above-mentioned conditions have been met, you can call the
PostMessage function to terminate the running application.

The Windows API PostMessage function is the key to terminating a program running in
Windows. After you have determined the application's handle, you simply execute the
PostMessage function with the WM_CLOSE command as an argument.
To declare this function within your program, include the following Declare statement in
the Global Module or General Declarations section of your form:
Declare Function PostMessage Lib "User" (ByVal Hwnd As Integer, ByVal wMsg
As Integer, ByVal wParam As Integer, ByVal lParam As Long) As Integer
Note that this Declare statement must be typed as one single line of text.
The PostMessage function requires four arguments to be passed to it. These arguments
are as follows:
hWnd An integer value set to the window's handle
wMsg An integer value set to the message ID that you want to send to the window
wParam An integer value set to a 16-bit parameter (depends on wMsg)
lParam A string or long value (depends on wMsg)

After calling the PostMessage function, an integer value will be returned. If this value is
set to TRUE (nonzero), the function was successful and the target application was
terminated.

Example Program
The following Visual Basic program shows how you can terminate an application
currently running in Windows. This example assumes that the application you want to
terminate is the Windows Solitaire game and that it is currently running in memory.
When you execute this program, it will display a message box telling you either that
Solitaire is not running (in which case the program simply ends) or that Solitaire is
running. Click the OK command button and Solitaire will be immediately terminated.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add the following Constants and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of text):
3. Declare Function IsWindow Lib "User" (ByVal Hwnd As Integer) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


4. Declare Function GetWindow Lib "User" (ByVal Hwnd As Integer, ByVal wCmd
5. As Integer) As Integer
6. Declare Function GetWindowLong Lib "User" (ByVal Hwnd As Integer, ByVal nIndex
7. As Integer) As Long
8. Declare Function PostMessage Lib "User" (ByVal Hwnd As Integer, ByVal wMsg
9. As Integer, ByVal wParam As Integer, ByVal lParam As Long) As Integer
10. Declare Function FindWindow Lib "User" (ByVal lpClassName As Any, ByVal
11. lpWindowName As String) As Integer
12. Const GW_OWNER = 4
13. Const GWL_STYLE = -16
14. Const WS_DISABLED = &H8000000
15. Const WS_CANCELMODE = &H1F
16. Const WM_CLOSE = &H10
17. Add the following code to the Form_Load event for Form1:
18. Sub Form_Load()
19. Dim Hwnd As Integer
20. Dim Y As Integer
21. Hwnd = FindWindow(0&, "Solitaire")
22. If Hwnd = 0 Then
23. MsgBox "SOLITAIRE is not running"
24. Exit Sub
25. Else
26. MsgBox "Click to quit SOLITAIRE"
27. End If
28. Y = EndTask(Hwnd)
29. If Y <> 0 Then
30. MsgBox "SOLITAIRE terminated"
31. Else
32. MsgBox "Error - Cannot terminate SOLITAIRE"
33. End If
34. End Sub
35. Create a new function called EndTask. Type the following code for this function:
36. Function EndTask(TargetHwnd As Integer) As Integer
37. Dim X As Integer
38. Dim ReturnVal As Integer
39. If TargetHwnd = hWndMe% Or GetWindow(TargetHwnd, GW_OWNER) = hWndMe% Then
40. End
41. End If
42. If IsWindow(TargetHwnd) = False Then GoTo EndTaskFail
43. If (GetWindowLong(TargetHwnd, GWL_STYLE) And WS_DISABLED) Then GoTo
EndTaskSucceed
44.
45. If IsWindow(TargetHwnd) Then
46. If Not (GetWindowLong(TargetHwnd, GWL_STYLE) And WS_DISABLED) Then
47. X = PostMessage(TargetHwnd, WM_CANCELMODE, 0, 0&)
48. X = PostMessage(TargetHwnd, WM_CLOSE, 0, 0&)
49. DoEvents
50. End If
51. End If
52. GoTo EndTaskSucceed
53.
54. EndTaskFail:
55. ReturnVal = False
56. GoTo EndTaskEndSub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


57. EndTaskSucceed:
58. ReturnVal = True
59. EndTaskEndSub:
60. EndTask% = ReturnVal
61. End Function

37: Restricting Mouse Pointer


Movement to a Control
Created: March 1, 1995

Abstract
In your Visual Basic® application, you may want to restrict the movement of the mouse
pointer (cursor) to a certain control or area of a window. This technique is called
clipping, and can be accomplished by calling the Windows® application programming
interface (API) ClipCursorRect and SetCursorPos functions.

Confining the Cursor (Mouse Pointer) to a Specific


Area of a Window
The Windows® application programming interface (API) ClipCursorRect function lets
you confine the cursor's movement to a specific area of a window. To declare this
function within your program, include the following Declare statement in the Global
Module or General Declarations section of your application's form:
Declare Sub ClipCursorRect Lib "User" Alias "ClipCursor" (lpRect As RECT)
Note that this Declare statement must be typed as one single line of text.

The ClipCursorRect function can be called by passing it just one argument—a RECT
rectangle structure. This structure describes the area to which you want to restrict the
cursor's movement. The RECT structure is defined as follows:
Type RECT
Left As Integer
Top As Integer
Right As Integer
Bottom As Integer
End Type
The left, right, top, and bottom values represent the area's coordinates within the window
that you want to work with. After you have determined the area you want to restrict
cursor movement to, you must use the SetCursorPos function to move the cursor to this
area. As stated earlier, you would add the Declare statement for this function to the
General Declarations section as:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Declare Sub SetCursorPos Lib "User" (ByVal X As Integer, ByVal Y As Integer)
When you want to move the cursor to a specific location, you call the SetCursorPos
function with the X argument set to the horizontal screen coordinate and the Y argument
set to the vertical screen coordinate that corresponds to the position on the screen that you
want the cursor moved to.
It is important to note that you must also call the ClipCursorClear function to restore the
mouse pointer to its normal state. If you don't do this, your program's user will not be
able to move the mouse pointer away from the restricted area, most likely forcing him or
her to reboot the computer system. The ClipCursorClear function can be called with a
value of zero to turn off the cursor clipping.

Example Program
The following program shows how you can restrict the mouse pointer's movement within
a Visual Basic program. In this example, you can only move the mouse pointer with the
Text Box control as long as the time delay is in effect. After the For-Next loop has
finished its work, the mouse pointer's movement is restored to its normal functionality.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Sub Form_Load()
4. Dim CursorX As Integer
5. Dim CursorY As Integer
6. Dim lpRect As RECT
7. Dim X As Integer
8. lpRect.Left = Text1.Left \ Screen.TwipsPerPixelX
9. lpRect.Top = Text1.Top \ Screen.TwipsPerPixelY
10. lpRect.Right = (Text1.Left + Text1.Width) \ Screen.TwipsPerPixelX
11. lpRect.Bottom = (Text1.Top + Text1.Height) \ Screen.TwipsPerPixelY
12. CursorX = lpRect.Left + (lpRect.Right - lpRect.Left) \ 2
13. CursorY = lpRect.Top + (lpRect.Bottom - lpRect.Top) \ 2
14. Call SetCursorPos(CursorX, CursorY)
15. Call ClipCursorRect(lpRect)
16. For X = 1 To 200
17. Debug.Print Str$(X)
18. Next X
19. Call ClipCursorClear(0&)
20. End Sub
21. Add a Text Box control to Form1. Text1 is created by default.

22. Add the following Declare statements to the General Declarations section of
Form1 (note that each Declare statement must be typed as one single line of text):
23. Declare Sub ClipCursorRect Lib "User" Alias "ClipCursor" (lpRect As RECT)
24. Declare Sub SetCursorPos Lib "User" (ByVal X As Integer, ByVal Y As Integer)
25. Declare Sub ClipCursorClear Lib "User" Alias "ClipCursor" (ByVal lpRect As Long)
26. Add a new BAS module to the project. Module1.Bas is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


27. Add the following structure to Module1.Bas:
28. Type RECT
29. Left As Integer
30. Top As Integer
31. Right As Integer
32. Bottom As Integer
33. End Type

38: Determining the Number of


Colors the Screen Supports
Created: March 1, 1995

Abstract
When developing a Visual Basic® application that will be executed on different
computer systems, you may need to determine at run time how many colors are supported
by the target machine's display driver. This article explains how to do this using three
Windows® application programming interface (API) functions.

Calculating the Number of Colors Supported by the


Display Driver
The Windows® application programming interface (API) functions CreateDC,
DeleteDC, and GetDeviceCaps can be used to calculate how many colors are supported
by the display (screen) device driver.
First, you need to call the CreateDC function. This function creates a device context for
the specified device (in this case, it will be the DISPLAY device). To declare this
function within your program, include the following Declare statement in the Global
Module or General Declarations section of your application's form:
Declare Function CreateDC Lib "GDI" (ByVal lpDriverName As String, ByVal
lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As Any)
As Integer
Note that this Declare statement must be typed as one single line of text.
The CreateDC function must be called with four arguments, as follows:
lpDriverName This is a string containing the DOS filename of the device you want to
create a device context for.
lpDeviceName If the driver supports more than one device, you must specify the name of
the individual device to use.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


lpOutput This is the filename or name of a device that will receive the output.
lpInitData Set this to zero to use the device's default initialization values or a
DEVMODE structure that contains the values that you want to use.

Because we want to know how many colors the display device supports, we call the
CreateDC function by issuing the statement:
hDC = CreateDC("DISPLAY", "", "", "")
The hDC variable will contain a handle to the device context just created. We can use this
handle to retrieve information about the device through the GetDeviceCaps function.
The Declare statement for the GetDeviceCaps function is:
Declare Function GetDeviceCaps Lib "GDI" (ByVal hDC As Integer, ByVal nIndex As Integer) As Integer
This function takes two arguments: the device's handle and a constant value that specifies
the type of information the function should retrieve. In our case, we need to call the
GetDeviceCaps function twice to retrieve the number of color planes and the number of
bits per pixel for each plane. From these two values, we can calculate how many colors
the display driver supports.
Calculating the number of colors supported by the display device is simple. You need to
multiply the number of bits per pixel by the number of color planes. However, since each
bit can represent two colors, the number of bits must first be raised to the power of 2.
This will give you the total number of colors the display device currently supports.
The last step you need to do is to call the DeleteDC function. The Declare statement for
the DeleteDC function is:
Declare Function DeleteDC Lib "GDI" (ByVal hDC As Integer) As Integer
You must call the DeleteDC function to remove the device context that you have created
earlier. This removes the device context and also frees the windows resources occupied
by the device context.

Example Program
The program below shows how you can determine the number of colors that a device
supports. This example calculates the number of colors supported by the display (screen)
device driver. The result is displayed in the text box.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default.

3. Add the following Constants and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as one single
line of text):
4. Declare Function GetDeviceCaps Lib "GDI" (ByVal hDC As Integer, ByVal nIndex

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


5. As Integer) As Integer
6. Declare Function CreateDC Lib "GDI" (ByVal lpDriverName As String, ByVal
7. lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As Any)
8. As Integer
9. Declare Function DeleteDC Lib "GDI" (ByVal hDC As Integer) As Integer
10. Const BitsPixel = 12
11. Const Planes = 14
12. Add the following code to the Form_Load event for Form1:
13. Sub Form_Load()
14. Dim NumColors As Long
15. Dim hDC As Integer
16. Dim X As Integer
17. Dim PL As Integer
18. Dim BP As Integer
19. hDC = CreateDC("DISPLAY", "", "", "")
20. PL = GetDeviceCaps(hDC, Planes)
21. BP = GetDeviceCaps(hDC, BitsPixel)
22. NumColors = 2 ^ CLng(PL * BP)
23. X = DeleteDC(hDC)
24. Text1.Text = Str$(NumColors)
25. End Sub

39: Drawing Custom Borders Around


Visual Basic Forms
Created: March 1, 1995

Abstract
You can use the Visual Basic® Line method to add special effects to your application's
appearance. This article explains how you can draw a double-line border around a form's
window.

Drawing Borders Around Forms


The Line method can be used to draw individual lines or boxes on a Visual Basic® form.
To draw a box using the Line method, you tell the function the coordinates of the upper-
left and lower-right corners of the box. The syntax for the line method is:
Line(StartX,StartY) - (EndX,EndY), , B
The B argument tells the Line method to draw a box using the color (in this case we used
the default color, so we didn't specify anything for this argument) and the coordinates we
specify.
Because we want to draw a double-line border around a form, we need to actually draw
two boxes. First, however, we have to determine the height and width of the form. We
can do this by using the form's ScaleHeight and ScaleWidth properties. These two
properties tell us how many points high and wide the form is. Then we only need to set

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


the ScaleMode property to 3 (pixel) and draw two boxes around the form. This gives us
the double-line effect we want on the form.

Example Program
The following Visual Basic program draws a double-line border around a form's window
area.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Sub Form_Load()
4. Dim I As Integer
5. ScaleMode = 3
6. DrawWidth = 2
7. AutoRedraw = True
8. For I = DrawWidth - 1 To 4 Step 3
9. Line (I, I)-(ScaleWidth - 1 - I, ScaleHeight - 1 - I), , B
10. Next I
11. AutoRedraw = False
12. End Sub
If your form contains a menu, the double-line border will be drawn directly underneath
the menu so that the menu is not included within the box that is drawn.

40: Calculating the Number of


Bytes Used by Files Stored in a Directory
Created: April 1, 1995

Abstract
This article explains how you can use the Visual Basic® Dir$, FileName, and FileLen
functions to calculate the space used by files in a directory.

When DiskSpaceFree Is Not Enough


The DiskSpaceFree function found in SETUPKIT.DLL can tell you the amount of free
space available on the specified disk drive. However, if you need to determine how much
space is occupied by the files stored in a single directory, you will not be able to use this
function.

How, then, can you find out how much space is used by the files? One solution is to open
each file in the directory and move the files pointer to the end of the file. Then you can

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


find out how many bytes are stored in the file. This method, however, is far too slow
because each file must be individually opened and closed.
A better solution is to use the Dir$, FileName, and FileLen functions in Visual Basic®
to scan the directory and keep a running total of the number of bytes in each file:
• The Dir$ function retrieves the name of a file from a disk. To begin a search for
all files in a directory, pass the name of the directory as the first argument to Dir$
and the filename pattern to search for as the second argument to Dir$. Because
we want to retrieve the length of each individual file stored in the directory, we
use a wildcard (*.*) filename. As each name is retrieved from disk, the file's
length is added to the variable (in our example program below) FileSize. When no
more files exist in the directory, Dir$ will return an empty (NULL) string

• The FileLen function returns the total number of bytes used by the specified file.
Using a Do-While loop to retrieve the name and length of each file found in the
directory is quicker and less prone to disk errors than the other method described
above.

Example Program
The program below shows how you can use a Do-While loop to calculate how many
bytes are occupied by all the files stored in a directory. The Directory variable is set to
the path of the directory you want to work with. After the program has determined the
length of all files stored in the directory, it displays the result in the Text Box.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default.

3. Add the following code to the Form_Load event for Form1:


4. Sub Form_Load()
5. Dim FileName As String
6. Dim FileSize As Currency
7. Dim Directory As String
8.
9. Directory = "c:\windows\system\"
10. FileName = Dir$(Directory & "*.*")
11. FileSize = 0
12.
13. Do While FileName <> ""
14. FileSize = FileSize + FileLen(Directory & FileName)
15. FileName = Dir$
16. Loop
17.
18. Text1.Text = "Total bytes used = " + Str$(FileSize)
19. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


41: Creating Custom Bitmap Files
Created: April 1, 1995

Abstract
When creating a new Visual Basic® application, you can add much to the program's
appearance and ease of use if you include bitmap (.BMP) graphic files. Bitmap files are
simply pictures or images saved in a special format. These bitmaps are available from
many different sources, including commercial online services. This article explains how
you can create your own custom bitmap files.

Designing a Bitmap File from Scratch


If you subscribe to any kind of online communications system, such as the Internet or
Microsoft's own MSN (The Microsoft Network), you can probably find thousands of
bitmap graphic files. These .BMP files can be included as part of your Visual Basic®
application. However, you may not find just the right bitmap to use in your application.
In this case, you can use a paint program, such as Windows® Paintbrush, to create your
own bitmap, or create very simple textual bitmaps using the Picture Box control.

Let's suppose that you want to design a bitmap that contains just the name of your Visual
Basic program. By setting a string variable to the application's name and using the
SavePicture statement, you can quickly create the desired bitmap file. The syntax for the
SavePicture statement is:
SavePicture Image, <filename>
where <filename> is the name of the bitmap file you want to create and Image is the
.BMP, .ICO, or .WMF graphic file format. In the example program below, we have
drawn the text on the Picture Box control; therefore, to save it to disk with the
SavePicture statement, we must also include the control's name in the command string.

Example Program
The following Visual Basic program uses the Print method to draw the text on the
Picture Box and then saves the resulting image to disk as a bitmap file.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Picture Box control to Form1. Picture1 is created by default. Set its
AutoRedraw property to True.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Add the following code to the Form_Load event for Form1:
4. Sub Form_Load()
5. Dim Text As String
6. Text = "BMP Demo"
7. Picture1.Print Text
8. SavePicture Picture1.Image, "C:\DEMO.BMP"
9. End Sub
When you execute this program, Visual Basic will create a file called DEMO.BMP in the
root directory of the default disk drive. The file is a bitmap image file containing the text
"BMP Demo". This text is first printed on the Picture Box control, then the SavePicture
statement is used to create the actual bitmap file.

42: Using Bitmaps to Create


Custom Cursors
Created: April 1, 1995

Abstract
The ability to change the appearance of the default Windows® icon can add a great deal
of flexibility and user-friendliness to your Visual Basic® application. Visual Basic
provides twelve different cursor shapes to choose from. For example, the hourglass
cursor is generally used to tell the user of your application that some type of lengthy
operation is taking place. When that operation has been completed, the cursor reverts
back to its normal pointer shape. This article explains how you can create your own
custom cursor shapes.

Designing Cursors in Visual Basic


In simple terms, a cursor is a bitmap image. The bitmap must be 32 x 32 pixels in size.
Each cursor, or icon, consists of two separate bitmap images:
• The XOR bitmap is the first bitmap. Its image is combined with the display's
image by using the exclusive OR operator.

• The AND bitmap is the second bitmap. Its image consists of an AND mask that is
combined with the XORed bitmap. Used in this way, the AND and XOR
operators create the icon's new image.

In Visual Basic, you can use the Windows® CreateCursor function to create a
completely new and different cursor shape. To use this function within your program,

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


include the following Declare statement in the Global Module or General Declarations
section of your program:
Declare Function CreateCursor Lib "User" (ByVal hInstance As Integer, ByVal
nXhotspot As Integer, ByVal nYhotspot As Integer, ByVal nWidth As Integer,
ByVal nHeight As Integer, lpANDbitPlane As Integer, lpXORbitPlan As Integer)
As Integer
Note that this Declare statement must be typed as one single line of text.
The CreateCursor function takes six arguments, as follows:
hInstance The application program's handle. This is the instance of the
application that will own the newly created cursor.
nXhotspot, These integer values must be set to the X and Y coordinates of the
nYhotspot cursor's location.
nWidth The width (in pixels) of the cursor image.
nHeight The height (in pixels) of the cursor image.
lpANDbitPlane This is a string or long value containing a pointer to the AND
bitmap's data.
lpXORbitPlane This is a string or long value containing a pointer to the XOR
bitmap's data.

The CreateCursor function returns an integer value that identifies the cursor. If this
value is zero, the function was unable to create the new cursor.

Once you have created the cursor, you need to tell Windows to use the new cursor by
calling the GetClassWord and SetClassWord functions. Call the GetClassWord
function first so that you can save the cursor's handle. Then you can restore the original
cursor's image or set the cursor's image to a new one by calling the SetClassWord
function.
The Declare statements for the GetClassWord and SetClassWord functions are:
Declare Function GetClassWord Lib "User" (ByVal hWnd As Integer, ByVal nIndex
As Integer) As Integer

Declare Function SetClassWord Lib "User" (ByVal hWnd As Integer, ByVal nIndex
As Integer, ByVal wNewWord As Integer) As Integer
Note that each Declare statement must be typed as one line of text.

To retrieve the default cursor's handle, you pass two arguments to the GetClassWord
function: the handle of the window that owns the cursor, and a value specifying the type
of information you want to retrieve. In this case, we would specify the GCW_HCURSOR
constant (the cursor's default handle). In our application, we save the returned handle to
an integer variable so that we can use it again later in the program.
The SetClassWord function takes an additional argument: the new value for the class
information. The class information, of course, refers to the cursor's new image.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


When the SetClassWord function is executed, the new cursor is called into action. In our
Visual Basic program, we can restore the cursor to its previous image by simply calling
the SetClassWord function with the original cursor's handle (which is why we saved this
handle to a variable earlier in the program).

Example Program
The following Visual Basic program displays a different cursor image on the screen. In
this example, the XORBitPlane and ANDBitPlane values are used to manipulate the
cursor's current image, thereby creating a new cursor. You can create your own custom
cursors in Visual Basic by modifying the original cursor as this example program does.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Change Cursor".

3. Add the following code to the Click event for Command1:


4. Sub Command1_Click()
5. Dim X As Integer
6. Dim nWidth As Integer
7. Dim nHeight As Integer
8. Dim hInstance As Integer
9. Dim hCursor As Integer
10. Dim OldCursor As Integer
11. ReDim ANDbitPlane%(100), XORbitPlane%(100)
12. For C% = 0 To 100
13. ANDbitPlane%(C%) = 63
14. XORbitPlane%(C%) = 255
15. Next C%
16.
17. nWidth = 32
18. nHeight = 32
19. hInstance = GetModuleHandle("VB.EXE")
20. hCursor = CreateCursor(hInstance, 0, 0, nWidth, nHeight, ANDbitPlane%(0),
21. XORbitPlane%(0))
22.
23. OldCursor = GetClassWord(Form1.hWnd, -12)
24. X = SetClassWord(Form1.hWnd, -12, hCursor)
25. End Sub
26. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Restore Cursor".

27. Add the following code to the Click event for Command2:
28. Sub Command2_Click()
29. X = SetClassWord(Form1.hWnd, -12, OldCursor)
30. Dummy% = DeleteObject(hCursor)
31. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


32. Add the following Declare statements to the general declarations section of
Form1 (note that each Declare statement must be typed as a single line of text):
33. Declare Function CreateCursor Lib "User" (ByVal hInstance As Integer, ByVal
34. nXhotspot As Integer, ByVal nYhotspot As Integer, ByVal nWidth As Integer,
35. ByVal nHeight As Integer, lpANDbitPlane As Integer, lpXORbitPlan As Integer)
36. As Integer
37.
38. Declare Function DeleteObject Lib "GDI" (ByVal hObject As Integer) As Integer
39.
40. Declare Function GetClassWord Lib "User" (ByVal hWnd As Integer, ByVal nIndex
41. As Integer) As Integer
42.
43. Declare Function GetModuleHandle Lib "Kernel" (ByVal lpModuleName As String)
44. As Integer
45.
46. Declare Function SetClassWord Lib "User" (ByVal hWnd As Integer, ByVal nIndex
47. As Integer, ByVal wNewWord As Integer) As Integer
48.
49. Declare Function SetCursor Lib "User" (ByVal hCursor As Integer) As Integer

43: Activating and Deactivating


the Screen Saver
Created: April 2, 1995

Abstract
The Windows® operating system can be configured to run a screen saver after a specified
amount of time has elapsed without any keyboard or mouse activity being sensed.
However, in certain situations, you may need to deactivate the screen saver, do some
processing in your Visual Basic® application, and then reactivate the screen saver. This
article explains how to do this in a Visual Basic program.

Modifying System Parameters in Visual Basic


Whenever you want to change the system settings in Windows®, you usually work
through the Control Panel applet. However, the Windows SystemParametersInfo
function can also be used within a Visual Basic® application to retrieve or set these same
settings. To modify these settings in Visual Basic, you can use the function with a
constant that describes the setting you want to modify. In this example, we use the
SPI_GETSCREENSAVEACTIVE constant to control the screen saver program.

To declare this function in your Visual Basic program, include the following Declare
statement in the Global Module or General Declarations section of your program:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Declare Function SystemParametersInfo Lib "User" (ByVal uAction As Integer,
ByVal uParam As Integer, lpvParam As Any, ByVal fuWinIni As Integer)
As Integer
Note that this Declare statement must be typed as a single line of text.
The SystemParametersInfo function takes four arguments, as follows:
uAction An integer value that tells the function which Windows setting you want to
modify.
uParam An integer value based on the uAction argument.
lpvParam An integer, string, long, or data structure, depending on the uAction argument.
fuWinIni An integer value that determines whether the WIN.INI initialization file will
be updated. The SPIF_UPDATEINIFILE constant writes the changes to the
WIN.INI file, while the SPIF_SENDWININICHANGE constant sends a
WM_WININICHANGE message to all currently running Windows-based
applications to tell them of the system changes you have just made. If
fuWinIni is set to zero, no changes are made to WIN.INI.

For a list of the Windows settings you can modify, see the Knowledge Base article
Q97142, "How to Use SystemParametersInfo API for Control Panel Settings," referenced
at the end of this article.
This function returns an integer value. If this value is TRUE (nonzero), the changes were
made successfully. If the return value is FALSE (zero), the function was not able to make
the requested changes to the operating system.

Example Program
The following program demonstrates how the Windows screen saver can be temporarily
activated or deactivated. To turn the screen saver off, click the Deactivate command
button; to turn the screen saver on again, click the Activate command button.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following constant and Declare statements to the general declarations
section of Form1 (note that the Declare statement must be typed as a single line
of text):
3. Declare Function SystemParametersInfo Lib "User" (ByVal uAction As Integer,
4. ByVal uParam As Integer, lpvParam As Any, ByVal fuWinIni As Integer)
5. As Integer
6.
7. Const SPI_SETSCREENSAVEACTIVE = 17
8. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Activate".

9. Add the following code to the Click event for Command1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


10. Sub Command1_Click()
11. Dim ret As Integer
12. ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, True, 0, 0)
13. End Sub
14. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Deactivate".

15. Add the following code to the Click event for Command2:
16. Sub Command2_Click()
17. Dim ret As Integer
18. ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, False, 0, 0)
19. End Sub

44: Modifying File Attributes


Created: April 2, 1995

Abstract
Every time a new file is created, the operating system gives the file one or more special
attribute settings. In your Visual Basic® program, you can use the GetAttr and SetAttr
functions to modify a file's attribute settings.

What Are File Attributes?


A file, whether it is an executable program file, a data file, or other type, is stored to disk
with one or more file attributes. The following table lists the seven possible attributes that
can be given to a file.
Attribute Value Description
Normal 0 Data can be read from or written to the file.
Read Only 1 Data can be read from but not written to the file.
Hidden 2 The file cannot be seen in the directory list.
System 4 The file is a system file that is used only by MS-DOS® or
Windows®.
Volume 8 The file is the special name given to the disk. Only one volume
Label label can be given to a disk as its unique identifier.
Directory 16 The file is a subdirectory.
Archive 32 The file has been modified since backup was last performed.

You can determine which attributes a file has been given by using the Visual Basic®
GetAttr function. The syntax for this function is:
GetAttr(filename)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


When this function is executed, it returns an integer value that tells you which attribute
has been given to the file. A file that is read-only, for example, will return a value of 1.
Because a file may have more than one attribute associated with it, you can use the And
operator to perform a bit-wise comparison.

Changing File Attributes


Visual Basic also provides the SetAttr function, which allows you to change a file's
attribute setting. The syntax for this function is:

SetAttr(filename),(attribute)
When changing a file's attributes, you must keep in mind that a file can have more than
one attribute set at any given time. You must use the And operator to change only the
desired bit in the attribute flag. The following table tells you the mask values you can use
to change only the individual bit you are interested in.
Attribute Mask Value
Archive 31
System 59
Hidden 61
Read Only 62

You can also remove all the attributes of a file by calling SetAttr with a value of zero as
the Attribute value.

Example Program
The following program shows how you can use the GetAttr and SetAttr functions in a
Visual Basic application. Execute this program by pressing the F5 function key. Click the
"Test Archive Bit" command button. The program will respond by testing the
AUTOEXEC.BAT file's attribute flag to see if its Archive bit is set. If the Archive bit is
set, the program responds with the message, "File has archive bit set." Click the "Set
Archive Off" command button. This resets the file's Archive bit to zero; the resulting
message is displayed in the Text Box.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Test Archive Bit".

3. Add the following code to the Click event for Command1:


4. Sub Command1_Click()
5. FileName = "c:\autoexec.bat"
6. X = GetAttr(FileName)
7. If X = 32 Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Text1.Text = "File has Archive bit set."
9. Else
10. Text1.Text = "File does not have Archive bit set."
11. End If
12. End Sub
13. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Set Archive Off".

14. Add the following code to the Click event for Command2:
15. Sub Command2_Click()
16. FileName = "c:\autoexec.bat"
17. SetAttr FileName, GetAttr(FileName) And 31
18. Text1.Text = "Archive attribute set to Off."
19. End Sub
20. Add a Text Box control to Form1. Text1 is created by default.

45: Using BitBlt to Display


Bitmaps
Created: April 2, 1995

Abstract
The Windows® BitBlt function can be used to display a bitmap image on a Visual
Basic® form. The bitmap image can be an image stored in memory or an image stored on
disk. This article demonstrates how the BitBlt function can display a bitmap file on the
screen.

Copying Bitmaps from One Source to Another


In a Visual Basic® application, you can use the BitBlt function to copy a bitmap from
one device context to another as long as both device contexts are compatible. (See "Tip
31: Creating the Windows Wallpaper Effect," for a complete explanation of BitBlt.)

If you're trying to copy a bitmap and the source and destination contexts are not
compatible, you must first use the CreateDIBitmap function. This function takes a
device-independent bitmap and converts it to a device-dependent bitmap. To declare this
function within your program, include the following Declare statement in the General
Declarations section of your form:
Declare Function CreateDIBitmap Lib "GDI" (ByVal hDC As Integer, lpInfoHeader
As BITMAPINFOHEADER, ByVal dwUsage As Long, ByVal lpInitBits As String,
lpInitInfo As BITMAPINFO, ByVal wUsage As Integer) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Note that this statement must be typed as a single line of text.
The CreateDIBitmap function requires the following arguments:
hDC An integer value set to the device context's handle. This device context
describes the device-dependent bitmap that will be created.
lpInfoHeader A BITMAPINFOHEADER structure that describes the device-
independent bitmap's format.
dwUsage If the CBM_INIT constant is specified, the bitmap is initialized as per the
lpInitBits and lpInitInfo parameters. If the bitmap data should not be
initialized, this long value should be set to zero.
lpInitBits A string containing the bitmap data in device-independent format, or a
long value containing a pointer to same.
lpInitInfo A BITMAPINFO structure that describes the lpInitBits device-
independent bitmap.
wUsage Set to the DIB_PAL_COLORS constant to specify the color table
relative to the currently selected palette, or to DIB_RGB_COLORS if the
color table contains RGB colors.

CreateDIBitmap returns an integer value of 1 or greater as the newly created bitmap's


handle, or zero if the function was unable to create the bitmap.
Once you have created the compatible bitmap, you need to create a compatible memory
device context. The CreateCompatibleDC function will do this work. This function's
declaration is:
Declare Function CreateCompatibleDC Lib "GDI" (ByVal hDC As Integer) As Integer
This function needs only one argument to be passed to it: the device context's handle. If
you specify the handle as being zero, a device context that is compatible with the screen
will be created.

Now you can call the BitBlt function to draw the bitmap image on the screen. This works
exactly as if you had used the LoadPicture method.
You should also use the DeleteDC and DeleteObject functions to release the Windows
resources used by the device context and bitmap you have just created.

Example Program
The following program shows how you can quickly draw a bitmap image on your Visual
Basic form. The TARTAN.BMP file shipped with Windows® is used in this example,
but any other bitmap that is 32 x 32 pixels can be substituted.
1. Create a new project in Visual Basic. Form1 is created by default. Set its
AutoRedraw property to True.

2. Add the following code to the Form_Load event for Form1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Sub Form_Load()
4. Call DrawBitMap
5. End Sub
6. Add the following Constant and Declare statements to the General Declarations
section of your program (note that each statement must be typed as a single line of
code):
7. Const CBM_INIT = &H4&
8. Const SRCCOPY = &HCC0020
9.
10. Declare Function CreateDIBitmap Lib "GDI" (ByVal hDC As Integer, lpInfoHeader
11. As BITMAPINFOHEADER, ByVal dwUsage As Long, ByVal lpInitBits As String,
12. lpInitInfo As BITMAPINFO, ByVal wUsage As Integer) As Integer
13.
14. Declare Function DeleteObject Lib "GDI" (ByVal hObject As Integer) As Integer
15.
16. Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X As Integer,
17. ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal
18. hSrcDC As Integer, ByVal XSrc As Integer, ByVal YSrc As Integer, ByVal dwRop
19. As Long) As Integer
20.
21. Declare Function CreateCompatibleDC Lib "GDI" (ByVal hDC As Integer) As Integer
22.
23. Declare Function SelectObject Lib "GDI" (ByVal hDC As Integer, ByVal hObject As
24. Integer) As Integer
25.
26. Declare Function DeleteDC Lib "GDI" (ByVal hDC As Integer) As Integer
27. Create a new function called ReadBitmapFile. Add the following code to this
function:
28. Function ReadBitmapFile(fname As String, bm As BITMAPINFO, bmdata As String)
29. As Integer
30. ' Function only handles 16-color uncompressed bitmaps.
31. Dim A As String
32. Dim I As Integer
33. Dim BitMapSize As Long
34.
35. Open fname$ For Binary As #1
36. A = String$(2, 0)
37. Get #1, , A
38. If A <> "BM" Then Close #1: Exit Function
39.
40. Seek #1, 15: ' Move to next bit.
41. Get #1, , bm.bmiHeader.biSize
42. Get #1, , bm.bmiHeader.biWidth
43. Get #1, , bm.bmiHeader.biHeight
44. Get #1, , bm.bmiHeader.biPlanes
45. Get #1, , bm.bmiHeader.biBitCount
46.
47. If bm.bmiHeader.biBitCount <> 4 Then
48. Close #1
49. ReadBitmapFile = -1
50. Exit Function
51. End If
52.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


53. Get #1, , bm.bmiHeader.biCompression
54. If bm.bmiHeader.biCompression <> 0 Then
55. Close #1
56. ReadBitmapFile = -1
57. Exit Function
58. End If
59.
60. Seek #1, 47: ' Skip to next bit.
61. Get #1, , bm.bmiHeader.biClrUsed
62. ' The number of RGB quads depends on the number of
63. ' colors - the defaults are enumerated below if bmnumcols& = 0.
64. If bm.bmiHeader.biClrUsed = 0 Then
65. bm.bmiHeader.biClrUsed = 16
66. End If
67.
68. If bm.bmiHeader.biClrUsed <> 16 Then
69. Close #1
70. ReadBitmapFile = -1
71. Exit Function
72. End If
73.
74. Get #1, , bm.bmiHeader.biClrImportant
75. ' We are now at offset 55 in the file
76. For I = 0 To bm.bmiHeader.biClrUsed - 1
77. ' Get RGB quads and set palette entries as appropriate. Note that BASIC
78. ' palette entries are not the same as standard Windows palette entries.
79. Get #1, , bm.bmicolors(I)
80. Next I
81.
82. ' Now read the bitmap information
83. BitMapSize = BitMapRowSize(bm.bmiHeader.biWidth, bm.bmiHeader.biBitCount)
84. * bm.bmiHeader.biHeight
85. bmdata = String$(BitMapSize, 0)
86. Get #1, , bmdata
87. Close #1
88. End Function
89. Add a new function called BitMapRowSize to the project. Add the following
code to this function:
90. Function BitMapRowSize(bmwidth As Long, bmbitspixel) As Long
91. ' Given bitmap width in pixels, and the number of bits
92. ' per pixel, calculate the bitmap row size in bytes.
93. Dim B As Integer
94. B = bmwidth * bmbitspixel
95. If (B Mod 32) <> 0 Then
96. BitMapRowSize = ((B + 32 - (B Mod 32)) \ 8) ' Pad to nearest 4 bytes.
97. Else
98. BitMapRowSize = B \ 8
99. End If
100. End Function
101. Add a new Module to your project. Module1.Bas is created by default.
Add the following Type structures to Module1.Bas:
102. Type BITMAPINFOHEADER ' 40 bytes
103. biSize As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


104. biWidth As Long
105. biHeight As Long
106. biPlanes As Integer
107. biBitCount As Integer
108. biCompression As Long
109. biSizeImage As Long
110. biXPelsPerMeter As Long
111. biYPelsPerMeter As Long
112. biClrUsed As Long
113. biClrImportant As Long
114. End Type
115.
116. Type BITMAPINFO
117. bmiHeader As BITMAPINFOHEADER
118. bmicolors(15) As Long
119. End Type
120. Add a new function called DrawBitMap to your project. Add the
following code to this function:
121. Sub DrawBitMap()
122. Dim hbmap As Integer
123. Dim bitmap As String
124. Dim RC As Integer
125. Dim chdc As Integer
126. Dim bm As BITMAPINFO
127. Dim I As Integer
128. RC = ReadBitmapFile("C:\windows\tartan.bmp", bm, bitmap)
129. hbmap = CreateDIBitmap(Me.hDC, bm.bmiHeader, CBM_INIT, bitmap, bm, 0)
130. chdc = CreateCompatibleDC(Me.hDC)
131. If chdc <> 0 And hbmap <> 0 Then
132. RC = SelectObject(chdc, hbmap)
133. ' Just put it up at 10,10 for now.
134. RC = BitBlt(Me.hDC, 10, 10, bm.bmiHeader.biWidth, bm.bmiHeader.biHeight,
135. chdc, 0, 0, SRCCOPY)
136. RC = DeleteDC(chdc)
137. RC = DeleteObject(hbmap)
138. End If
139. End Sub
[Note: The RC = BitBlt line above must be typed or pasted as a single line of text.—Ed.]

46: Deleting Entire Directories


Created: April 3, 1995

Abstract
Visual Basic® offers several commands to manipulate disk files. You can delete a file
with the Kill statement and delete a directory with the RmDir function. If the Visual

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Basic application you are creating needs to remove all the files stored in a directory, these
two commands will do this for you. This article shows how you can use Visual Basic file
commands to remove all files stored in a subdirectory, including those files stored under
nested subdirectories.

Recursively Deleting Files and Directories from a Disk


The Visual Basic® Kill statement deletes a specified file from the disk. To delete a
specific file, you simply pass the filename to the Kill statement. The Kill statement
supports the wildcard "*" and "?" characters. By the same token, the RmDir statement in
Visual Basic deletes a directory from the disk. To delete a directory successfully, the
directory must be empty—that is, all files in the directory must have been previously
deleted.

It's easy to see how the Kill and RmDir statements can be used to delete entire directory
structures very quickly. The example program below starts off by changing to the target
directory and then deleting all files stored in that directory. Once all the files from this
directory have been deleted, the program removes the empty directories it finds. This
process is repeated until the entire target directory and its associated files have been
successfully removed from the disk.
The example program only processes files that have a file attribute of Normal. This
means that you would have to modify the program to search for and delete files with
other attributes, such as hidden, read-only, or volume label files.

Example Program
The following program will delete all the files in a directory. Note that in this example it
is assumed that a directory named TEST exists on drive C. All files, including nested
subdirectories and their files, will be removed from disk with this program.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Remove Entire Directory".

3. Add the following code to the Click event for Command1:


4. Sub Command1_Click()
5. Dim TempDir As String
6. TempDir = "C:\TEST"
7. Nuke TempDir
8. End Sub
9. Add a new subroutine procedure called Nuke. Add the following code to this
procedure:
10. Sub Nuke(DirName As String)
11. Const ATTR_NORMAL = 0
12. Const ATTR_DIRECTORY = 16

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


13.
14. Dim OriginalDir, FileName, NextFileName As String
15.
16. OriginalDir = CurDir$
17. ChDir DirName
18. FileName = Dir$("*.*", ATTR_NORMAL)
19. Do While FileName <> ""
20. Kill FileName
21. FileName = Dir$
22. Loop
23.
24. Do
25. FileName = Dir$("*.*", ATTR_DIRECTORY)
26. While FileName = "." Or FileName = ".."
27. FileName = Dir$
28. Wend
29.
30. If FileName = "" Then Exit Do
31. Nuke (FileName)
32. Loop
33.
34. ChDir OriginalDir
35. RmDir DirName
36. End Sub

47: Changing the Default MS-DOS


Icon
Created: April 5, 1995

Abstract
When you execute an MS-DOS® program from within a Visual Basic® application, your
MS-DOS program uses the default MS-DOS icon. This article explains how, using some
functions from Windows®, you can change this default icon to a different icon of your
choice.

Substituting One Icon for Another


The key to changing the default MS-DOS® icon to one of your choosing is to use the
Windows® ExtractIcon function. The Declare statement for the ExtractIcon function
is:
Declare Function ExtractIcon Lib "Shell" (ByVal hInst%, ByVal lpExeName$, ByVal
hIcon%) As Integer
Note that this statement must be typed as a single line of code.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The ExtractIcon function requires three arguments, as follows:
hInst The program's instance handle.
lpszExeName The name of the program that contains the icon you want to extract.
iIcon The index number associated with the icon you want to retrieve. If this
value is -1, the total number of icons in the file will be returned.

To extract an icon from a program's executable file (.EXE) or from a dynamic-link


library (.DLL) file, you need to know the application's instance handle and the index
number of the specific icon you want to retrieve from the file. Calling the ExtractIcon
function returns the handle of the icon.

The next step is to use the SetClassWord function to retrieve the handle of the default
icon used by the specified window. After the Shell method has executed the TEST.BAT
file (in our demonstration program below), we use the SetClassWord function to
actually change the default icon to another icon.
You should note that we are changing the icon for the MS-DOS window to another icon,
and that this new icon will be used by all other applications within this same window
class (DOS). This is why, in the demonstration program below, we take the final step of
deleting the new MS-DOS icon just before the program is terminated.

Example Program
The following program shows how the default MS-DOS icon can be changed to a
different icon from within a Visual Basic application.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each statement must be typed as a single line of text):
3. Const SWP_NOSIZE = 1
4. Const SWP_NOMOVE = 2
5. Const SWP_NOACTIVATE = &H10
6. Const SWP_SHOWWINDOW = &H40
7. Const SWP_HIDEWINDOW = &H80
8. Const SWP_FLAGS = SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOACTIVATE
9. Const SWP_SHOW = SWP_SHOWWINDOW Or SWP_FLAGS
10. Const SWP_HIDE = SWP_HIDEWINDOW Or SWP_FLAGS
11. Const HWND_BOTTOM = 1
12. Const GCW_HICON = (-14)
13. Const GCW_HMODULE = (-16)
14.
15. Declare Function GetModuleUsage Lib "Kernel" (ByVal hWnd%) As Integer
16.
17. Declare Function ExtractIcon Lib "Shell" (ByVal hInst%, ByVal lpExeName$,
18. ByVal hIcon%) As Integer
19.
20. Declare Function DestroyIcon Lib "User" (ByVal hIcon%) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


21.
22. Declare Function FindWindow Lib "User" (ByVal lpClassName As Any, ByVal
23. lpCaption As Any) As Integer
24.
25. Declare Function SetWindowPos Lib "User" (ByVal h%, ByVal hb%, ByVal X%, ByVal
26. y%, ByVal cx%, ByVal cy%, ByVal F%) As Integer
27.
28. Declare Function GetClassWord Lib "User" (ByVal hWnd%, ByVal nIndex%) As Integer
29.
30. Declare Function SetClassWord Lib "User" (ByVal hWnd%, ByVal nIndex%, ByVal
31. wNewWord%) As Integer
32. Add the following code to the Form_Load event for Form1:
33. Sub Form_Load()
34. Dim DosFile As String, IconFile As String
35. DosFile = "C:\TEST.PIF"
36. IconFile = "C:\VB\ICONS\ARROWS\ARW01RT.ICO"
37. Call LaunchPif(DosFile, IconFile)
38. End Sub
39. Create a new subroutine procedure called LaunchPif. Add the following code to
this procedure:
40. Sub LaunchPif(PifFile As String, IconName As String)
41. Dim Res As Integer
42. Dim MyInst As Integer
43. Dim PifIcon As Integer
44. Dim OldIcon As Integer
45. Dim PifhWnd As Integer
46. Dim PifInst As Integer
47.
48. PifInst = Shell(PifFile, 2)
49. MyInst = GetClassWord((Form1.hWnd), GCW_HMODULE)
50. If Dir$(IconName) <> "" Then
51. IconName = IconName & Chr$(0)
52. PifIcon = ExtractIcon(MyInst, IconName, 0)
53. Else
54. PifIcon = 0
55. End If
56. If GetModuleUsage(PifInst) <> 0 And PifIcon > 0 Then
57. PifhWnd = FindWindow(0&, "TEST")
58. OldIcon = SetClassWord(PifhWnd, GCW_HICON, PifIcon)
59. Res = SetWindowPos(PifhWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_HIDE)
60. Res = SetWindowPos(PifhWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOW)
61. End If
62. Do While GetModuleUsage(PifInst) <> 0
63. DoEvents
64. Loop
65.
66. If PifIcon > 0 Then
67. Res = SetClassWord(PifhWnd, GCW_HICON, OldIcon)
68. Res = DestroyIcon(PifIcon)
69. End If
70. End Sub
71. Using Notepad, create a DOS batch file called TEST.BAT in the root directory of
drive C. Type the "dir" command as the text for this batch file.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


72. Create a new .PIF file in the root directory of drive C. Set the program filename to
TEST.BAT. Click on the windowed and background options to run TEST.BAT as
a windowed, background task.

73. Save the project as DOSBOX.MAK. Select Make EXE File from Visual Basic's
File menu to create an executable program file called C:\DOSMAK.EXE.

74. In Program Manager, add the DOSBOX.EXE program to an existing program


group or create a new program group. In the properties window, type the
program's filename as "C:\DOSBOX.EXE" and click on the Run Minimized
option.
From Program Manager, execute the DOSBOX.EXE program file. You will see the
TEST icon temporarily displayed on the desktop. The TEST icon has been modified to
show the new arrow icon instead of the regular MS-DOS icon used by default. Next, the
program will display its own icon on the desktop. Click the DOSBOX icon and select
Close from its Control menu to terminate the program.

48: Copying a Window's Client


Area to a Bitmap
Created: April 6, 1995

Abstract
In a Visual Basic® application, you may want to save the contents of a window's client
area to the Windows® Clipboard application. You can save data to the Clipboard in a
variety of formats. This article explains how the client area of a window can be saved to
the Clipboard in Windows as a bitmap file.

Creating Bitmap Formats in the Clipboard


To save a window's client area to a bitmap format in the Windows® Clipboard
application, you need to create a memory device context that is compatible with the
bitmap file format. (A memory device context is simply a block of memory that
represents a display surface, such as a window.) Next, you use the SelectObject function
to save the object. The BitBlt function is then used to copy the image from the memory
device context to a bitmap.
The Windows application programming interface (API) provides several functions that
let your program modify the Clipboard application. In the example below, we first open
communication with the Clipboard by calling the OpenClipboard function. Likewise,

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


when we have finished using the Clipboard, we execute the CloseClipboard function. To
actually write data to the Clipboard, we call the SetClipboardData function.
To declare the SetClipboardData function within your program, include the following
Declare statement in the Global Module or General Declarations section of your form:
Declare Function SetClipboardData Lib "User" (ByVal wFormat As Integer, ByVal
hMem As Integer) As Integer
Note that this statement must be typed as one single line of code.

The SetClipboardData function requires two arguments, as follows:


wFormat An integer value containing the Clipboard format that should be used to write
data to the Clipboard. The CONSTANT.TXT file contains a list of the
Clipboard data formats that can be used.
hMem An integer value containing the memory block's global memory handle. This
data must be in the same format as specified by the wFormat argument.

Because we want to save the contents of a window to the Clipboard, we call the
SetClipboardData function by passing it the MF_BITMAP constant, which tells the
function to save the window's data in the Clipboard in bitmap file format.

Example Program
The following program shows how to copy the contents of a window's client area (the
entire window, in this case) to the Clipboard in bitmap format. After executing this
program, you can verify that the data was saved to the Clipboard by running Clipboard
Viewer from the Accessories group in Program Manager.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each statement must be typed as a single line of text):
3. Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X As Integer,
4. ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal
5. hSrcDC As Integer, ByVal XSrc As Integer, ByVal YSrc As Integer, ByVal dwRop
6. As Long) As Integer
7.
8. Declare Function GetWindowDC Lib "User" (ByVal hWnd As Integer) As Integer
9.
10. Declare Function CreateCompatibleDC Lib "GDI" (ByVal hDC As Integer) As Integer
11.
12. Declare Function CreateCompatibleBitmap Lib "GDI" (ByVal hDC As Integer, ByVal
13. nWidth As Integer, ByVal nHeight As Integer) As Integer
14.
15. Declare Function SelectObject Lib "GDI" (ByVal hDC As Integer, ByVal hObject As
16. Integer) As Integer
17.
18. Declare Function OpenClipboard Lib "User" (ByVal hWnd As Integer) As Integer
19.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


20. Declare Function SetClipboardData Lib "User" (ByVal wFormat As Integer, ByVal
21. hMem As Integer) As Integer
22.
23. Declare Function CloseClipboard Lib "User" () As Integer
24.
25. Const SRCCOPY = &HCC0020
26. Const MF_BITMAP = &H4
27. Add the following code to the Form_Load event for Form1:
28. Sub Form_Load()
29. Dim hwnddc As Integer
30. Dim hmemdc As Integer
31. Dim hbitmap As Integer
32. Dim oldbitmap As Integer
33. Dim res As Integer
34. Form1.ScaleMode = 3
35. hwndc% = GetWindowDC(Form1.hWnd)
36. hmemdc% = CreateCompatibleDC(Form1.hDC)
37. hbitmap% = CreateCompatibleBitmap(Form1.hDC, Form1.ScaleWidth, Form1.ScaleWidth)
38.
39. oldbitmap% = SelectObject(hmemdc%, hbitmap%)
40.
41. res% = BitBlt(hmemdc%, 0, 0, Form1.ScaleWidth, Form1.ScaleHeight, hwndc%, 0, 0,
SRCCOPY)
42.
43. res% = OpenClipboard(Form1.hWnd)
44. res% = SetClipboardData(hbitmap%, MF_BITMAP)
45. res% = CloseClipboard()
46. End Sub

49: Centering Forms


Created: April 10, 1995

Abstract
To make your Visual Basic® applications more visually attractive, you can center each
form on your screen. Centering forms can be done by using the Height and Width
properties of the form and then using the Move method to position the form at its new
location on the screen.

Centering a Form on the Screen


To center a Visual Basic® form on the screen at run time is very simple. The Screen
object can tell you the width and height of a specific form, the Height property reports
the height of a form or control, and the Width property reports the width of a form or
control.
If you subtract the form's height from the screen's height and divide the result by 2, you
can center the form vertically on the screen. Likewise, subtracting the form's width from
the screen's width and dividing the result by 2 gives you the position needed to center the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


form vertically on the screen. It is then a simple matter of executing the Move method to
actually center the form on the display screen.

Example Program
The following program shows how you can center a form in your Visual Basic
application.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Sub Form_Load()
4. Dim TopCorner As Integer
5. Dim LeftCorner As Integer
6.
7. If Form1.WindowState <> 0 Then Exit Sub
8.
9. TopCorner = (Screen.Height - Form1.Height) \ 2
10. LeftCorner = (Screen.Width - Form1.Width) \ 2
11. Form1.Move LeftCorner, TopCorner
12.
13. End Sub

50: Using Drag and Drop in List


Box Controls
Created: April 10, 1995

Abstract
Many Windows®-based applications allow you to "drag" an item from one location on
the screen to another and then "drop" the selected item in a new position. This technique
is called drag and drop. Items can be dragged from one control to another, or even
between two different Windows-based applications. This article explains how you can
use the drag-and-drop technique to move individual items to new positions within a list
box control.

Moving Items Around in a List Box


The Windows SendMessage function can be used within a Visual Basic® application to
simulate dragging and dropping items from one location to another within a List Box.
This technique allows you to selectively sort the contents of a List Box in situations
where the Sorted property cannot be used. In the demonstration program below, the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


height of each row in the List Box is used along with the SendMesage function to
determine the new location for the selected item.

Example Program
The following Visual Basic program lets you "drag and drop" an item in a List Box to
another position within the same List Box. This, in effect, allows you to selectively sort
the items in a List Box without using the Sorted property.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as one single line
of text):
3. Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg
4. As Integer, ByVal wParam As Integer, lParam As Any) As Long
5. Const LB_GETTOPINDEX = &H400 + 15
6. Add the following code to the Form_Load event for Form1:
7. Sub Form_Load()
8. Dim i As Integer
9. Me.Show
10. For i = 1 To 10
11. List1.AddItem "Item " & i
12. Next i
13. End Sub
14. Add a List Box control to Form1. List1 is created by default.

15. Add the following code to the MouseDown event for List1:
16. Sub List1_MouseDown(Button As Integer, Shift As Integer, X As Single,
17. Y As Single)
18. Static SecondClick As Integer
19. Static DropText As String
20. Dim RowHeight As Single
21. Dim TopI As Integer, InsertI As Single
22. TopI = SendMessage(List1.hWnd, LB_GETTOPINDEX, 0&, 0&)
23. RowHeight = TextHeight("X")
24. InsertI = Y \ RowHeight
25. If Button = 2 And DropText = "" And SecondClick = False Then
26. List1.ListIndex = InsertI + TopI
27. DropText = List1.Text
28. MousePointer = 10
29. SecondClick = True
30. List1.RemoveItem InsertI + TopI
31. ElseIf Button = 2 And Len(DropText) > 2 And SecondClick = True Then
32. If InsertI + TopI < List1.ListCount - 1 Then
33. List1.AddItem DropText, InsertI + TopI
34. Else
35. List1.AddItem DropText, InsertI + TopI + 1
36. End If
37. DropText = ""

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


38. SecondClick = False
39. MousePointer = 0
40. End If
41. End Sub
When you execute this program, the List Box is filled with ten items. Using the right
mouse button, click on an individual item in the List Box. The item you just clicked on is
removed from the List Box. Move the mouse pointer to the position you want to move
the selected item to and click the right mouse button a second time. The selected item is
moved to the new position within the List Box.

51: Displaying a Status Bar While


Executing Lengthy Procedures
Created: April 10, 1995

Abstract
When a Visual Basic® program is executing a time-consuming task, you can display the
progress of that task by using a status bar in the program. This article explains how you
can use a three-dimensional control to add this feature to your Visual Basic programs.

Showing the Percentage Status Bar


To add a status bar to your Visual Basic® program, you use a three-dimensional (3D)
Panel control. Your program displays a blue horizontal bar that is filled from left to right.
As your program updates the percentage-completed variable, the 3D Panel is also
updated. In the example program below, we set the X variable equal to the number of
bytes in the TEST.COM file. As each block of 100 bytes is read from the file, the
FloodPercent property of the 3D Panel control is adjusted accordingly. This is how the
horizontal bar appears to grow from left to right until the entire file has been processed.

Example Program
The following program shows how you can add a percentage-completed status bar to
your own Visual Basic application. It is assumed that you have a temporary file called
TEST.COM in the root directory of drive C. As this program reads data from the
temporary file, it displays the percentage completed on the status bar. The program ends
when the entire file has been processed, showing a "100 percent completed" message in
the status bar.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Label control to Form1. Label1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Add a 3D Panel control to Form1. Panel3D1 is created by default. Set its
FloodType property to 1-Flood from left to right. Set its FloodShowPct property
to True.

4. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Process File".

5. Add the following code to the Click event for Command1:


6. Sub Command1_Click()
7.
8. Dim inp As String * 100
9. x = FileLen("c:\test.com")
10. Open "c:\test.com" For Binary As #1
11. Label1 = "Working..."
12. Label1.Refresh
13. i = 0
14. While Not EOF(1)
15. Get #1, , inp
16. i = i + 100
17. Panel3D1.FloodPercent = 100 * i / x
18. Text1 = inp
19. Wend
20. Close #1
21. Label1 = "Done"
22. End Sub
23. Add a Text Box control to Form1. Text1 is created by default.

52: Modifying a Window's Title Bar


Caption
Created: April 10, 1995

Abstract
Almost every Windows®-based application displays a caption in its title bar. This
caption is usually the application's name. This article will tell you how you can modify
the caption of an application.

Replacing the Title Bar Caption of a Window


The Windows® application programming interface (API) provides two functions you can
use in your program to modify the caption displayed in a window's title bar. The
GetWindowText function retrieves the caption of the title bar and the SetWindowText
function sets the caption of the title bar to a new string.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To declare the GetWindowText function within your Visual Basic® program, include
the following Declare statement in the Global Module or General Declarations section of
your form:
Declare Function GetWindowText Lib "User" (ByVal hWnd As Integer, ByVal lpString
As String, ByVal aint As Integer) As Integer
Note that this Declare statement must be typed as one single line of text.
The GetWindowText function requires three arguments, as follows:
hWnd An integer value containing the window's handle.
lpString A string buffer long enough to hold the caption text in the titlebar.
aint An integer value set to the length of lpString.

After calling this function, GetWindowText will return an integer value set to the length
of the caption's text. This length count does not include the string's terminating NULL
character.
The Windows API SetWindowText function allows you to set the contents of the title
bar's caption to a string of your choice. The SetWindowText function requires two
arguments, as follows:
hWnd An integer value containing the window's handle.
lpString A string containing the window's new title bar caption.

The example program below retrieves the text stored in the title bar ("Caption Demo")
and adds the current time to the string. However, we must take a couple of steps to ensure
that Visual Basic displays the new caption correctly.
First, we use the Left$ function to remove the original caption's terminating NULL byte.
Later on in the program, we retrieve the current time as a string and append it to the end
of the original caption stored in the title bar. If we did not remove the terminating NULL
byte from the original caption's string, Visual Basic would stop printing the text before it
reached the time portion of the string.
Approximately every ten seconds, the Timer control is used to update the target
window's title bar. However, another step must be included in this routine to prevent
Windows from appending the time string more than once.

The example program below adds the current time to the window's title bar. The first
time the Timer executes its code, the original title bar's caption is "Caption Demo". The
second time the routine is executed, the original title bar's caption is "Caption Demo
08:12:11" (or whatever the current time happens to be). If we keep appending the current
time to the original title bar's caption, the string will soon contain multiple but different
time stamps. This is not what we want—we only want one time stamp to be included in
the string. This is why the example program searches for the first colon character in the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


original string (":") and sets the "original" caption's text back to only the actual "Caption
Demo" text.

Example Program
The following program shows how to modify an application's title bar. This program
adds the current time to the caption of the target window—in this case, our own Visual
Basic program. After executing this program, Visual Basic will add the current time to
the caption of Form1. The entire string will be updated approximately every ten seconds.

1. Create a new project in Visual Basic. Form1 is created by default. Set its Caption
property to "Caption Demo".

2. Add a Timer control to Form1. Timer1 is created by default. Set its Interval
property to 10000.

3. Add the following Declare statements to the General Declarations section of


Form1 (note that each Declare statement must be typed as a single line of code):
4. Declare Function GetWindowText Lib "User" (ByVal hWnd As Integer, ByVal lpString
5. As String, ByVal aint As Integer) As Integer
6. Declare Function SetWindowText Lib "User" (ByVal hWnd As Integer, ByVal lpString
7. As String) As Integer
8. Add the following code to the Timer1-Timer event for Timer1:
9. Sub Timer1_Timer()
10. Timer1.Enabled = True
11. Dim X As Integer
12. Dim CurTime As String * 35
13. Dim CapText As String * 35
14. Dim NewCap As String * 35
15. Dim CapLength As Integer
16. Dim L As Integer
17.
18. CapLength = 30
19. X = GetWindowText(Form1.hWnd, CapText, CapLength)
20. ' X now equals the length of CapText not including the terminating
21. ' NULL character. We MUST remove the NULL character.
22. CapText = Left$(CapText, X)
23. ' The original caption is up to the first colon in the time string,
24. ' minus 3 bytes to go back to end of original caption's text.
25. L = InStr(CapText, ":")
26.
27. If L = 0 Then
28. GoTo Adjust
29. End If
30.
31. CapText = Left$(CapText, L - 3)
32. Adjust:
33. NewCap = ""
34. CurTime = Time$
35. NewCap = RTrim$(CapText) + " " + RTrim$(CurTime)
36.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


37. X = SetWindowText(Form1.hWnd, NewCap)
38. End Sub

53: Adding Three-Dimensional


Effects to Visual Basic Controls
Created: April 10, 1995

Abstract
The Professional Edition of Visual Basic® provides controls to create three-dimensional
(3D) check boxes, command buttons, frame controls, group push buttons, option buttons,
and panels. This article explains how you can add this 3D-look to controls without using
the 3D controls provided in Visual Basic. The routine used in the example program will
work with both the Standard and Professional editions of Visual Basic.

Creating 3D Controls
You can change the appearance of a control such as a Text Box by giving it a three-
dimensional (3D) look. This effect can be achieved by using the Line method in Visual
Basic® to draw borders around the target control.

The example program below draws a raised border on two sides of the Text Box and
Label controls. This code can be easily modified to create more professional looking
Visual Basic applications.

Example Program
The program below shows how to add three-dimensional effects to a Text Box and Label
control. By modifying the BorderOffset, BorderWidth, and RaisedBorder variables, you
can change the appearance of the target control very quickly.
1. Create a new project in Visual Basic. Form1 is created by default. Set its
AutoRedraw property to True.

2. Add the following code to the Form_Load event for Form1:


3. Sub Form_Load()
4. Call Draw3DBorder(Form1, Text1, False, 3)
5. Call Draw3DBorder(Form1, Label1, False, 15)
6. End Sub
7. Add a Label control to Form1. Label1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Add a Text Box control to Form1. Text1 is created by default.

9. Create a new subroutine procedure called "Draw3DBorder." Add the following


code to this procedure (note that the first line should be typed as a single line of
code):
10. Sub Draw3DBorder(TargetForm As Form, TargetControl As Control, RaisedBorder
11. As Integer, BorderWidth As Integer)
12.
13. Dim BorderOffset As Integer
14. Dim X1 As Integer, X2 As Integer
15. Dim Y1 As Integer, Y2 As Integer
16. Dim OriginalForeColor As Long, OriginalDrawWidth As Long
17. Dim UpperColor As Long, LowerColor As Long
18.
19. 'Define how far the 3D lines are drawn from the outer edges of the
20. 'control. Modify to your taste.
21.
22. BorderOffset = 8
23.
24. 'Define the four corners of the 3D box to be drawn.
25. X1 = TargetControl.Left - BorderOffset
26. Y1 = TargetControl.Top - BorderOffset
27. X2 = X1 + TargetControl.Width + (BorderOffset * 2)
28. Y2 = Y1 + TargetControl.Height + (BorderOffset * 2)
'Change the form's ForeColor and DrawWidth properties,
'so we'll save them first and restore when done.

OriginalForeColor = TargetForm.ForeColor
OriginalDrawWidth = TargetForm.DrawWidth

'If RaisedBorder is True, the white lines are drawn on the


'top and left sides.

If RaisedBorder Then
UpperColor = QBColor(15)
LowerColor = QBColor(8)
Else
UpperColor = QBColor(8)
LowerColor = QBColor(15)
End If

'Draw line on left.


TargetForm.DrawWidth = BorderWidth
TargetForm.ForeColor = UpperColor
TargetForm.Line (X1, Y2)-(X1, Y1)

'Draw line on top.


TargetForm.Line -(X2, Y1)

'Draw line on right.


TargetForm.ForeColor = LowerColor
TargetForm.Line -(X2, Y2)

'Draw line on bottom.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


TargetForm.Line -(X1, Y2)

'Return the form's properties to their original state.


TargetForm.ForeColor = OriginalForeColor
TargetForm.DrawWidth = OriginalDrawWidth
End Sub

54: Preventing a Window Form


from Being Resized
Created: April 10, 1995

Abstract
In many Windows®-based applications, the user can resize a window by dragging one of
the window's borders to a new position. This article will tell you how to prevent a form
from being resized. You can also modify the example program so that it will allow the
user to resize the form only to a certain size.

Using Get/SetWindowPlacement to Size a Form


In a Visual Basic® application, you can use the Windows® application programming
interface (API) GetWindowPlacement and SetWindowPlacement functions to restrict a
form's size. Each time Visual Basic loads a form or the form is resized by the user, the
Resize event is triggered. By including code in the Resize event, you can prevent a form
from being resized smaller, larger, or at all.
The GetWindowPlacement function returns the specified window's current location,
visibility status, and its minimized and maximized positions. The SetWindowPlacement
function sets the specified window's current location, visibility status, and its minimized
and maximized positions. Both functions use the same type structures and arguments.

To declare these two functions within your program, include the following Declare
statements in the Global Module or General Declarations section of your application's
form (note that each Declare statement must be typed as one single line of text):
Declare Sub GetWindowPlacement Lib "User" (ByVal hWnd As Integer, lpWnd
As WINDOWPLACEMENT)

Declare Function SetWindowPlacement Lib "User" (ByVal hWnd As Integer, lpWndPl


As WINDOWPLACEMENT) As Integer
The GetWindowPlacement or SetWindowPlacement function must be called with two
arguments: The hWnd argument must contain the handle of the window that you want to
retrieve information for; the lpWndPl argument is a WINDOWPLACEMENT structure
that will hold the window's information.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The WINDOWPLACEMENT structure uses the RECT and POINTAPI structures as
well, and is defined as follows:
Type WINDOWPLACEMENT '22 bytes
Length As Integer
Flags As Integer
ShowCmd As Integer
PtMinPosition As POINTAPI
PtMaxPosition As POINTAPI
RcNormalPosition As RECT
End Type
The RECT structure, which describes the coordinates of a rectangle, is defined as
follows:
Type RECT '8 bytes
Left As Integer
Top As Integer
Right As Integer
Bottom As Integer
End Type
The values in the RECT structure represent the position of the window, in screen
coordinates.
And, finally, the POINTAPI structure is defined as follows:
Type POINTAPI '4 bytes
X As Integer
Y As Integer
End Type
The following table describes each field in the WINDOWPLACEMENT structure.
Length An integer value that must be set to 22, the length of this structure.
Flags An integer value containing either WPF_SETMINPOSITION (the
PtMinPosition specifies the X,Y location of the window when
minimized) or WPF_RESTORETOMAXIMIZED (the
SW_SHOWMINIMIZED constant must be specified in the
ShowCmd parameter; it indicates the window should be maximized
the next time it is restored).
ShowCmd An integer value that describes the visibility flags.
PtMinPosition A POINTAPI structure containing the X,Y location of the window
when it is minimized.
PtMaxPosition A POINTAPI structure containing the X,Y location of the window
when it is maximized.
RcNormalPosition A RECT structure containing the position of the window when it is
restored (set to its normal size).

After we call the GetWindowPlacement function, we can retrieve the window's current
size and save these values in variables for later use in our application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Because we want to prevent the user from resizing a form, we insert code in the form's
resize event. This code uses the GetWindowPlacement function to retrieve the form's
current size. If the window's size is not the same as it was when our program was first
executed, we call SetWindowPlacement, which sets the window's size back to its
original coordinates. This function has the same arguments as the
GetWindowPlacement function.

Example Program
The example program below uses the GetWindowPlacement and
SetWindowPlacement functions to prevent the form from being resized. You can
optionally add code to allow the window to be resized only to a specific size, if desired.
1. Start a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the general declarations section of Form1:


3. Dim lpRect As RECT
4. Dim lpWnd As WINDOWPLACEMENT
5. Dim nPoint As POINTAPI
6. Dim lpRect2 As RECT
7. Dim lpWnd2 As WINDOWPLACEMENT
8. Dim nPoint2 As POINTAPI
9. Dim lpWndPl As WINDOWPLACEMENT
10. Dim X As Integer
11. Dim FormWidth As Integer
12. Dim FormHeight As Integer
13. Dim FormWidth2 As Integer
14. Dim FormHeight2 As Integer
15. Add the following code to the Form_Load event for Form1:
16. Sub Form_Load()
17. 'Get the form's current size and position.
18. lpWnd.Length = 22 'set length of lpWnd first!
19.
20. Call GetWindowPlacement(Form1.hWnd, lpWnd)
21. FormWidth = lpWnd.RcNormalPosition.Right - lpWnd.RcNormalPosition.Left
22. FormHeight = lpWnd.RcNormalPosition.Bottom - lpWnd.RcNormalPosition.Top
23. End Sub
24. Add the following code to the Form_Resize event for Form1:
25. Sub Form_Resize()
26. lpWnd2.Length = 22 'set length of lpWnd2 first!
27. Call GetWindowPlacement(Form1.hWnd, lpWnd2)
28. FormWidth2 = lpWnd2.RcNormalPosition.Right - lpWnd2.RcNormalPosition.Left
29. FormHeight2 = lpWnd2.RcNormalPosition.Bottom - lpWnd2.RcNormalPosition.Top
30.
31. If FormWidth <> FormWidth2 Then
32. GoTo NoWidthChange
33. End If
34. If FormHeight <> FormHeight2 Then
35. GoTo NoHeightChange
36. End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


37.
38. Exit Sub
39. 'Do not allow form's width or height to be changed. Reset to original values.
40.
41. NoWidthChange:
42. X = SetWindowPlacement(Form1.hWnd, lpWnd)
43. Exit Sub
44. NoHeightChange:
45. X = SetWindowPlacement(Form1.hWnd, lpWnd)
46.
47. End Sub
48. Add a new BAS module to the project. Module1.Bas is created by default.

49. Add the following Declare statements and type structures to Module1.Bas (note
that each Declare statement should be typed as a single line of text):
50. Declare Sub GetWindowPlacement Lib "User" (ByVal hWnd As Integer, lpWnd
51. As WINDOWPLACEMENT)
52.
53. Declare Function SetWindowPlacement Lib "User" (ByVal hWnd As Integer, lpWndPl
54. As WINDOWPLACEMENT) As Integer
55.
56. Type RECT '8 bytes
57. Left As Integer
58. Top As Integer
59. Right As Integer
60. Bottom As Integer
61. End Type
62.
63. Type POINTAPI '4 bytes
64. X As Integer
65. Y As Integer
66. End Type
67.
68. Type WINDOWPLACEMENT '22 bytes
69. Length As Integer
70. Flags As Integer
71. ShowCmd As Integer
72. PtMinPosition As POINTAPI
73. PtMaxPosition As POINTAPI
74. RcNormalPosition As RECT
75. End Type
76. Run the program. Try to resize the form by clicking and dragging one of the
form's borders. The form will immediately reset to its previous size.

nts that were not marked read-only.


19 Attempt was

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


made to load a
compressed
executable
file. The file
must be
decompressed
before it can
be loaded.
20 Dynamic-link
library (.DLL)
file was
invalid. One
of the .DLLs
required to run
this
application
was corrupt.
21 Application
requires
Microsoft®
Windows 32-
bit extensions.

The Windows API FreeLibrary function unloads a previously loaded .DLL. The
FreeLibrary function should be called after you have tried to load a .DLL file with the
LoadLibrary function. This function's declaration statement is as follows:
Declare Sub FreeLibrary Lib "Kernel" (ByVal h As Integer)
To unload a .DLL from memory, you simply call the FreeLibrary function with the
module's instance handle.
If your Visual Basic application attempts to load a .DLL that does not exist, Windows
will respond with a critical-error-handler message box. You can use the Windows API
SetErrorMode function to tell Windows to handle the error or to tell Windows that your
program will process the error condition itself. The declaration statement for
SetErrorMode is as follows:
Declare Function SetErrorMode Lib "Kernel" (ByVal wMode As Integer) As Integer
This function requires only one argument: a constant value that tells Windows how to
handle Interrupt 24h errors. The value you pass to SetErrorMode may be a combination
of these values:
SEM_FAILCRITICALERRORSWindows does not display the critical-error-handler
message box and so returns the error to the calling
application.
SEM_NOGPFAULTERRORBOX Windows does not display the general-protection-

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


fault message box. This flag should be set only by
debugging applications that handle GP faults
themselves.
SEM_NOOPENFILERRORBOX Windows does not display a message box when it
fails to find a file.

After calling the SetErrorMode function, an integer value is returned. This value is the
previous state of the error-mode flag.
In a Visual Basic application, you can use the SetErrorMode function in conjunction
with the LoadLibrary and FreeLibrary functions to determine if a user's system has the
.DLL files your program needs.

Example Program
The following program shows how to trap Error Code 48, "Error in loading DLL", from
within your Visual Basic application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statements to the General Declarations section of


Form1 (note that each Declare statement must be typed as a single line of text):
3. Declare Function LoadLibrary Lib "Kernel" (ByVal f$) As Integer
4. Declare Sub FreeLibrary Lib "Kernel" (ByVal h As Integer)
5. Declare Function SetErrorMode Lib "Kernel" (ByVal wMode As Integer) As Integer
6. Add the following code to the Form_Load event for Form1:
7. Sub Form_Load()
8. Dim NameofDLL As String
9. Dim IsThere As Integer
10. Dim ErrNumber As Integer
11. Dim ErrText As String
12.
13. NameofDLL = "kernel.dll"
14. IsThere = DoesLibraryExist(NameofDLL, ErrNumber, ErrText)
15.
16. If IsThere = True Then
17. text1.Text = "DLL exists!"
18. Else
19. text1.Text = Str$(ErrNumber) + " " + ErrText
20. End If
21.
22. End Sub
23. Add a Text Box control to Form1. Text1 is created by default.

24. Create a new function called DoesLibraryExist. Add the following code to this
function (note that the first line, the OriginalErrorValue% lines, and all Explain$
lines must be typed as a single line of code):

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


25. Function DoesLibraryExist(DllName$, ErrorReturned%, ErrorExplanation$)
26. As Integer
27. Dim hInst As Integer
28. Dim ReturnValue As Integer
29. Dim Explain$
30. Dim OriginalErrorValue%
31. Const SEM_NOOPENFILEERRORBOX = &H8000
32. Const SEM_FAILCRITICALERRORS = &H1
33.
34. ReturnValue = True
35. OriginalErrorValue% = SetErrorMode(SEM_NOOPENFILEERRORBOX Or
36. SEM_FAILCRITICALERRORS)
37. hInst = LoadLibrary(DllName$)
38. OriginalErrorValue% = SetErrorMode(OriginalErrorValue)
39. If hInst > 32 Then
40. ReturnValue = True
41. FreeLibrary (hInst)
42. Else
43. ReturnValue = False
44. Select Case hInst
45. Case 0
46. Explain$ = "System is out of memory, executable file is corrupt, or
47. relocations are invalid."
48. Case 2
49. Explain$ = "File not found."
50. Case 3
51. Explain$ = "Path not found."
52. Case 5
53. Explain$ = "Sharing or network protection error."
54. Case 6
55. Explain$ = "Library required separate data segments for each task."
56. Case 8
57. Explain$ = "Insufficient memory."
58. Case 10
59. Explain$ = "Incorrect Windows version."
60. Case 11
61. Explain$ = "It was either not a Windows application or there was an
62. error in the file."
63. Case 12
64. Explain$ = "It was designed for a different operating system."
65. Case 13
66. Explain$ = "It was designed for MS-DOS 4.0."
67. Case 14
68. Explain$ = "File type unknown."
69. Case 15
70. Explain$ = "The file was designed for an earlier Version of Windows."
71. Case 16
72. Explain$ = "An attempt was made to load a second instance of an
73. executable file containing multiple data segments not marked
74. read-only."
75. Case 19
76. Explain$ = "Attempt was made to load a compressed file. It must be
77. decompressed before it can be loaded."
78. Case 20
79. Explain$ = "DLL file is invalid. This file or one called by it is
80. corrupt."

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


81. Case 21
82. Explain$ = "The file requires Microsoft Windows 32-bit extensions."
83. Case Else
84. Explain$ = "The reason it wouldn't load is unclear. Error code " &
85. Trim(Str(hInst)) & "."
86. End Select
87. ErrorReturned = Int(hInst)
88. ErrorExplanation$ = Explain$
89. End If
90. DoesLibraryExist = ReturnValue
91. End Function
When you run this program, it should display the message "DLL Exists" in the Text Box.
Change the NameofDLL string variable to "WIN.COM" and run the application a second
time. You should receive an error number and error message in the Text Box because
there is no .DLL file by the name of WIN.COM.

56: Sorting Integer Arrays


According to Index Positions
Created: April 17, 1995

Abstract
There are various methods of sorting data in a Visual Basic® application. This article
explains how to sort an integer array while preserving the array's original sort order.

Sorting Integer Arrays


When writing an application in Visual Basic®, you can store numeric values or strings in
a List Box control. The List Box's Sort property can be set to True to automatically sort
the entries as they are added to the control. However, if you need to sort, for example, an
integer array so that the position of each item in the array is preserved, you must write
your own sort procedure.
The following program shows how to sort an array of integer values according to the
integer's position in the array. In the example program (below), the integers are stored in
the array as follows:
Mat(1)=5
Mat(2)=7
Mat(3)=4
Mat(4)=6
Mat(5)=3
If we sorted these values in descending numerical order, the result would be:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Mat(1)=7
Mat(2)=6
Mat(3)=5
Mat(4)=4
Mat(5)=3
However, we want to be able to sort the array according to the integer's index value in the
array. Therefore, the example program sorts this array as:
Mat(1)=2
Mat(2)=4
Mat(3)=1
Mat(4)=3
Mat(5)=5

Example Program
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Sub Form_Load()
4. Dim X As Integer
5. Dim Mat(1 To 5) As Integer
6. Dim Temp As Integer
7. Dim Order As String
8. Dim Tempstr As String
9. Mat(1) = 5
10. Mat(2) = 7
11. Mat(3) = 4
12. Mat(4) = 6
13. Mat(5) = 3
14.
15. For X = 1 To 5
16. List1.AddItem "Number " + Str$(Mat(X))
17. Next X
18.
19. Order = 12345
20. X = 1
21. Y = 1
22. For X = 1 To 5
23. For Y = 1 To 5
24. If X = Y Or X < Y Then
25. GoTo NextOne
26. End If
27.
28. If Mat(Y) > Mat(X) Then
29. Temp = Mat(X)
30. Mat(X) = Mat(Y)
31. Mat(Y) = Temp
32. Tempstr = Mid$(Order, X, 1)
33. Mid$(Order, X, 1) = Mid$(Order, Y, 1)
34. Mid$(Order, Y, 1) = Tempstr
35. End If
36. NextOne:
37. Next Y

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


38.
39. Next X
40. 'Display results in List Box #2
41. For X = 5 To 1 Step -1
42. List2.AddItem "Array" + Str$(Mid$(Order, X, 1))
43. Next X
44. End Sub
45. Add a List Box control to Form1. List1 is created by default.

46. Add a second List Box control to Form1. List2 is created by default.

57: Creating a "Virus" to Prevent


Unauthorized Use of Your Computer
Created: April 17, 1995

Abstract
This article explains how you can design an application in Visual Basic® that acts like a
virus. You can use this program to prevent unauthorized users from using your system for
more than 10 minutes.

A 10-Minute Virus Stops Others from Using Your


Computer
If you are working in an office situation where other users have access to your computer
system, you may want to develop a Visual Basic® application that can prevent
unauthorized use of your computer.
The MSDNBUG.EXE virus program interrupts the currently running Windows®-based
application after the computer has been running for 10 minutes. The program
immediately displays a stay-on-top form that remains on the screen until the virus
program is terminated by holding down the ALT key and typing 169 on the numeric
keypad while the focus is on the Picture Box control. In addition, once the virus program
has been actuated, the Timer control is used to display a message about not removing the
diskette in drive B. This message box is displayed approximately once every second.
Again, the user cannot remove the diskette in drive B or return to the previously running
Windows-based application—the only alternative is to reboot the computer system, and
the user is warned not to do this to prevent further "damage" to the hard drive's contents.

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The program below is a sample virus application written in Visual Basic. Because the
program is added to the StartUp group on the Windows desktop, it will be executed each
time the computer is turned on. After the user has been working on the computer for 10
minutes, the virus will interrupt the currently running application and display its screen.
If the user attempts to remove a diskette from drive B, the program will also display a
message box telling the user not to remove the diskette because it has a hidden file that is
needed to restore the computer to its normal condition. The program can be terminated
only by holding down the ALT key and typing 169 on the numeric keypad.
1. Create a new project in Visual Basic. Form1 is created by default. Set the
following properties for Form1:

Caption: WARNING: FATAL VIRUS DETECTED


ClipControls: False
ControlBox: False
KeyPreview: True
MaxButton: False
MinButton: False
Visible: False
2. Add the following Dim, Constant, and Declare statements to the General
Declarations section of Form1 (note that the Declare statement must be typed as a
single line of code):
3. Declare Function SetWindowPos Lib "User" (ByVal hWnd As Integer, ByVal
4. hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal CX
5. As Integer, ByVal CY As Integer, ByVal wFlags As Integer) As Integer
6.
7. Const SWP_NOSIZE = &H1
8. Const SWP_NOMOVE = &H2
9. Const SWP_NOACTIVATE = &H10
10. Dim FunLoad As Integer
11. Dim Hwnd_Topmost As Integer
12. Dim Wp As Integer
13. Dim NumTimerEvents As Integer
14. Add the following code to the Form_Load event for Form1 (note that the Wp line
must be typed as a single line of code):
15. Sub Form_Load()
16. Hwnd_Topmost = -1
17. Wp = SetWindowPos(Form1.hWnd, Hwnd_Topmost, 0, 0, 0, 0, SWP_NOSIZE +
18. SWP_NOMOVE + SWP_NOACTIVATE)
19. Form1.Hide
20. Timer1.Interval = 65535
21. Timer1.Enabled = True
22. NumTimerEvents = 0
23. End Sub
24. Add the following code to the KeyPress event for Form1:
25. Sub Form_KeyPress(KeyAscii As Integer)
26. If KeyAscii = 3 Then FunLoad = True
27. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


28. Add the following code to the Unload event for Form1:
29. Sub Form_Unload(cancel As Integer)
30. If Not FunLoad Then cancel = True: End
31. End Sub
32. Add a Label control to Form1. Label1 is created by default. Set the following
properties for Label1:
AutoSize: False
BorderStyle: 0-None
Caption: WARNING: The diskette in drive B has the MSDN virus.
33. Add a second Label control to Form1. Label2 is created by default. Set the
following properties for Label2:
BorderStyle: 0-None
AutoSize: False
Caption: This virus has corrupted the directory of all files on the hard drive of
this computer. This virus copies the original information to a hidden file on the
diskette that is required for recovery of the hard drive. DO NOT TURN OFF THE
COMPUTER. Please contact Customer Support at (360) 297-4717 for instructions
(Monday to Friday, 8 a.m. to 4 p.m. Eastern time).
34. Add a Picture Box control to Form1. Picture1 is created by default. Set the
following properties for Picture1:

BorderStyle: 1-Fixed Single


Picture: C:\VB\METAFILE\BUSINESS\COMPUTERS.WMF
35. Add the following code to the KeyPress event for Picture1 (note that the Wp line
must be typed as a single line of code):
36. Sub Picture1_KeyPress(KeyAscii As Integer)
37. If KeyAscii <> 169 Then Exit Sub
38. Hwnd_Topmost = -2
39. Wp = SetWindowPos(Form1.hWnd, Hwnd_Topmost, 0, 0, 0, 0, SWP_NOSIZE +
40. SWP_NOMOVE + SWP_NOACTIVATE)
41. End
42. End Sub
43. Add a Timer control to Form1. Timer1 is created by default. Set the following
properties for Timer1:
Enabled: True
Interval: 1000

44. Add the following code to the Timer event for Timer1 (please note that the
MsgBox line must be typed as a single line of code):
45. Sub Timer1_Timer()
46. Const Max_Intervals = 5 '5 minutes
47. NumTimerEvents = NumTimerEvents + 1
48. If NumTimerEvents >= Max_Intervals Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


49.
50. On Error Resume Next
51. 'If the diskette has been removed, the timer is reset
52. 'to display the error message box once every second.
53. If Len(Dir("B:*.*")) = 0 Then
54. If Visible Then
55. Timer1.Interval = 1000
56. MsgBox "The virus-infected diskette has been removed from the PC --
57. recovery may not be possible.", 16, "MSDN Virus"
58. End If
59. Else
60. Visible = True
61. WindowState = 2
62.
63. End If
64. On Error GoTo 0
65. End If
66.
67. End Sub
68. Create an executable program file by selecting File/Make EXE File from the
Visual Basic menu. Save the program file as MSDNBUG.EXE.

69. Add the MSDNBUG.EXE program to the StartUp group on your Windows
desktop.

58: Separating Information in a


List Box with Tabs
Created: April 17, 1995

Abstract
The List Box control provided in Visual Basic® allows you to create a list of items,
optionally sorted alphabetically. Each item can contain any type of ASCII characters,
including control codes such as the tab character, and all items can be sorted if the Sort
property of the List Box is set to True. However, when trying to add items separated by
tabs, the output is not correctly formatted in two or more columns. This article will
explain how you can correctly format items in a List Box that contain embedded tab
(ASCII 9) characters.

Setting Tab Stops in List Boxes


If you've ever tried to align multiple columns of data in a List Box control, you'll know
how difficult this task can be. The data just doesn't line up properly. By using the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Windows® application programming interface (API) GetDC, ReleaseDC,
GetDeviceCaps, GetTextExtent, and SendMessage functions, you can add items to a
List Box in correctly positioned columns.
To display text in columns using a List Box control, you must first calculate the
horizontal resolution of the device context (in this case, the List Box), and then determine
the screen resolution ratio. Once you have this information, you can call the
GetTextExtent function. This function tells you the height and width of a text string. The
final step is to use SendMessage to actually set the tab stops in the List Box to the
desired positions. You can then add items with embedded tab characters to the List Box
control and the output will be formatted correctly.

Example Program
The program below shows how to add items to a List Box control. Each item consists of
three parts, separated by tab characters.
1. Create a new project in Visual Basic®. Form1 is created by default.

2. Add the following Declare statements to the General Declarations section of


Form1 (note that each Declare statement must be typed as a single line of code):
3. Declare Function GetTextExtent Lib "GDI" (ByVal hDC%, ByVal lpString As String,
4. ByVal nCount As Integer) As Long
5.
6. Declare Function GetDeviceCaps Lib "GDI" (ByVal hDC%, ByVal nIndex%) As Integer
7.
8. Declare Function GetDC Lib "USER" (ByVal hWnd As Integer) As Integer
9.
10. Declare Function ReleaseDC Lib "USER" (ByVal hWnd As Integer, ByVal hDC As
11. Integer) As Integer
12.
13. Declare Sub SendMessage Lib "USER" (ByVal hWnd As Integer, ByVal wMsg As
14. Integer, ByVal wParam As Integer, lParam As Any)
15. Add the following code to the Form_Load event for Form1 (note that the
List1.AddItem line must be typed as a single line of code):
16. Sub Form_Load()
17. Dim X As Integer
18. Dim ListHandle As Integer
19. Dim TabPos(2) As Integer
20.
21. ' Set tab stops at the following positions:
22. TabPos(0) = 5
23. TabPos(1) = 30
24. TabPos(2) = 60
25. ListHandle = List1.hWnd
26.
27. Call gSetListTabs(Form1, ListHandle, 3, TabPos())
28.
29. List1.AddItem "Microsoft Word" + Chr$(9) + "Version 6.0" + Chr$(9) + "10
30. files"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


31. List1.AddItem "Visual Basic" + Chr(9) + "Version 3.0" + Chr(9) + "89 files"
32.
33. End Sub
34. Add a List Box control to Form1. List1 is created by default.

35. Create a new Sub procedure called gSetListTabs. Add the following code to this
procedure (note that the Sub line must be typed as a single line of code):
36. Sub gSetListTabs(fForm As Form, iListHandle As Integer, iNumberOfColumns As
37. Integer, iListTabs() As Integer)
38.
39. '* 'fForm'-the form on which the listbox resides *
40. '* 'iListHandle'-listbox hWnd (I use GetFocus()) *
41. '* 'iNumberOfColumns'-# of tabs wanted in the listbox*
42. '* 'iListTabs()'-the array of tab positions you want *
43.
44. Dim iListDlgWidth As Integer, lTextWidth As Long
45.
46.
47. 'Get the Pixel width
48. Dim iPixelWidth As Integer
49. fForm.ScaleMode = PIXELS 'Set to Pixels(3)
50. iPixelWidth = fForm.ScaleWidth 'Get Scalewidth in pixels
51. fForm.ScaleMode = Twips 'Set back to Twips(1)
52.
53. 'Set the Twip to Pixel Ratio
54. Dim sinTwipPixelRatio As Single
55. sinTwipPixelRatio = 1 / (fForm.ScaleWidth / iPixelWidth)
56.
57. 'Get the screen device context
58. Dim iWindowContext As Integer
59. iWindowContext = GetDC(fForm.hWnd)
60.
61. 'Use the device context to get the horizontal resolution
62. Dim iHorzResPixels As Integer
63. iHorzResPixels = GetDeviceCaps(iWindowContext, HORZRES)
64.
65. 'Calculate the screen resolution ratio
66. Dim sinScreenRatio As Single
67. sinScreenRatio = (640 / iHorzResPixels)
68.
69. 'Set up and calc the 'textwidth' average
70. Dim sTemp As String, iTemp As Integer
71. sTemp = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
72. iTemp = 62
73.
74. 'GetTextExtent() returns a long, the lower 16 bits have
75. 'the font textwidth in twips
76. lTextWidth = GetTextExtent(iWindowContext, sTemp, iTemp)
77. iListDlgWidth = (lTextWidth Mod 65536) / 2 'bottom 16 in Twips
78. iListDlgWidth = iListDlgWidth * sinTwipPixelRatio 'apply ratio
79. iListDlgWidth = CInt(iListDlgWidth * sinScreenRatio) / 15 'Res 640 x480
80. iListDlgWidth = 15
81.
82. 'Apply factor to each tab position

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


83. Dim I As Integer
84. For I = 0 To iNumberOfColumns - 1
85. iListTabs(I) = iListTabs(I) * iListDlgWidth 'This is the magic Number
86. Next I
87.
88. Call SendMessage(iListHandle, LB_SETTABSTOPS, iNumberOfColumns, iListTabs(0))
89.
90. iWindowContext = ReleaseDC(fForm.hWnd, iWindowContext) 'Clean up resource
91.
92. End Sub
When you execute this program, Visual Basic displays the two items in the List Box.
Each item consists of three parts and each part is positioned at the user-defined tab stops
within the List Box.

59: Retrieving Text Under the Mouse


Pointer
Created: April 17, 1995

Abstract
When developing an application in Visual Basic®, you may need to determine what word
or phrase is under the mouse pointer. This article will demonstrate, through the use of an
example program, how to retrieve text from a control.

Retrieving Text from a Control


The Instr function in Visual Basic® is a powerful tool you can use to manipulate text
strings. The Instr function is used to determine the position of a target string within a
larger string. The target string can be defined as a whole word or phrase or simply a
character, such as a space character. You can use it in conjunction with the Mid$
function to easily remove parts of one string from another. The ParseText routine shown
in the example program below is based on using these two Visual Basic functions to
retrieve the text under the mouse pointer.

Example Program
The following program shows how to retrieve a word or phrase under the mouse pointer
(cursor).
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1 (note that each Words
line must be typed as a single line of code):
3. Sub Form_Load()
4. 'Make the picture box take up the form's entire client area.
5. Form1.Picture1.Top = 0

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


6. Form1.Picture1.Left = 0
7. Form1.Picture1.Height = Form1.ScaleHeight
8. Form1.Picture1.Width = Form1.ScaleWidth
9.
10. ' Create some sample text to work with. This could be
11. ' text loaded in from a file.
12. ' Note - The text MUST currently end with a space.
13. Text$ = "This is an example of how to determine which "
14. Text$ = Text$ + "word the cursor is over in a picture "
15. Text$ = Text$ + "box. This example is of course "
16. Text$ = Text$ + "released into public domain. "
17.
18. 'Parse the text into the Words array.
19. Call ParseText(Text$, Words())
20.
21. 'Create the prompts for the text.
22. Words(0).PromptText = "This program"
23. Words(3).PromptText = "A model"
24. Words(11).PromptText = "The mouse cursor"
25. Words(16).PromptText = "A control in VB that can display a Bitmap, MetaFile
26. or Icon"
27. Words(17).PromptText = "A control in VB that can display a Bitmap, MetaFile
28. or Icon"
29. Words(20).PromptText = "A model"
30. Words(26).PromptText = "This program can be copied, modified, and used
31. without violating a copyright."
32. Words(27).PromptText = "This program can be copied, modified, and used
33. without violating a copyright."
34. Form1.Show
35. 'Display the text on the Picture Box.
36. Call DisplayText(Words(), Form1.Picture1)
37. Form1.Picture1.AutoRedraw = True
38. End Sub
39. Add a Picture Box control to Form1. Picture1 is created by default.

40. Add the following code to the MouseMove event for Picture1 (note that the Sub
line must be typed as a single line of code):
41. Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single,
42. Y As Single)
43. Form1.Caption = ""
44. For I = 0 To UBound(Words)
45. If Y >= Words(I).Top And Y <= Words(I).Bottom Then
46. If X >= Words(I).Left And X <= Words(I).Right Then
47. Form1.Caption = Words(I).PromptText
48. End If
49. End If
50. Next
51. End Sub
52. Create a new Sub procedure called ParseText. Add the following code to this
procedure:
53. Sub ParseText(Text$, WordHolder() As WordType)
54. 'Find the number of spaces in the text, to determine
55. 'approx. how many words are in it.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


56. Start = 1 'Start at the beginning.
57.
58. Do
59. I = InStr(Start, Text$, Chr$(32)) 'Look for a space after
60. 'the one just found.
61.
62. 'If a space was found, add 1 to the counter and move
63. 'the start to its location.
64. If I > 0 Then
65. NumSpaces = NumSpaces + 1
66. Start = I + 1
67. End If
68. Loop Until I = 0
69. ReDim WordHolder(NumSpaces + 1) 'Redimension the array to
70. 'the # of words in the text.
71. Start = 1 'Reset the starting position.
72. WordNum = 0 'Create a counter for the
73. 'current location in the
74. 'array.
75. Do
76. I = InStr(Start, Text$, Chr$(32)) 'Look for a space after
77. 'the one just found.
78. 'If a space was found, make the next word equal to what
79. 'was in between it and the previous space.
80.
81. If I > 0 Then
82. WordHolder(WordNum).Word = Mid$(Text$, Start, (I - Start) + 1)
83. Start = I + 1
84. WordNum = WordNum + 1
85. End If
86. Loop Until I = 0
87. End Sub
After executing this program, Visual Basic enlarges the Picture Box to fit the entire client
area of Form1. Next, it displays the sentences in the Picture Box control. When you
move the mouse pointer over selected words in the Picture Box, the phrases associated
with the text pointed to by the mouse pointer are shown in the form's titlebar.

60: Executing Program Manager's


File/Run Command
Created: April 17, 1995

Abstract

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


From the Windows® Program Manager, you can select the File/Run menu command to
execute any Windows-based application. This article explains how you can use this same
File/Run command from within a Visual Basic® application.

The File/Run Command


To execute Program Manager's File/Run menu command, we must send a special
message to Program Manager. First we determine the handle to Program Manager, then
we use the SendMessage function to actually invoke the File/Run menu selection.

We can use the Windows® application programming interface (API) FindWindow


function to retrieve the handle for Program Manager. To declare the FindWindow
function within your program, include the following Declare statement in the Global
Module or General Declarations section of your form (note that this statement must be
typed as a single line of code):
Declare Function FindWindow Lib "User" (ByVal lpClassName As String, ByVal
lpWindowName As Long) As Integer
To call FindWindow, you must pass it the following two arguments:
lpClassName A string (or long pointer to a string) that contains the window's class
name. A value of zero is used to accept any class.
lpWindowName A string (or long pointer to a string) that contains the window's title bar
text. A value of zero is used to accept any window title.

Because we want to execute a command within Program Manager, we call the


FindWindow function with this statement:
RunFile = FindWindow("ProgMan", 0)
The window's handle will be returned in the RunFile variable. We can then call the
SendMessage function.
The SendMessage function can be used to send a message to another window. In our
case, we want to tell Program Manager to execute one of its menu selections, namely
File/Run. The Declare statement for SendMessage is as follows (note that it must be
typed as a single line of code):
Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg
As Integer, ByVal wParam As Integer, ByVal lParam As Long) As Long
As you can see, the SendMessage function requires four arguments. These arguments
are:
hWnd An integer value containing the window's handle. The message will be sent to
this window.
wMsg An integer value containing the message to send to the window.
wParam A 16-bit value containing additional message-dependent information, if
required.
lParam A 32-bit value containing additional message-dependent information, if

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


required.

The Windows SDK Help File contains a description of each windows message you can
use with the SendMessage function. For now, the message we want to send to Program
Manager is represented by the value 107, which initiates the File/Run menu selection in
Program Manager. In the example program below, the SendMessage function executes
the File/Run command, then the DoEvents statement waits until that command has been
processed, and then control returns to the program, with Form1 set to have the focus.

Example Program
The following program executes the File/Run command from Program Manager. When
you run this program, click on the command button. The File/Run dialog box will be
called up on your screen. Select any Windows-based application you wish to run. After
that application has terminated, control returns to this program.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Declare Function FindWindow Lib "User" (ByVal lpClassName As String, ByVal
4. lpWindowName As Long) As Integer
5.
6. Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg
7. As Integer, ByVal wParam As Integer, ByVal lParam As Long) As Long
8. Const WM_COMMAND = &H111
9. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Execute File/Run".

10. Add the following code to the Click event for Command1:
11. Sub Command1_Click()
12. Dim RunFile As Integer
13. Dim Result As Integer
14.
15. RunFile = FindWindow("ProgMan", 0)
16. If RunFile <> 0 Then
17. Result = SendMessage(RunFile, WM_COMMAND, 107, 0)
18. DoEvents
19. Form1.SetFocus
20. End If
21.
22. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


61: Creating Splitter Bars
Created: April 17, 1995

Abstract
When designing a Visual Basic® application, you may want to include code that allows
the user to resize, for example, two Text Boxes by dragging and dropping a splitter bar.
This article explains how to create a program that uses a splitter bar to enlarge or shrink
the two Text Boxes to different sizes.

Resizing Text Boxes with a Splitter Bar


You can add both visual appeal and ease of use to a Visual Basic® application by
including splitter bars. A splitter bar is a horizontal or vertical bar that the user clicks on
to automatically resize a control, such as a Text Box, on a form. By using splitter bars,
for instance, you can have two Text Boxes displayed on the application's window, one at
the top of the form and the other at the bottom of the form. The splitter bar is positioned
between the two text boxes. When the user drags the splitter bar (which is actually a
Picture Box control) towards the top of the window, the first Text Box is made smaller
and the second Text Box is made larger. Conversely, when the splitter bar is dragged
towards the bottom of the window, the first Text Box grows in size vertically while the
second Text Box shrinks in size vertically. This technique allows users to size the Text
Box controls according to their own preferences, which also allows them to see more or
less data in each individual control.
The key to creating a splitter bar in a Visual Basic application is the DragDrop event.
Almost every control supports the DragDrop event. Each time a control is dropped at a
new location, the DragDrop event is triggered. When the DragDrop event is triggered, the
target control that you want to manipulate must contain the code to do whatever it is you
want to do. In our example program below, we want to resize the text boxes, therefore we
set the DragDrop event to trigger the Resize event for each Text Box on the form.

Each Text Box control is responsible for calling the Resize event of the program's main
form, Form1. This Resize event causes Visual Basic to draw each Text Box control to its
new size at its new position. This same technique can be applied to almost any other form
or control except menus, timers, lines, and shapes.

Example Program
1. Create a new project in Visual Basic. Form1 is created by default. Set the
following properties for Form1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Height: 3735
Left: 1470
Top: 1320
Width: 6720
2. Add the following code to the Resize event for Form1:
3. Sub Form_Resize()
4. Picture1.Left = 0
5. 'Make sure the form is not minimized!
6. If Form1.ScaleHeight < Picture1.Height + 1 Then
7. Form1.Height = Form1.Height - Form1.ScaleHeight + Picture1.Height + 1
8. Else
9. If Form1.ScaleHeight < Picture1.Top + Picture1.Height Then
10. Picture1.Top = Form1.ScaleHeight - Picture1.Height - 1
11. End If
12.
13. 'Set Text1 to the Width and Height of form1
14. Text1.Width = Form1.ScaleWidth
15. Text1.Height = Form1.Picture1.Top
16.
17. Picture1.Width = Form1.ScaleWidth
18.
19. 'Set Text2 to the Width and Height of Form1
20. Text2.Top = Picture1.Top + Picture1.Height
21. Text2.Width = Form1.ScaleWidth
22. Text2.Height = Form1.ScaleHeight - Picture1.Top - Picture1.Height
23. End If
24. End Sub
25. Add a Text Box control to Form1. Text1 is created by default. Set the following
properties for Text1:
Height: 1815
Left: 0
Top: 0
Width: 6615
26. Add the following code to the DragDrop event for Text1:
27. Sub Text1_DragDrop(Source As Control, X As Single, Y As Single)
28. If Y > 0 Then
29. Picture1.Top = Y 'Move splitter bar.
30. Form_Resize
31. End If
32. End Sub
33. Add a second Text Box control to Form1. Text2 is created by default. Set the
following properties for Text2:
Height: 1455
Left: 0
Top: 1920
Width: 6615

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


34. Add the following code to the DragDrop event for Text2:
35. Sub Text2_DragDrop(Source As Control, X As Single, Y As Single)
36. If Y < Text2.Height Then
37. Picture1.Top = Y + Text2.Top - Picture1.Height
38. Form_Resize
39. End If
40. End Sub
41. Add a Picture Box control to Form1. Picture1 is created by default. Set the
following properties for Picture1:

BackColor: &H000000C0 (the color red)


DragMode: 1 (automatic)
DrawStyle: 0 (solid)
Height: 135
Left: 0
MousePointer: 7 (size n s)
Top: 1800
Width: 6495
Run the example program. A red bar (that is, the Picture Box control) separates the top
Text Box from the bottom Text Box. You can resize either Text Box by clicking on the
red bar and dragging the bar towards the top or bottom of Form1. When you release the
mouse button, both Text Boxes are resized accordingly.

62: Retrieving Filenames


Associated with an Application
Created: April 24, 1995

Abstract
This article explains how you can use the Windows® application programming interface
(API) FindExecutable function to determine which filename extension is associated with
a specific Windows-based application.

Using the FindExecutable Function


Using the Windows® File Manager, you can associate a filename extension with a
specific application. For instance, all filenames with the .TXT extension are associated
with the Notepad application. These associations are stored in the registration database
and in the WIN.INI initialization file. Using file associations allows you to double-click a

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


filename and have the associated Windows-based application automatically execute and
load that file.
In a Visual Basic® application, you can use the Windows application programming
interface (API) FindExecutable function to retrieve the name of the executable file that
is associated with a specific filename. The Declare statement for the FindExecutable
function is as follows (note that it must be typed as a single line of code):
Declare Function FindExecutable Lib "shell.dll" (ByVal lpszFile As String, ByVal
lpszDir As String, ByVal lpszResult As String) As Integer
The FindExecutable function takes three arguments, as follows:
lpszFile A string containing the filename that is associated with a specific application.
lpszDir A string containing the path of the default directory to use.
lpszResult A string buffer that will hold the application's name. This buffer should be
128 bytes long.

After executing the FindExecutable function, an integer value will be returned indicating
success or failure. If the value returned is greater than 32, the function was successful and
lpszResult will contain the full path and filename of the executable application associated
with the file. If the value returned is less than 32, the function was not successful.
FindExecutable can return the following error codes:
0 Insufficient system memory or corrupt program file
2 File not found
3 Path not found
5 Sharing or protection error
6 Separate data segments required for each task
8 Insufficient memory to run application
10 Windows version is incorrect
11 Program file is invalid
12 Program requires a different operating system
13 Program requires MSDOS 4.0
14 Program type is unknown
15 Protected memory mode is not supported
16 When loading a second instance of a program, invalid use of data segments
19 Cannot execute a compressed program file
20 Not a valid dynamic-link library (DLL)
21 Windows 32-bit extensions is required

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


When you run the program below, it will return the name of the Windows-based
application associated with the file called README.WRI. This, of course, is the
Windows Write applet.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default.

3. Add the following Declare statement to the General Declarations section of


Form1 (note that the Declare statement should be typed as one single line of
code):
4. Declare Function FindExecutable Lib "shell.dll" (ByVal lpszFile As String, ByVal
5. lpszDir As String, ByVal lpszResult As String) As Integer
6. Add the following code to the Form_Load event for Form1:
7. Sub Form_Load()
8. Text1.Text = ""
9. Dim X As Integer
10. Dim FileName As String
11. Dim DirName As String
12. Dim Result As String
13. FileName = "README.WRI"
14. DirName = "C:\VB\"
15. Result = Space$(128)
16. X = FindExecutable(FileName, DirName, Result)
17.
18. Text1.Text = Result
19.
20. End Sub

63: Preventing List Box from


Redrawing (Refreshing)
Created: April 24, 1995

Abstract
In a Visual Basic® application you can update the contents of a List Box control by
using the AddItem or RemoveItem methods. However, if you do not want the contents
of the modified List Box to be updated until all items have been added or deleted, you
can use the Windows® application programming interface (API) SendMessage function
to set a flag (WM_SETREDRAW) telling Windows not to update the control until you
specifically ask it to do so. This article explains how you can prevent a List Box control
from being updated immediately.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Using the WM_SETREDRAW Message
In a Visual Basic® application, you can use a List Box control to hold items such as
names of people. When a user clicks on an item in a List Box control, that item is
highlighted and is said to be selected. If a List Box's MultiSelect property is set to True,
multiple items can be selected at one time. New items can be added to a List Box control
by using the AddItem method, and items can be deleted using the RemoveItem method.
However, as soon as you use AddItem or RemoveItem, the List Box's Refresh event is
triggered, which in turn updates the contents of the control. This behavior may not be
appropriate in cases where you want to suppress the updating process while adding,
deleting, or changing a large number of entries.
You can force your Visual Basic application to update the List Box control at a specific
time by using the Windows® application programming interface (API) SendMessage
and SendMessageByString functions. You must send the WM_SETREDRAW message
to the control to prevent the List Box from being updated. In addition, you must also send
the actual data to the List Box by using the SendMessage function—you cannot use the
AddItem or RemoveItem methods, because these methods will override the
WM_SETREDRAW message.

Setting the redraw flag to TRUE turns the redraw function on, and setting the redraw flag
to FALSE turns the redraw function off. Therefore, in a Visual Basic application that is
updating a List Box, you must send the WM_SETREDRAW message before you actually
begin manipulating the contents of the control. After you have finished adding or
removing items from the List Box control, you can send another WM_SETREDRAW
message to turn the redraw function on again. This will cause Windows to display the
modified List Box control immediately.

Example Program
The program shown below displays two List Box controls on the form. You can select
items from the first List Box by clicking them. Each selected item will be added to the
second List Box control when you click the "Show Results" command button.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement should be typed as a single
line of code):
3. Const WM_SetRedraw = &HB
4. Const LB_ADDSTRING = &H401
5.
6. Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As
7. Integer, ByVal wParam As Integer, lParam As Any) As Integer
8.
9. Declare Function SendMessageByString Lib "User" Alias "PostMessage" (ByVal hWnd
10. As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As
11. String) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


12. Add the following code to the Form_Load event for Form1:
13. Sub Form_Load()
14. Dim X As Integer
15. Dim D As Integer
16. Dim S As String
17. 'Add some dummy data to List1
18. For X = 0 To 15
19. List1.AddItem "Item #" + Str$(X)
20. Next X
21. End Sub
22. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Show Results".

23. Add the following code to the Click event for Command1:
24. Sub Command1_Click()
25. 'Show updated list box now
26. X = SendMessage(List2.hWnd, WM_SetRedraw, 1, 0)
27. End Sub
28. Add a List Box control to Form1. List1 is created by default. Set its MultiSelect
property to True.

29. Add the following code to the DblClick event for List1:
30. Sub List1_DblClick()
31. 'Disable the Repaint event
32. X = SendMessage(List2.hWnd, WM_SetRedraw, 0, 0)
33.
34. S = List1.List(List1.ListIndex)
35. ' Must use SendMessageByString instead of
36. ' List2.AddItem S to prevent redrawing
37. D = SendMessageByString(List2.hWnd, LB_ADDSTRING, 0, S)
38. End Sub
39. Add a second List Box control to Form1. List2 is created by default.

64: Changing the Case of Text


Entered in a Text Box Control
Created: April 24, 1995

Abstract
This article explains how you can force a Visual Basic® Text Box control to convert all
typed text to either uppercase or lowercase characters.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Forcing Text to Be Uppercase or Lowercase
The Visual Basic® Text Box control allows your program to accept any ASCII character
typed on the keyboard by the user. You can force Windows® to convert the typed text to
either all uppercase or all lowercase characters.

The Windows application programming interface (API) GetWindowLong and


SetWindowLong functions return or set various types of information about the style
associated with the specified window. Every window in a Windows-based application has
certain attributes that determine how that window is used within an application. Some of
these style attributes can be changed at run time to modify the behavior of controls such
as the Text Box control.
To determine a window's current style settings, you can use the GetWindowLong
function. The Declare statement for GetWindowLong is as follows (note that it must be
typed as a single line of code):
Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal nIndex
As Integer) As Long
The GetWindowLong function requires two arguments, as follows:
hWnd An integer value containing the window's handle
nIndex An integer value containing the type of window information you want to
retrieve. This value may be one of the following constants:
GWL_EXSTYLE—Retrieves the extended window style.
GWL_STYLE—Retrieves the window style.
GWL_WINDPROC—Retrieves the window function's address.

After the GetWindowLong function is executed, a long value is returned. This value
depends on the specific nIndex argument used to call the function.
To change a window's style, you call the SetWindowLong function. Its Declare
statement is as follows (note that it must be typed as a single line of code):
Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal nIndex
As Integer, ByVal dwNewLong As Long) As Long
The SetWindowLong function requires one more argument than the GetWindowLong
function, namely, dwNewLong. This long value should contain the new style value you
want to apply to the specified window.
When you want to force a Text Box control to convert typed text to uppercase characters,
you can call SetWindowLong with dwNewLong set to the constant ES_UPPERCASE.
Conversely, to convert all typed text to lowercase characters, you call SetWindowLong
with dwNewLong set to the constant ES_LOWERCASE. In an actual Visual Basic
application, you would first preserve the control's original window style and then restore
the window's style when your special task has been completed.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
The program shown below displays two Command Buttons and a Text Box on a form.
Click the "Uppercase Only" Command Button to force all text typed in the Text Box to
uppercase characters. Conversely, click the "Lowercase Only" Command Button to force
typed text to be converted to lowercase characters.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statements to the General Declarations section of


Form1 (note that each Declare statement must be typed as a single line of text):
3. Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal nIndex
4. As Integer) As Long
5.
6. Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal nIndex
7. As Integer, ByVal dwNewLong As Long) As Long
8. Add a Text Box control to Form1. Text1 is created by default.

9. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Uppercase Only".

10. Add the following code to the Click event for Command1:
11. Sub Command1_Click()
12. Text1.Text = ""
13. X = ChangeCase(Text1, True)
14. Text1.SetFocus
15. End Sub
16. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Lowercase Only".

17. Add the following code to the Click event for Command2:
18. Sub Command2_Click()
19. Text1.Text = ""
20. X = ChangeCase(Text1, False)
21. Text1.SetFocus
22. End Sub
23. Create a new function called ChangeCase. Add the following code to this
function:
24. Function ChangeCase(TheControl As Control, UpLow As Integer) As Integer
25. Const GWL_STYLE = (-16)
26. Const ES_UPPERCASE = &H8&
27. Const ES_LOWERCASE = &H10&
28. Dim Rtn As Long
29. Dim EditStyle As Long
30. EditStyle = GetWindowLong(TheControl.hWnd, GWL_STYLE)
31. If UpLow = True Then
32. EditStyle = EditStyle Or ES_UPPERCASE

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


33. End If
34. If UpLow = False Then
35. EditStyle = EditStyle Or ES_LOWERCASE
36. End If
37. Rtn = SetWindowLong(TheControl.hWnd, GWL_STYLE, EditStyle)
38. End Function

65: Separating a Path into


Individual Fields
Created: April 24, 1995

Abstract
When developing an application in Visual Basic®, you may need to ask the user to enter
a fully qualified path, such as when saving a data file to disk. However, your program
may need to determine if the specified directory and filename are valid DOS names, or
you may need to use the individual elements of the path in some other way. This article
demonstrates how you can write a procedure to extract the individual path, filename, and
filename extension from a fully qualified path.

Parsing the Elements of a Path


The Visual Basic® InStr and Left$ functions provide the tools you need to parse, or
extract, certain text from a larger text string. The InStr function lets you search for a
specific character within a text string. If it finds the target character, InStr returns the
character's position in the text string. Once you know where the target text is, you can use
the Left$ function to retrieve only a specific portion of the original text string. In the
example program below, we want to retrieve the directory name from the path. Therefore,
we first call InStr to search the target string (Full) for the "\" backslash character. The
backslash character tells us that the name of a directory was specified in the path. If the
backslash character is found, we use the Left$ function to extract this directory name and
store it in the variable Pname. We know that the name of the directory starts at the
string's first position and ends at the position returned by InStr. This same technique is
used to extract the filename extension from the specified filename, only the InStr
function is told to search for the '.' (period character) and the Mid$ function is used to
extract the actual filename's extension.

Example Program
The following program shows how you can separate a fully qualified path into separate
directory, filename, and filename extension fields.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

3. Add the following code to the Form_Load event for Form1:


4. Sub Form_Load()
5. Dim FullName As String
6. Dim X As Integer
7. Dim PathName As String
8. Dim FileName As String
9. Dim ExtName As String
10.
11. FullName = "c:\winword\legal\filename.exe"
12. X = BreakDown(FullName, FileName, PathName, ExtName)
13.
14. Text1.Text = ""
15. Text1.Text = "Pathname > " & PathName & Chr(13) & Chr(10)
16. Text1.Text = Text1.Text & "FileName > " & FileName & Chr(13) & Chr(10)
17. Text1.Text = Text1.Text & "Extension > " & ExtName & Chr(13) & Chr(10)
18.
19. End Sub
20. Create a new function called BreakDown. Add the following code to this
function (note that the BreakDown line must be typed as a single line of code):
21. BreakDown(Full As String, FName As String, PName As String, Ext As String) As Integer
22. If Full = "" Then
23. BreakDown = False
24. Exit Function
25. End If
26.
27. If InStr(Full, "\") Then
28. FName = Full
29. PName = ""
30. Sloc% = InStr(FName, "\")
31. Do While Sloc% <> 0
32. PName = PName + Left$(FName, Sloc%)
33. FName = Mid$(FName, Sloc% + 1)
34. Sloc% = InStr(FName, "\")
35. Loop
36.
37. Else
38. PName = ""
39. FName = Full
40. End If
41.
42. Dot% = InStr(Full, ".")
43. If Dot% <> 0 Then
44. Ext = Mid$(Full, Dot%)
45. Else
46. Ext = ""
47. End If
48. BreakDown = True

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


49. End Function

66: Disabling Task-Switching with


the SetSysModalWindow Function
Created: April 24, 1995

Abstract
When developing an application in Visual Basic®, you may need to perform a task that
should not be interrupted. This article explains how to use the Windows® application
programming interface (API) SetSysModalWindow and LockInput functions to disable
task-switching.

Preventing a User from Switching to Another


Application
The Windows® application programming interface (API) SetSysModalWindow
function can be used to prevent a user from switching to a different Windows-based
application while your program is executing. The ALT+TAB, CTRL+ESC, ALT+F4, and
ALT+ESC keystroke combinations will not bring up the Task Manager or any other
application—these keystrokes will simply be ignored. For a discussion of the
SetSysModalWindow function, see "Additional References" below.
In addition, the LockInput function can be used to force all input to your Visual Basic®
application only. No other application will receive any mouse or keyboard data. The
Declare statement for the LockInput function is as follows (note that it must be typed as
a single line of code):
Declare Function LockInput Lib "User" (ByVal hReserved As Integer, ByVal
hwndInput As Integer, ByVal fLock As Integer) As Integer
The LockInput function requires three arguments:
hReserved An integer value that must be set to a value of zero.
hwndInput An integer value containing the window's handle. This is the window that
will receive all input.
fLock An integer value set to TRUE (nonzero) to lock input or FALSE (zero) to
unlock input.

When your program is terminated, you must use the LockInput function to restore input
to other Windows-based programs. In addition, you must destroy the system modal

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


window; otherwise the user will not be able to switch to any other Windows-based
applications and will have to reboot the computer system.

Example Program
The program below shows how you can prevent a user from switching to another
Windows-based application while your program is executing.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Set the following properties for Form1:


3. ClipControls = False
4. ControlBox = false
5. MaxButton = False
6. MinButton = False
7. Add the following Dim and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
8. Declare Function GetActiveWindow Lib "User" () As Integer
9. Declare Function SetFocusAPI Lib "User" Alias "SetFocus" (ByVal Hwnd As Integer)
10. As Integer
11. Declare Function SetSysModalWindow Lib "User" (ByVal Hwnd As Integer) As Integer
12. Declare Function LockInput Lib "User" (ByVal hReserved As Integer, ByVal
13. hwndInput As Integer, ByVal fLock As Integer) As Integer
14. Dim TopHwnd As Integer
15. Add the following code to the Form_Load event for Form1:
16. Sub Form_Load()
17.
18. Dim X As Integer
19.
20. Show
21. DoEvents
22. TopHwnd = GetActiveWindow()
23.
24. X = SetFocusAPI(TopHwnd)
25. X = SetSysModalWindow(TopHwnd)
26. X = LockInput(0, TopHwnd, 1)
27. End Sub
28. Add the following code to the Form_Unload event for Form1:
29. Sub Form_Unload(Cancel As Integer)
30. X = LockInput(0, TopHwnd, 0)
31. End Sub
32. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Quit".

33. Add the following code to the Click event for Command1:
34. Sub Command1_Click()
35. X = LockInput(0, TopHwnd, 0)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


36. End
37. End Sub

67: Returning Focus to a Specific Control


After Executing WinHelp
Created: April 24, 1995

Abstract
You can add online Help to your Visual Basic® application by using the Windows®
application programming interface (API) WinHelp function. However, you need to keep
the focus on the control that had the focus just before you executed the WinHelp
function. This article explains how a control can retain the focus after calling WinHelp.

Using Form-Level Variables to Retain Focus


Almost every application developed for Windows® includes a Help command. In a
Visual Basic® application, you can attach a Help command to a Command Button
control. When the user clicks on the Command Button, your program calls the Windows
application programming interface (API) WinHelp function to display the actual Help
file.

After the user exits the Help program, however, the Command Button control now has
the focus. It would be preferable to have the focus set to the control (such as a Text Box
control) that had the focus before WinHelp was executed.

You can force your application to automatically retain a control's focus by defining a
Form-level variable as a control. When the focus is moved to a different control, such as
a Text Box, the Form-level variable should be set to the control that is getting the focus.
Then, after displaying the Help file, the focus can be set back to the control by using the
Form-level variable.
In the example program below, the focus returns to the Text Box control each time the
Help Command Button is clicked. In other words, the Command Button never retains the
focus.

Example Program
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following statements to the General Declarations section of Form1 (note
that the Declare statement should be typed as a single line of code):
3. Const HELP_CONTENTS = &H3
4.
5. Declare Function WinHelp Lib "User" (ByVal hWnd As Integer, ByVal lpHelpFile As
6. String, ByVal wCommand As Integer, dwData As Any) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


7.
8. Dim ControlWithFocus As Control
9. Add a Text Box control to Form1. Text1 is created by default.

10. Add the following code to the GotFocus event for Text1:
11. Sub Text1_GotFocus()
12. Set ControlWithFocus = Text1
13. End Sub
14. Add a second Text Box control to Form1. Text2 is created by default.

15. Add the following code to the GotFocus event for Text2:
16. Sub Text2_GotFocus()
17. Set ControlWithFocus = Text2
18. End Sub
19. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Help".

20. Add the following code to the Click event for Command1:
21. Sub Command1_Click()
22. Dim RVal As Integer
23. RVal = WinHelp(Form1.hWnd, "c:\vb\vb.hlp", HELP_CONTENTS, 0)
24. ControlWithFocus.SetFocus
25. End Sub
Run the program. There are two Text Box controls and one Command Button control
shown on the form. Notice that Text1 currently has the focus. Click the Command Button
to display the Contents window in Help. Exit Help. The Text1 control should still have
the focus. Move the focus to the second Text Box control, Text2. Click the Help
command button a second time and exit Help. The second Text Box control should have
the focus. The Command Button control never retains the focus.

68: Removing Duplicate Items


from List Box Controls
May 1, 1995

Abstract
The Visual Basic® StrComp function can be used in conjunction with the RemoveItem
method to delete entries that are duplicated in List Box controls. This article explains

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


how you can compare the items in two separate List Box controls and delete the
duplicate entries from one of the controls.

Comparing the Contents of Two List Box Controls


The AddItem and RemoveItem methods allow you to add or delete items from a List
Box control in Visual Basic®. If the Sorted property of a List Box is set to True, the
items are automatically sorted in alphabetical order as each new item is added or an old
item is removed.

If you have two List Box controls in your Visual Basic application and you want to
remove the items from the second control that are already in the first List Box, you can
use the StrComp function, which allows you to compare two strings to see if they are
identical.
When using StrComp to determine if two strings are identical, you can tell the function
to ignore uppercase and lowercase differences. In other words, the function can be told to
treat the string "this is a test" to be the same as or different from the string "THIS IS A
TEST".
If you want StrComp to ignore the case of the strings you are comparing, use the 1
argument. To make StrComp include the case of the strings in the comparison, use the 0
argument (that is, X = StrComp(String1, String2, 0) or X=StrComp(String1, String2, 1).) In
addition, the Option Compare Text command, which you would place in the General
Declarations section of a form or module, tells StrComp (and other string functions) that
all string comparisons are to ignore the upper- and lowercase differences.
After you call the StrComp function, it returns the status of the string comparison.
StrComp returns one of the following four possible values.
-1 The first string is less than the second string.
0 The first string is identical to the second string.
1 The first string is greater than the second string.
NULL Either String1 or String2 is a NULL (empty) string.

Example Program
The program below shows how to remove duplicate items from List Box controls. When
the program is first executed, both List Box controls contain two entries that are
identical. Clicking the "Remove Duplicates" command button removes the identical
items from the second List Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Sub Form_Load()
4. List1.AddItem "test1"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


5. List1.AddItem "test2"
6. List1.AddItem "test3"
7. List1.AddItem "test4"
8.
9. List2.AddItem "test1"
10. List2.AddItem "test2"
11. List2.AddItem "test5"
12.
13. End Sub
14. Add a List Box control to Form1. List1 is created by default. Set its Sorted
property to True.

15. Add a second List Box control to Form1. List2 is created by default. Set its
Sorted property to True.

16. Add a Command Button control to Form1. Command1 is created by default.

17. Add the following code to the Click event for Command1:
18. Sub Command1_Click()
19. Call EliminateDupEntries(List1, List2)
20. End Sub
21. Create a new function called EliminateDupEntries. Add the following code to
this function:
22. Sub EliminateDupEntries(First As Control, Sec As Control)
23. Dim Findx As Integer
24. Dim Sindx As Integer
25. Dim Ret As Integer
26.
27. Findx = 0
28. For Sindx = 0 To Sec.ListCount - 1
29. For Findx = Sindx To First.ListCount - 1
30. Ret = StrComp(First.List(Findx), Sec.List(Sindx))
31. Select Case Ret
32. Case 0:
33. Sec.RemoveItem Sindx
34. Sindx = Sindx - 1
35. Exit For
36. Case 1:
37. Exit For
38. End Select
39. Next Findx
40. Next Sindx
41. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


69: Forcing a Picture Control to
Use Only a Specific Font
May 1, 1995

Abstract
When developing an application in Visual Basic® that may be run on many different
computer systems, you may want to ensure that a control such as a Picture Box is
formatted correctly with regard to the size and type of font used. This article explains
how you can use the Windows® application programming interface (API) SendMessage
and GetStockObject functions to force a control to use a specific font when displaying
text.

Using the GetStockObject Function to Select a Font


Many controls such as Picture Box and Text Box controls have a Font property. The
Font property is usually set by the programmer at design time to a specific font.
However, in some situations, you may want to force Windows® to use a different font at
a specific time in your program for that control. In such cases, you can use the Windows
application programming interface (API) SendMessage and GetStockObject functions
to tell Windows that a control's Font property is to be set to a different font.
To change a control's Font property to another font, you use the GetStockObject
function. The Declare statement for this function is as follows:
Declare Function GetStockObject Lib "GDI" (ByVal nIndex As Integer) As Integer
GetStockObject requires only one argument—an integer value containing the type of
stock object you want to use. In our case, we want to use the ANSI_FIXED_FONT stock
object, which has a value of 11. After you call the GetStockObject function, it returns an
integer value. This value is set to NULL if the function was not successful, or to a handle
that identifies the logical object itself.

In the example program below, we want to force the Picture Box control to use a fixed
font instead of Bookman Old Style, which is set to a point size of 24, at design time. To
set the font to a fixed font while the program is being executed, we issue the following
statement:
X = SendMessage(Picture1.hWnd, WM_SETFONT, GetStockObject(ANSI_FIXED_FONT), 1)
After issuing this command, anytime we use the Print method to print text on the Picture
Box control, the fixed font is used.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
The example program below displays a Picture Box control on Form1. The Print
method is used to display the text "This is a test" in the Picture Box control. Note that
the default Font property value is ignored and the ANSI fixed font is used when the text
is displayed in the control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement should be typed as a single
line of code):
3. Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As
4. Integer, ByVal wParam As Integer, lParam As Any) As Long
5. Declare Function GetStockObject Lib "GDI" (ByVal nIndex As Integer) As Integer
6. Const ANSI_FIXED_FONT = 11
7. Const WM_SETFONT = &H30
8. Add the following code to the Form_Load event for Form1:
9. Sub Form_Load()
10. Dim X As Long
11. X = SendMessage(Picture1.hWnd, WM_SETFONT, GetStockObject(ANSI_FIXED_FONT),
12. 1)
13. Picture1.Print "This is a test"
14.
15. End Sub
16. Add a Picture Box control to Form1. Set its Font property to Bookman Old
Style, point size: 24.

70: Creating Temporary Files


May 1, 1995

Abstract
When developing an application in Visual Basic®, you may need to create a temporary
file on disk. This article explains how to use the Windows® application programming
interface (API) GetTempFileName function to create temporary files.

Managing Temporary Files


The Windows® application programming interface (API) GetTempFileName function
can be used to create a temporary file on a floppy or hard disk. Files created by this
function are not automatically deleted when your Visual Basic® application terminates—
you must do this using Visual Basic's Kill statement.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To create a temporary file in Visual Basic, you use the GetTempFileName function. The
Declare statement for this function is as follows (note that it must be typed as a single
line of code):
Declare Function GetTempFileName Lib "Kernel" (ByVal cDriveLetter As Integer,
ByVal lpPrefixString As String, ByVal wUnique As Integer, ByVal
lpTempFileName As String) As Integer
GetTempFileName requires four arguments, as follows:
cDriveLetter An integer value containing the disk drive letter.
lpPrefixString A string containing the filename prefix. This is a standard DOS
filename, except that it should be less than eight characters long,
because it will be padded with the wUnique value when the file is
created.
wUnique An integer value containing the number to use to append to the eight-
character filename prefix. If a value of zero is specified, the function
generates its own random number from the system's current time
stamp.
lpTempFileName A string that will hold the name of the newly created temporary file.
This string should be initialized to a length of at least 144 characters.

The GetTempFileName function will create the temporary file on the first hard disk or
on the disk specified by the TEMP environment variable. You can set the
TF_FORCEDRIVE bit of the cDriveLetter argument to tell the GetTempFileName
function to create the file in the current directory of the specified disk. In all other cases,
the temporary file will be created on the disk specified in the cDriveLetter argument.
After you call the GetTempFileName function, the file will have been created on the
specified disk. The lpTempFileName buffer will contain the file's complete path,
terminated by the number specified by the wUnique argument.
Once you have successfully created the temporary file from within your application, you
can isolate the actual filename by issuing these two statements:
TempFileName = Left(TempFileName, InStr(TempFileName, Chr(0)) - 1)
TempFileName = Trim(Right(TempFileName, Len(TempFileName) - 3))
The first statement uses the InStr function to strip off the last character returned in the
buffer used to hold the filename. This byte is the value used in the wUnique argument.
The second statement removes the preceding "C:\" drive specifier characters from the
filename.
You should be aware that temporary files created by the GetTempFileName function
remain on the disk until you actually delete them.

Example Program
The following program shows how you can create temporary files from within your
Visual Basic application. Each time you execute this program, a new temporary file is

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


created. Be sure to delete these temporary files from your disk when finished with this
program.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Declare Function GetTempFileName Lib "Kernel" (ByVal cDriveLetter As Integer,
4. ByVal lpPrefixString As String, ByVal wUnique As Integer, ByVal
5. lpTempFileName As String) As Integer
6. Const TF_FORCEDRIVE = &H80
7. Add the following code to the Form_Load event for Form1:
8. Sub Form_Load()
9. Dim X As Integer
10. Dim Drive As Integer
11. Dim Prefix As String
12. Dim Unique As Integer
13. Dim TempFileName As String
14. Dim PathName As String
15.
16. TempFileName = Space$(144)
17. NewFileName = Space$(144)
18.
19. PathName = "C:\WINDOWS"
20. Drive = Asc(UCase(Left(PathName, 1))) + TF_FORCEDRIVE
21. Prefix = "DATA"
22. Unique = 0
23.
24. ChDir PathName
25. X = GetTempFileName(Drive, Prefix, Unique, TempFileName)
26. TempFileName = Left(TempFileName, InStr(TempFileName, Chr(0)) - 1)
27. TempFileName = Trim(Right(TempFileName, Len(TempFileName) - 3))
28. Text1.Text = TempFileName
29. End Sub
30. Add a Text Box control to Form1. Text1 is created by default.

71: Dragging Controls at Run


Time
May 5, 1995

Abstract

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Many Windows®-based applications allow you to move a control, such as a window, to a
new position on the screen. This is accomplished by clicking the left mouse button on the
control and, while holding the mouse button down, dragging the object to a new location
on the screen. When you release the mouse button, the object remains at the new screen
position. This article explains how you can add this functionality to your own Visual
Basic® applications.

Moving Forms and Other Controls


When you click the mouse button, Visual Basic® triggers its MouseDown or MouseUp
events. When you press the mouse button down, a MouseDown event is invoked;
similarly, when you release the mouse button, a MouseUp event is invoked. At run time,
you can allow a user to position controls at new locations on the screen by trapping the
MouseDown event for each control.
Each time a control receives the focus or detects mouse movement, Windows® calls the
SetCapture or ReleaseCapture function. The Windows application programming
interface (API) SetCapture and ReleaseCapture functions set or release the mouse
capture, which tells the system which object is currently being manipulated. These
functions can be used in conjunction with the SendMessage function to position a control
at a new location on the screen.

When an object, such as a form, is moved at run time, Windows generates a MOVE
message. By trapping the MouseDown event for a control, you can tell Visual Basic to
issue a move command to the operating system. This system command (MOVE) tells
Windows to move the window to the new position.
In the example program below, the user can move both the form and command button to
new locations. When the MouseDown event is triggered for the control, the
ReleaseCapture function is called. Next, the SendMessage function tells Windows to
actually execute the MOVE command. This anchors the object at its new position on the
screen.

Example Program
The example program below shows how to drag a control, such as a form or command
button, to a new position on the screen.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Const WM_SYSCOMMAND = &H112
4. Const SC_MOVE = &HF012
5. Declare Sub ReleaseCapture Lib "User" ()
6. Declare Sub SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg
7. As Integer, ByVal wParam As Integer, lParam As Long)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Add the following code to the MouseDown event for Form1:
9. Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single,
10. Y As Single)
11. ReleaseCapture
12. SendMessage Form1.hWnd, WM_SYSCOMMAND, SC_MOVE, 0
13. End Sub
14. Add a Command Button control to Form1. Command1 is created by default.

15. Add the following code to the MouseDown event for Command1:
16. Sub Command1_MouseDown(Button As Integer, Shift As Integer, X As Single,
17. Y As Single)
18. ReleaseCapture
19. SendMessage Command1.hWnd, WM_SYSCOMMAND, SC_MOVE, 0
20. End Sub

72: Positioning the Cursor over a Control


That Receives Focus
May 1, 1995

Abstract
The Default property of a Command Button can be used to place the focus on the
control at run time. If this property is set to True, the Command Button receives the
focus; if it is set to False, the Command Button does not have the focus. In addition, the
SetFocus method can be used to shift the focus to a specific control or form. However,
the mouse pointer's position is not changed. This article explains how you can position
the mouse pointer over the control that has just received the focus.

Using SetCursorPos to Change Cursor Position


Whenever you use the SetFocus method to move the focus to a different control or form,
the mouse pointer's position is not changed. In many situations, it would be nice if the
position of the mouse pointer could follow the control that has the focus. We can
implement this feature in a Visual Basic® application by using the SetCursorPos
function provided in the Windows® application programming interface (API). The
Declare statement for the SetCursorPos function is:
Declare Sub SetCursorPos Lib "User" (ByVal X As Integer, ByVal Y As Integer)
As you can see, this function takes two arguments. The X argument represents the
horizontal position of the cursor and the Y argument represents the vertical position of the
cursor. To successfully move the cursor using SetCursorPos, you must first determine
the correct coordinates to use with the function.

How do we actually determine the position of a control on the screen? First, we know
that the Command Button control has both Width and Height properties that tell us the
exact size of the control; ditto for the Form control. To calculate the approximate

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


coordinates of the center point of the Command Button, we can add the left position of
the Command Button to the Form's left position, and divide this value by the half the
width of the Command Button, which gives us the center position of the Command
Button.
However, we also need to adjust the values we calculate for the width of the form's
border and title bar. The final step is to divide this value we have just calculated by the
TwipsPerPixelX and TwipsPerPixelY values to obtain the control's true center position on
the screen. It is then a simple matter to call the SetCursorPos function to move the
mouse pointer to this new location.
Each time a control receives the focus, the GotFocus event for that control is triggered. In
addition, the focus can be shifted to a control by clicking that control. In this case, the
Click event is triggered. By including code that positions the mouse pointer at the center
of the control in these two events, you can successfully position the cursor over any
control as soon as it receives the focus.

Example Program
The program below shows how to move the mouse pointer to the control that has just
received the focus.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that this Declare statement should be typed as one single line of
code):
3. Declare Sub SetCursorPos Lib "User" (ByVal X As Integer, ByVal Y As Integer)
4. Add a Label control to Form1. Label1 is created by default. Set its Caption
property to "Save Changes?"

5. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Yes".

6. Add the following code to the Click event for Command1:


7. Sub Command1_Click()
8. Dim X As Integer, Y As Integer
9. X = (Form1.Left + Command2.Left + Command2.Width / 2 + 60) /
10. Screen.TwipsPerPixelX
11. Y = (Form1.Top + Command2.Top + Command2.Height / 2 + 360) /
12. Screen.TwipsPerPixelY
13. SetCursorPos X, Y
14. Command2.SetFocus
15. End Sub
16. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "No".

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


17. Add the following code to the GotFocus event for Command2:
18. Sub Command2_GotFocus()
19. Dim X As Integer, Y As Integer
20. X = (Form1.Left + Command2.Left + Command2.Width / 2 + 60) /
21. Screen.TwipsPerPixelX
22. Y = (Form1.Top + Command2.Top + Command2.Height / 2 + 360) /
23. Screen.TwipsPerPixelY
24. SetCursorPos X, Y
25. End Sub
Run the program by pressing the F5 function key. Notice that the Yes Command Button
has the focus. Press the TAB key once. The focus has now been placed on the No
Command Button. In addition, the mouse pointer is positioned over the second
Command Button.

Run the program a second time. Using the mouse, click the No Command Button. The
Command Button receives the focus and the mouse pointer is positioned over the
button. This demonstrates that no matter which one is used to move the focus—the TAB
key or the mouse—the mouse pointer can be programmed to follow control that has the
focus.

73: Creating Nested Directories


May 1, 1995

Abstract
You can use the Visual Basic® MkDir statement to create a new directory on any floppy
or hard disk. This article explains how you can use this statement to create nested
subdirectories, even if part of the path already exists.

The MkDir Statement


When developing an application in Visual Basic®, you may need to allow the user to
store a data file in a specific directory on disk. The directory can be created with Visual
Basic's MkDir statement. The syntax for this statement is:

MkDir "C:\<JUNK>"
where JUNK is the name of the directory you want to create and C:\ is the drive you want
to create it on. If you want to create a nested subdirectory, for example C:\JUNK\TEST,
you must first create the JUNK directory and then create the TEST directory. However, if

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


the JUNK directory already exists, the MkDir statement will interrupt your application
with a "Path/File Access Error" displayed in a message box.
To prevent this error message from appearing when your application is creating
directories, you can use the On Error and Resume Next statements. These two
statements allow you to trap this access error and force your Visual Basic program to
continue executing as if the error had not actually occurred.

The example program below creates a directory that is three levels deep, called
"\JUNK\TEST\TEST". The first time the MkDir statement is executed, it creates the
JUNK directory. The second and third times MkDir is executed, however, it produces
Error 76. The Resume Next statement tells our program to ignore this error each time it
is encountered until we have successfully created the entire directory structure.

Example Program
This program shows how to create nested directories even if one of the directories in the
path already exists.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Create Directory".

3. Add the following code to the Click event for Command1:


4. Sub Command1_Click()
5. CreateNewDirectory "C:\junk3\test\test"
6. MsgBox "Directory was created", 16, "Dir Demo"
7. End Sub
8. Create a new function called CreateNewDirectory. Add the following code to
this function:
9. Sub CreateNewDirectory(DirName As String)
10. Dim NewLen As Integer
11. Dim DirLen As Integer
12. Dim MaxLen As Integer
13.
14. NewLen = 4
15. MaxLen = Len(DirName)
16.
17. If Right$(DirName, 1) <> "\" Then
18. DirName = DirName + "\"
19. MaxLen = MaxLen + 1
20. End If
21. On Error GoTo DirError
22.
23. MakeNext:
24. DirLen = InStr(NewLen, DirName, "\")
25. MkDir Left$(DirName, DirLen - 1)
26. NewLen = DirLen + 1
27. If NewLen >= MaxLen Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


28. Exit Sub
29. End If
30. GoTo MakeNext
31. DirError:
32. Resume Next
33. End Sub
Run the program by pressing the F5 function key. When you click the "Create Directory"
command button, the program creates a directory called "JUNK\TEST\TEST" on drive
C. Change the path in the Click event for Command1 to "C:\JUNK\TEST\TEST2" and
run the program a second time. It will create a second subdirectory called TEST2 under
C:\JUNK\TEST. Notice that Error 76 is ignored by the program. This error is generated
by MkDir when it attempts to create a directory (in this case, C:\JUNK\TEST) that
already exists.

74: Scrolling Through Two List


Box Controls Simultaneously
May 8, 1995

Abstract
You can add code to your Visual Basic® application to allow a user to scroll through the
contents of two separate List Box controls at the same time. This article explains how
you can add this functionality to your program.

The TopIndex Property of List Box Controls


When using a List Box control, the user can click the mouse on the scroll bar to move up
or down the list of items. If the user clicks the mouse on an individual item, that item is
said to be selected. The ListIndex property is a unique value that represents the selected
item's position within the List Box.
You can also scroll through a List Box control by using the TopIndex property. This
property, however, can only be changed at run time, not during design time. The
TopIndex property moves you through the items in the List Box control. In other words,
it works just as if the user had used the scroll bar.

Let's assume that you have two List Box controls on a form in your Visual Basic®
application. As you scroll through the items in the first List Box control, you want to also
scroll through the same items in the second List Box control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


In an application, you can use the TopIndex property to move a specific item in the List
Box control so that that item appears at the top of the List Box. The following statement,
for example, moves the third item in the List Box to the top of the control:
List1.TopIndex = (2)
In the example program below, we want to scroll through both List Box controls at the
same time. To do this, we use a Timer control so that the second List Box control is
updated as soon as the item is selected in the first List Box control.

We first use a static variable—that is, a variable whose contents do not change when we
exit a procedure—to keep track of the currently selected item in the first List Box. Each
time a new item is selected in the List Box, this variable is set to that item's TopIndex
value.
Next, we set the ListIndex property of the second List Box control equal to that of the
first List Box control. This highlights the two items in each List Box that have the same
ListIndex value. It doesn't matter what the actual item is—the items are both selected
based on their position within the controls.
Each time you select an item in the first List Box, that same item is also selected in the
second List Box.

Example Program
This example program shows how to scroll through the contents of two List Box controls
simultaneously.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Option Explicit
4. DefInt A-Z
5. Add the following code to the Form_Load event for Form1:
6. Private Sub Form_Load()
7. Dim X As Integer
8. 'Initialize two list boxes with alphabet
9. For X = 1 To 26
10. List1.AddItem Chr$(X + 64)
11. Next X
12. For X = 1 To 26
13. List2.AddItem Chr$(X + 64)
14. Next X
15. timer1.Interval = 1
16. timer1.Enabled = True
17.
18. End Sub
19. Add a List Box control to Form1. List1 is created by default.

20. Add a second List Box control to Form1. List2 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


21. Add a Command Button control to Form1. Command1 is created by default.

22. Add the following code to the Click event for Command1:
23. Private Sub Command1_Click()
24. End
25. End Sub
26. Add a Timer control to Form1. Timer1 is created by default.

27. Add the following code to the Timer1 event for Timer1:
28. Private Sub timer1_Timer()
29. Static PrevList1
30. Dim TopIndex_List1 As Integer
31.
32. 'Get the index for the first item in the visible list.
33. TopIndex_List1 = List1.TopIndex
34.
35. 'See if top index has changed.
36. If TopIndex_List1 <> PrevList1 Then
37. 'Set the top index of List2 equal to List1,
38. 'so that the list boxes scroll together.
39. List2.TopIndex = TopIndex_List1
40. PrevList1 = TopIndex_List1
41. End If
42. 'Select the item in the same position in both list boxes.
43. If List1.ListIndex <> List2.ListIndex Then
44. List2.ListIndex = List1.ListIndex
45. End If
46.
47. End Sub

75: Invoking Menu Items in Other


Applications with SendMessage
May 8, 1995

Abstract
Within a Visual Basic® application, you can execute a menu item in another Windows®-
based program. This article explains how to use several Windows application
programming interface (API) functions to execute menu commands.

Executing Menu Commands


In some situations, you may need to actuate an application, such as Notepad, and execute
one or more of that application's menu commands. The Windows® application
programming interface (API) provides several functions that enable you to perform this
type of operation in Visual Basic®.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


• The Windows API FindWindow function can be used to determine the handle of
the application that contains the menu item you want to execute. FindWindow
returns an integer value containing the application's handle.

• You also need to retrieve the handle associated with the target window's menu.
The GetMenu function will return the handle as an integer value.

• Once you have the target window's menu handle, you need to determine the
entry's position in the menu and retrieve the handle of the pop-up menu. In the
example program below, we want to retrieve the handle of the File menu
selection. Therefore, we would call the GetSubMenu function with zero as the
entry's position. The first entry in every pop-up menu always begins with entry
number zero.

• Next, we want to retrieve the ID number of the specific menu entry we want to
execute. We retrieve this ID number by calling the GetMenuItemID function
with the entry's position specified as one (the position of the Open menu selection
in the pop-up menu).

• The final step is to make the target application the active application and to issue
the SendMessage function, which in turn sends a WM_COMMAND message to
the target window. The WM_COMMAND message is set to the target
application's window to execute the File/Open command.

Example Program
The following example program executes another application's menu commands. This
program assumes that the Windows Notepad application program is already running in
memory. This program uses SendMessage to execute the File/Open menu selection in
Notepad.
When you execute the program, click the Command Button. After a second or two, you
will see that Notepad has been activated and that its Open File dialog box is displayed on
the screen.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement should be typed as a single
line of code):
3. Private Declare Function FindWindow Lib "User" (ByVal lpClassName As Any, ByVal
4. lpWindowName As Any) As Integer
5. Private Declare Function GetMenu Lib "User" (ByVal hWnd As Integer) As Integer
6. Private Declare Function GetMenuItemID Lib "User" (ByVal hMenu As Integer, ByVal
7. nPos As Integer) As Integer
8. Private Declare Function GetSubMenu Lib "User" (ByVal hMenu As Integer, ByVal
9. nPos As Integer) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


10. Private Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal
11. wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
12. Const WM_COMMAND = &H111
13. Add a Command Button control to Form1. Command1 is created by default.

14. Add the following code to the Click event for Command1:
15. Private Sub Command1_Click()
16. Dim hWnd As Integer
17. Dim hMainMenu As Integer
18. Dim hMenu As Integer
19. Dim MenuID As Integer
20.
21. hWnd = FindWindow("NotePad", "Untitled - NotePad")
22. If hWnd = 0 Then Exit Sub
23.
24. hMainMenu = GetMenu(hWnd)
25. hMenu = GetSubMenu(hMainMenu, 0)
26. MenuID = GetMenuItemID(hMenu, 1)
27. AppActivate "Untitled - NotePad"
28. X& = SendMessage(hWnd, WM_COMMAND, MenuID, 0&)
29.
30. End Sub

76: Detecting Right Mouse Button Clicks


on List Box Controls
May 8, 1995

Abstract
When using a List Box control in a Visual Basic® application, the user can click on an
item with the left mouse button. That item then becomes selected. This article explains
how you can select items with the right mouse button instead of the left mouse button.

Intercepting Right Mouse Button Click Events


The LB_GETITEMRECT message can be used to determine which item in a List Box
was selected. This message retrieves the coordinates of a bounding rectangle for the
selected item in the List Box control. To invoke this message, you must tell it the entry
number, starting at zero, whose dimensions you want to retrieve, as well as a RECT
structure that will hold the coordinate information.
To determine which item a user clicked on with the right mouse button, you trap the
MouseUp event. The MouseUp event can be used to determine which mouse button was
pressed and the mouse's current X and Y coordinates on the form or control.
Once we have determined the mouse's position over the List Box control, we can use the
Windows® application programming interface (API) SendMessage function to return the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


index number of the item the mouse was positioned over when the MouseUp event was
triggered.

Example Program
The example program below displays a List Box control on a form. Whenever you click
the right mouse button on an item in the List Box, the message "Right Click on" is
displayed in the Text Box along with the index number corresponding to the selected
item.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Private Sub Form_Load()
4. List1.AddItem "Item #1"
5. List1.AddItem "Item #2"
6. List1.AddItem "Item #3"
7. End Sub
8. Add a List Box control to Form1. List1 is created by default.

9. Add the following code to the MouseUp event for List1 (note that the Private
lines must be typed as a single line of code):
10. Private Sub List1_MouseUp(Button As Integer, Shift As Integer, X As Single,
11. Y As Single)
12. Dim Item%
13. If (Button = 2) Then
14. Item% = GetRClickedItem(List1, X, Y)
15. If (Item% = LB_ERR) Then
16. Text1.Text = "ERROR"
17. Else
18. Text1.Text = "Right Click on " + Str(Item%)
19. End If
20. End If
21. End Sub
22. Add a Text Box control to Form1. Text1 is created by default.

23. Add a new module to the project. Module.Bas is created by default.

24. Add the following code to the Module.Bas file (note that the Private and If lines
must be typed as a single line of code):
25. Type RECT
26. Left As Integer
27. Top As Integer
28. Right As Integer
29. Bottom As Integer
30. End Type
31. Global Const WM_USER = &H400
32. Global Const LB_GETITEMRECT = (WM_USER + 25)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


33. Global Const LB_ERR = (-1)
34. Private Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal
35. wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
36. Function GetRClickedItem%(MyList As Control, X As Single, Y As Single)
37. Dim ClickX%, ClickY%, Ret&, CurRect As RECT
38. ClickX% = X \ Screen.TwipsPerPixelX
39. ClickY% = Y \ Screen.TwipsPerPixelY
40. i% = 0
41. Do While True
42. Ret& = SendMessage(MyList.hWnd, LB_GETITEMRECT, i%, CurRect)
43. If (Ret& = LB_ERR) Then
44. GetRClickedItem% = LB_ERR: Exit Function
45. End If
46. If (ClickX% >= CurRect.Left) And (ClickX% <= CurRect.Right) And
47. (ClickY% >= CurRect.Top) And (ClickY% <= CurRect.Bottom) Then
48. GetRClickedItem% = i%: Exit Function
49. End If
50. i% = i% + 1
51. Loop
52.
53. End Function

77: Determining the Amount of RAM


Installed in a Computer
May 8, 1995

Abstract
You can use the Windows® application programming interface (API) MemManInfo
function to determine how much random access memory (RAM) is installed in the
computer system. This article explains how to retrieve the amount of RAM.

How Much Memory Do You Have?


The Windows® application programming interface (API) MemManInfo function can be
called to determine how much random access memory (RAM) is installed in your
computer. This function is included in the TOOLHELP.DLL file.
To use the MemManInfo function in a Visual Basic® application, you must declare the
function as follows:
Private Declare Function MemManInfo% Lib "Toolhelp.dll" (lpmmi As TagMemManInfo)
The MemManInfo function takes only one argument: a structure that will hold
information about the memory manager. The number of pages of memory is stored in the
wPageSize field of this structure. We need only multiply the number of pages found by a
value of 4 to calculate how much RAM is installed.

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The example program below shows how to retrieve the amount of RAM installed in the
computer system.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Private Sub Form_Load()
4. Dim R As Long
5. Text1.Text = "Total RAM installed: "
6. R = GetRAMSize
7. Text1.Text = Text1.Text + Str(R)
8.
9. End Sub
10. Add a new module to the project. Module.Bas is created by default.

11. Add the following code to the Module.Bas file:


12. Type TagMemManInfo
13. dwSize As Long
14. dwLargestFreeBlock As Long
15. dwMaxPagesAvailable As Long
16. dwMaxPagesLockable As Long
17. dwTotalLinearSpace As Long
18. dwTotalUnlockedPages As Long
19. dwFreePages As Long
20. dwTotalPages As Long
21. dwFreeLinearSpace As Long
22. wPageSize As Integer
23. End Type
24. Private Declare Function MemManInfo% Lib "Toolhelp.dll" (lpmmi As TagMemManInfo)
25. Function GetRAMSize() As Long
26. Dim mmi As TagMemManInfo
27. mmi.dwSize = Len(mmi)
28. x% = MemManInfo(mmi)
29. If x% <> 0 Then
30. GetRAMSize = mmi.dwTotalPages * 4
31. Else
32. GetRAMSize = 0
33. End If
34. End Function
35. Add a Text Box control to Form1. Text1 is created by default.

78: Retrieving the Names of All Printers


May 8, 1995

Abstract

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


When Windows® is executed, it uses the WIN.INI initialization file to determine what
printer is attached to the computer system. This article explains how to determine the
names of all printers as stored in the Devices section of the WIN.INI file.

Populating a List Box Control with Printer Names


You can retrieve the names of all printers attached to the computer system by using the
Windows® application programming interface (API) GetProfileString function. To
declare this function in your Visual Basic® application, include the following Declare
statement in the Global Module or General Declarations section of your program:
Private Declare Function GetProfileString Lib "Kernel" (ByVal lpAppName
As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal
lpReturnedString As String, ByVal nSize As Integer) As Integer
(Note that this Declare statement must be typed as a single line of code.)
The GetProfileString function requires five arguments, as follows:
lpAppName A string containing the section name. Not case-sensitive.
lpKeyName A string containing the entry name to retrieve. Not case-sensitive. If
this is a long value set to zero, a list of all entries found in the
specified section will be returned in lpReturnedString.
lpDefault A string containing the default value to return if no entry is found.
lpReturnedString A string buffer that will hold the information the function retrieves.
nSize An integer value set to the maximum number of characters to be
stored in lpReturnedString.

Calling the GetProfileString function returns an integer value. This value is a count of
the number of characters that were stored in the lpReturnedString buffer, but does not
include the terminating NULL byte. (Each entry returned in lpReturnedString is
terminated by a NULL character.) When your program is retrieving more than a single
entry, the last entry in the buffer is marked with two consecutive NULL bytes to signal
the end of the list.
Because we want to retrieve the names of all printers stored in the devices section of the
WIN.INI file, we would execute the following statement:
RetVal = GetProfileString(Section, 0&, "", Buffer, Len(Buffer))
This tells the GetProfileString function to retrieve all entries stored in the "devices"
section of WIN.INI (notice the 0& argument to tell the function to provide us with a list
of the entries).

Example Program
The example program below populates a List Box control with the names of all printers
attached to the computer system.

1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Declare statement to the General Declarations section of
Form1 (note that this Declare statement must be typed as a single line of code):
3. Private Declare Function GetProfileString Lib "Kernel" (ByVal lpAppName As
4. String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal
5. lpReturnedString As String, ByVal nSize As Integer) As Integer
6. Add the following code to the Form_Load event for Form1:
7. Private Sub Form_Load()
8. Dim RetVal As Integer
9. Dim Buffer As String
10. Dim Section As String
11. Dim Start As Integer
12.
13. Buffer = Space$(1024)
14. Section = "devices"
15. RetVal = GetProfileString(Section, 0&, "", Buffer, Len(Buffer))
16.
17. Do Until Left$(Buffer, 1) = Chr$(0)
18. Start = InStr(Buffer, Chr$(0))
19. List1.AddItem Left$(Buffer, Start - 1)
20. Buffer = Right$(Buffer, Len(Buffer) - Start)
21. Loop
22. End Sub
23. Add a List Box control to Form1. List1 is created by default.

79: Closing All MDI Child Windows at


One Time
May 8, 1995

Abstract
This article explains how you can simultaneously close all child windows of a running
Visual Basic® application.

Using the Count Property of MDI Forms


The multiple document interface (MDI) feature of Visual Basic® allows you to create
applications that have multiple forms within a single parent form. This allows you to use
the multitasking functions of the Windows® operating system in your programs.
The Windows Notepad is an example of an MDI application. You can open several text
files at one time and move between each document with a click of the mouse.
When you create a child form while your program is executing, you must also remember
to close all the open child windows before your application terminates. Otherwise, you
could cause some unforeseen problems with other applications.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The count property of a control, such as a form, can be used to determine how many
members of that particular collection exist. In this case, the collection refers to the child
forms of the parent form. We can, therefore, determine how many child forms exist in our
application program by executing a statement such as:
X = Forms.Count
After this statement executes, the variable X will contain the number of child forms that
we have created. It is important to decrement this value by one because the count starts
with the value of one, not zero. Once we know how many child forms we have created
within our application program, we can use the TypeOf statement in a loop to close each
child form that exists. The TypeOf statement is used to determine the type of object you
are dealing with. In this case, we want to find out if the object is a form (Form1, the name
of the child form).
The final step to removing the child forms from the parent form is to use the Unload
statement. Therefore, to remove all child forms from our program while it is running, we
simply check each object in the form, making sure that it is indeed a child form of the
MDI form, and execute an Unload statement to close the form.

Example Program
The following program shows how to close all child forms at one time. Run the program
by pressing the F5 function key. The MDIForm1 form is displayed. Double-click the
client area of MDIForm1 to create a child form (Form1). Do this until you have several
child forms visible on the screen. Click the "Close Children" menu option to close all
child windows.
1. Create a new project in Visual Basic. Form1 is created by default. Set the
MDIChild property to True.

2. From Visual Basic's Insert menu, click "MDI Form" to create a Multiple
Document Interface form. MDIForm1 is created by default.

3. Add the following code to the DblClick event for MDIForm1:


4. Private Sub MDIForm_DblClick()
5. Dim X As New Form1
6. X.Show
7. End Sub
8. From Visual Basic's Tools menu, click Menu Editor. Set the Caption field to
"&Close Children" and the Name field to "mnuClose".

9. Add the following code to the mnuClose_Click event:


10. Private Sub mnuClose_Click()
11. Dim X As Integer
12. For X = (Forms.Count - 1) To 0 Step -1
13. If TypeOf Forms(X) Is Form1 Then
14. Unload Forms(X)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


15. End If
16. Next X
17. End Sub
18. From Visual Basic's Tools menu, select Project Options. Set the StartUp Form to
MDIForm1.

80: Drawing Borders Around Controls


May 8, 1995

Abstract
You can draw borders of any width around controls such as Text Boxes to give the
control a three-dimensional look. This article explains how to add a border to a control.

Using a Pen and Brush to Draw Borders


Through functions included in the Windows® application programming interface (API),
you can draw borders around controls in your Visual Basic® application. The CreatePen
function can be used to draw lines (solid, invisible, dotted) and the CreateSolidBrush
function can be used to fill areas of an object.

After you have created a pen and brush to use with the specific object (such as a Text
Box control) that you want to draw filled lines around, you need to determine the
coordinates of the bounding rectangle around the target object. Next, you must intercept
the Windows WM_PAINT message. The WM_PAINT message triggers Visual Basic's
Paint event. The message is sent to a window when the window needs to have its client
area redrawn. The Message Blaster custom control can be used to process the
WM_PAINT message. For information on the Message Blaster custom control, see the
reference materials listed at the end of this article. Once the Paint event has been
triggered, the control is redrawn with the desired borders around its perimeter.

Example Program
The program below shows how to add a three-dimensional look to a Text Box control.
This program draws a filled line across the top and down the right-hand border of the
Text Box.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default.

3. From Visual Basic's Tools menu, select Custom Controls and add the
MSGBLAST.VBX to your Toolbox. Add a Message Blaster control to Form1.
MsgBlaster1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


4. Add the following Dim, Constant, and Declare statements to the General
Declarations section of Form1 (note that each Private Declare statement must be
typed as a single line of code):
5. Const PS_SOLID = &H0
6. Const WM_PAINT = &HF
7. Private Declare Function DeleteObject Lib "GDI" (ByVal hObject As Integer)
8. As Integer
9. Private Declare Function SelectObject Lib "GDI" (ByVal hDC As Integer, ByVal
10. hObject As Integer) As Integer
11. Private Declare Function Polygon Lib "GDI" (ByVal hDC As Integer, lpPoints As
12. POINTAPI, ByVal nCount As Integer) As Integer
13. Private Declare Function CreateSolidBrush Lib "GDI" (ByVal crColor As Long)
14. As Integer
15. Private Declare Function GetDC Lib "User" (ByVal hWnd As Integer) As Integer
16. Private Declare Function CreatePen Lib "GDI" (ByVal nPenStyle As Integer, ByVal
17. nWidth As Integer, ByVal crColor As Long) As Integer
18. Dim TX As Integer
19. Dim TY As Integer
20. Dim DC_FRM As Integer
21. Dim PT1() As POINTAPI
22. Dim PT2() As POINTAPI
23. Add the following code to the Form_Load event for Form1:
24. Private Sub Form_Load()
25. MsgBlaster1.hWndTarget = Form1.hWnd
26. MsgBlaster1.MsgList(0) = WM_PAINT
27. MsgBlaster1.MsgPassage(0) = -1
28. End Sub
29. Add the following code to the Form_Activate event for Form1:
30. Private Sub Form_Activate()
31. TX = Screen.TwipsPerPixelX
32. TY = Screen.TwipsPerPixelY
33. DC_FRM = GetDC(Form1.hWnd)
34. Get_Rect
35. End Sub
36. Add the following code to the MsgBlaster1_Message event (note that the Private
statement must be typed as a single line of code):
37. Private Sub MsgBlaster1_Message(MsgVal As Integer, wParam As Integer, lParam
38. As Long, ReturnVal As Long)
39. Shadow
40. End Sub
41. Create a new procedure called Get_Rect. Add the following code to this
procedure:
42. Sub Get_Rect()
43. ReDim PT1(6) As POINTAPI
44. ReDim PT2(6) As POINTAPI
45.
46. PT1(0).X = Text1.Left / TX
47. PT1(0).Y = Text1.Top / TY
48.
49. PT1(1).X = (Text1.Left) / TX + 2

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


50. PT1(1).Y = (Text1.Top) / TY - 2
51.
52. PT1(2).X = (Text1.Left + Text1.Width) / TX + 2
53. PT1(2).Y = (Text1.Top) / TY - 2
54.
55. PT1(3).X = (Text1.Left + Text1.Width) / TX + 2
56. PT1(3).Y = (Text1.Top + Text1.Height) / TY - 2
57.
58. PT1(4).X = (Text1.Left + Text1.Width) / TX
59. PT1(4).Y = (Text1.Top + Text1.Height) / TY
60.
61. PT1(5).X = (Text1.Left + Text1.Width) / TX
62. PT1(5).Y = (Text1.Top) / TY
63. End Sub
64. Create a new procedure called Shadow. Add the following code to this procedure:
65. Sub Shadow()
66. hbr = CreateSolidBrush(RGB(125, 125, 125))
67. hpen = CreatePen(PS_SOLID, 1, RGB(125, 125, 125))
68.
69. r = SelectObject(DC_FRM, hbr)
70. r = SelectObject(DC_FRM, hpen)
71.
72. r = Polygon(DC_FRM, PT1(0), 6)
73.
74. r = SelectObject(DC_FRM, rbrush)
75. r1 = DeleteObject(r)
76. r = SelectObject(DC_FRM, rpen)
77. r1 = DeleteObject(r)
78. End Sub
79. Add a new module to the project. Module.Bas is created by default.

80. Add the following POINTAPI structure to Module.Bas:


81. Type POINTAPI '4 bytes
82. X As Integer
83. Y As Integer
84. End Type

81: Repairing and Compressing a


Microsoft Access Database from Visual
Basic
May 8, 1995

Abstract
Within a Visual Basic® application, you can compress and repair a Microsoft® Access®
database (.MDB) file. This article explains how you can accomplish these two tasks in
Visual Basic.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Repairing and Compacting Access Files
On occasion, a Microsoft® Access® database file can become damaged. For example, a
database can become damaged if the computer system is powered down without first
closing the database file. The RepairDatabase statement provided in Visual Basic® can
be used to repair a previously corrupted database file. You need only pass the name of the
.MDB file to the statement to repair it.

When records are deleted from a database file, the file can become defragmented. You
can compress a defragmented database file by using Visual Basic's CompactDatabase
statement. CompactDatabase's main purpose is to compress a Microsoft Access file, but
it can also be used to change the database's sort order, encrypt/decrypt the database, or
create a Microsoft Access 1.0 compatible file.
The CompactDatabase statement requires four arguments, as follows:
SourceFile The database's complete path and filename.
DestFile The database's new path and filename.
Locale The sorting order to be used.
Options Set to one of the following values:
DB_ENCRYPT Encrypt database.
DB_DECRYPT Decrypt database.
DB_VERSION10 Create a compatible Access 1.0 database file.

When the CompactDatabase statement is executed, it copies each valid record from the
original database file to the new database file. Note that these two filenames must be
different and that the security settings of the original file are automatically transferred to
the new file.

Example Program
The program below shows how to repair and/or compact a Microsoft Access database
(.MDB) file. To repair a damaged database file, click the "Repair" command button; to
compress (remove deleted records) a database file, click the "Compact" command button.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Common Dialog control to Form1. CommonDialog1 is created by default.

3. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Repair".

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. On Error GoTo Repair_Error

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


7. Dim MDB_Name As String
8.
9. CommonDialog1.Filter = "Access (*.mdb)|*.mdb"
10. CommonDialog1.Flags = &H1000
11. CommonDialog1.FilterIndex = 1
12. CommonDialog1.Action = 1
13.
14. If CommonDialog1.FileName <> "" Then
15. Screen.MousePointer = 11
16. MDB_Name = CommonDialog1.FileName
17. RepairDatabase (MDB_Name)
18. Screen.MousePointer = 0
19. MsgBox "Database repaired successfully", 64, "Repair"
20. End If
21. Screen.MousePointer = 0
22. Exit Sub
23. Repair_Error:
24. MsgBox "Error when repairing database", 16, "Error"
25. Screen.MousePointer = 0
26. Exit Sub
27. End Sub
28. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Compact".

29. Add the following code to the Click event for Command2:
30. Private Sub Command2_Click()
31. On Error GoTo Compact_Error
32.
33. Dim MDB_Name As String
34. Dim MDB_NewName As String
35. Dim MDB_Local As String
36. Dim MDB_Options As String
37.
38. MDB_NewName = "c:\dummy.mdb"
39. CommonDialog1.Filter = "Access (*.MDB)|*.mdb"
40. CommonDialog1.Flags = &H1000
41. CommonDialog1.FilterIndex = 1
42. CommonDialog1.Action = 1
43.
44. If CommonDialog1.FileName <> "" Then
45. MDB_Name = CommonDialog1.FileName
46. CompactDatabase MDB_Name, MDB_NewName & MDB_Local & MDB_Options
47. Kill MDB_Name
48. Name MDB_NewName & MDB_Local & MDB_Options As MDB_Name
49. MsgBox "Database compressed OK", 64, "Compact"
50. End If
51. Exit Sub
52. Compact_Error:
53. MsgBox "Unable to compress database", 16, "Error"
54. Exit Sub
55. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


82: Retrieving Multiple Filenames
from the Common Dialog Control
May 15, 1995

Abstract
The Common Dialog control in Visual Basic® allows you to display an Open File dialog
box. You can select one or more filenames from the Open File dialog box to use within
your Visual Basic program. This article explains how you can retrieve multiple filenames
from the dialog box, parse them into separate strings, and display them in a List Box
control.

Parsing Filenames from the Common Dialog Control


In a Visual Basic® application, you can use an Open File dialog box to allow your users
to select a file. Using the Open File dialog box, users can select the drive and directory,
as well as the individual files they want to use. To select a file, the user simply clicks the
filename. The dialog box’s FileName property can be used in your program to determine
the name of the selected file.
If the Flags property of the Common Dialog control is set to a value of 512 (&H200),
the user can select a group of files to work with. To select multiple files, the user would
hold the Shift key down while clicking the mouse on each filename. As with selecting a
single file, the FileName property of the dialog box would return the names of all the
selected files. Each filename is separated by a space character.
The InStr function can be used within a Do-While loop to parse, or extract, each
individual filename from the FileName property. Assuming that the filenames are stored
in the string called FileNames, we can tell the InStr function to search through the string
until it finds a space character. To extract a single filename, you need to first save the
position in the target string that you are starting to search from (this is the beginning of
the filename). Then you would use the InStr function to search for the first space
character in the string. If a space character is found, you can use the starting position and
the position returned by InStr to extract that single filename.

Example Program
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Common Dialog control to Form1. CommonDialog1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Add a List Box control to Form1. List1 is created by default.

4. Add a Command Button control to Form1. Command1 is created by default.

5. Add the following code to the Click event for Command1:


6. Private Sub Command1_Click()
7. Dim DelimPos As Integer
8. Dim FileNames As String
9. Dim NextName As String
10.
11. CommonDialog1.Flags = &H200&
12. CommonDialog1.Action = 1
13. CommonDialog1.Filter = 1
14.
15. FileNames = CommonDialog1.FileName
16.
17. Do While Len(FileNames) > 0
18. DelimPos = InStr(FileNames, " ")
19. If DelimPos = 0 Then
20. NextName = FileNames
21. FileNames = ""
22. Else
23. NextName = Mid$(FileNames, 1, DelimPos - 1)
24. FileNames = Mid$(FileNames, DelimPos + 1)
25. End If
26. List1.AddItem NextName
27. Loop
28. End Sub
Run the example program by pressing the F5 function key. Click the command button to
call up the Open File dialog box. Type a filename such as “*.*” and click the OK
command button. Select several files from the file list by holding the SHIFT key down
and clicking each individual filename. Click the OK command button when you have
selected several files. The files you selected will be displayed in the List Box control

83: Listing Fields and Associated


Properties for an Attached Microsoft
Access Table
May 15, 1995

Abstract
This article describes a sample user-defined Access Basic function that you can use to
retrieve all field names and their associated properties for an attached Microsoft®
Access® table.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


More Information
This article assumes that you are familiar with Access Basic and with creating
Microsoft® Access® applications using the programming tools provided with Microsoft
Access. For more information on Access Basic, please refer to the Building Applications
manual for Access 2.0 and the Introduction to Programming manual for Access 1.x.
The example program below uses tools in Visual Basic® to get information from a
Microsoft Access database.

Example Program
This program demonstrates how to create and use the sample ListFieldProperties()
function.
1. Open the sample database NWIND.MDB. (This database can usually be found in
the C:\ACCESS\SAMPAPPS directory.)

2. From the File menu, choose New, and select Module.

3. Enter the following code to create the ListFieldProperties() function:


4. Function ListFieldProperties ()
5. Dim MyDB As Database
6. Dim MyTable As TableDef
7. Set MyDB = DBEngine(0)(0)
8. Set MyTable = MyDB.TableDefs("Categories")
9. For X = 0 To MyTable.Fields.Count - 1
10. Debug.Print MyTable.Fields(X).Name
11. For Y = 0 To MyTable.Fields(X).Properties.Count - 1
12. Debug.Print Chr(9) & MyTable.Fields(X).Properties(Y).Name
13. Next Y
14. Next X
15. End Function
16. From the View menu, choose Immediate Window.

17. In the Immediate window, type the following line and press the ENTER key:
18. ? ListFieldProperties()
The name of each field in the Categories table will be displayed along with that
field's properties.

84: Creating a Scrolling "Credits"


Control
May 15, 1995

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Abstract
You can add visual appeal to your Visual Basic® applications by including a routine that
automatically scrolls text vertically within a picture box. This article explains how you
can add this functionality to your programs.

Scrolling Text Vertically Within a Picture Box


The Windows® application programming interface (API) BitBlt function can be used to
copy a section of a Picture Box control to another section of that same control. You must
remember to set the ScaleMode property of the Picture Box control to Pixel mode.

The example program below shows how to use the BitBlt function to print scrolling text
on a Picture Box control. A Timer control is used to print a string of text on the Picture
Box control at selected time intervals.

Example Program
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
the Declare statement must be typed as a single line of code):
3. Const SRCCOPY = &HCC0020
4. Const ShowText$ = "This line of text scrolls vertically."
5. Private Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X
6. As Integer, ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight
7. As Integer, ByVal hSrcDC As Integer, ByVal XSrc As Integer, ByVal YSrc
8. As Integer, ByVal dwRop As Long) As Integer
9. Dim ShowIt%
10. Add a Picture Box control to Form1. Picture1 is created by default. Set its
ScaleMode property to 3-Pixel.

11. Add a Timer control to Form1. Timer1 is created by default. Set its Interval
property to 25.

12. Add the following code to the Timer event for Timer1 (note that the Ret = line
must be typed as a single line of code):
13. Private Sub Timer1_Timer()
14. Dim Ret As Integer
15. If (ShowIt% = 30) Then
16. Picture1.CurrentX = 0
17. Picture1.CurrentY = Picture1.ScaleHeight - 30
18. Picture1.Print ShowText$
19. ShowIt% = 0
20. Else
21. Ret = BitBlt(Picture1.hDC, 0, 0, Picture1.ScaleWidth,
22. Picture1.ScaleHeight - 1, Picture1.hDC, 0, 1, SRCCOPY)
23. ShowIt% = ShowIt% + 1

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


24. End If
25. End Sub
Run the program by pressing the F5 function key. After a short time, the text "This line of
text scrolls vertically." will be displayed in the Picture Box control. Each time the Timer
control reaches 25, the line of text will be scrolled upward in the Picture Box control.

85: Hiding MDI Child Forms at Run


Time
May 15, 1995

Abstract
There may be situations in which you do not want a multiple-document interface (MDI)
child form displayed while your program is executing. This article explains how you can
hide an MDI child form.

Making MDI Forms Invisible


A multiple-document interface (MDI) child form allows you to have several windows
open at the same time with different documents loaded in each window. This is how
Notepad and similar programs operate so that you can switch between different text files.
You cannot, however, hide an MDI child form at run time.

So how can you temporarily hide an MDI child window from the user? By moving the
window to a nonexistent position on your screen.
In the example program below, the MDI child form is moved off the current viewing area
of the screen. Windows® itself doesn't care where you place the window; and, to your
user, it appears as if the window has been hidden.

Example Program
This program shows how you can temporarily hide an MDI child form in your Visual
Basic® application. Run the program by pressing the F5 function key. The MDI child
window is visible on the screen. Click the mouse on the main form. The MDI child
window disappears from view. Double-click the main form and the MDI child form is
again visible on the screen.

1. Create a new project in Visual Basic. Form1 is created by default. Set the
following properties for Form1:
BorderStyle = 1-Fixed Single
Height = 1140
Left = 2220

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Top = 3030
Width = 4605
2. From the Insert menu, select MDI Form. MDIForm1 is created by default.

3. From the Insert menu, select Form. Form2 is created by default. Set the form's
MDIChild property to True.

4. Add the following code to the Click event for MDIForm:


5. Private Sub MDIForm_Click()
6. Form1.Move -(2 * Form1.Width), -(2 * Form1.Height)
7. End Sub
8. Add the following code to the DblClick event for MDIForm:
9. Private Sub MDIForm_DblClick()
10. Form1.Move 2220, 3030, 4605, 1140
11. End Sub
12. Add the following code to the Form_Load event for MDIForm:
13. Private Sub
14. MDIForm_Load()
15. Form1.Show
16. Form2.Show
17. End Sub

86: Allowing a Visual Basic Application to


Accept Drag-and-Drop Files
May 15, 1995

Abstract
Many Windows®-based applications can accept, or process, a file that has been dragged
from File Manager. This article explains how you can add this feature to your own Visual
Basic® application.

Using MSGBLAST.VBX to Accept Drag-and-Drop


Files
Using File Manager, you can drag a file to another application and, when you release the
mouse button (drop the file), the target application can process the file any way it wants
to.
In order for a program to be able to accept drag-and-drop files, however, the program
must have a method of recognizing when a file has been sent to it. In Visual Basic®, this
can be done by using the Message Blaster custom control and three Windows®

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


application programming interface (API) functions: DragAcceptFiles, DragQueryFile,
and DragFinish.
The DragAcceptFiles function tells Windows that a specific window (that is, your Visual
Basic application's form) can accept files dropped from File Manager. The Declare
statement for this function is:
Private Declare Sub DragAcceptFiles Lib "shell" (ByVal hWnd As Integer, ByVal
bool As Integer)
(Note that this Declare statement must be typed as a single line of code.)
The DragAcceptFiles function takes only two arguments: the handle of the window that
will accept the dropped files, and an integer value that specifies if the file can be accepted
or ignored. If the Boolean argument is set to True, the window can accept dropped files;
if it is set to zero, the window can no longer accept dropped files.
You can retrieve the name of the file that was dropped on the target window by calling
the DragQueryFile function. This function's declaration statement is:
Private Declare Function DragQueryFile Lib "shell" (ByVal wParam As Integer,
ByVal Index As Integer, ByVal lpszFile As Any, ByVal BufferSize As Integer)
As Integer
(Note that this Declare statement must be typed as a single line of code.)
DragQueryFile requires four arguments, as follows:
wParam An integer value that contains the internal data structure's handle. This is
provided by the WM_DROPFILES message.
Index An integer value containing the number of the individual file to be retrieved.
If this value is -1, the number of files listed in the wParam structure will be
returned.
lpszFile A string buffer that contains the name of the dropped file.
BufferSize An integer value containing the maximum number of characters in lpszFile.

After calling the DragQueryFile function, an integer value reports the status of the
function. This value contains the number of characters copied to the lpszFile string or the
number of files available if Index was set to zero.
The third function needed to work with drag-and-drop files is the DragFinish function.
This function simply requires that the internal data structure's handle be passed to it.
DragFinish frees all structures used when transferring the file to the target application.
The final step is to process the WM_DROPFILES message. This message is sent by
Windows each time it needs to send a drag-and-drop request to a program. In your Visual
Basic program you need only use the Message Blaster custom control to intercept the
WM_DROPFILES message before Windows actually processes it itself. In the example
program below, we use the Message Blaster control to retrieve the name of the dropped
file and store that name in the List Box control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
The example program below shows how to allow your Visual Basic application to accept
drag-and-drop files from File Manager. To use this demonstration program, first execute
the Windows Explorer or File Manager application. Then run the DEMO.EXE program.
When you drag a file from File Manager to DEMO.EXE's window and release the mouse
button, the filename will be displayed in the List Box control.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
each Declare statement must be typed as a single line of code):
3. Option Explicit
4. Private Declare Sub DragAcceptFiles Lib "shell" (ByVal hWnd As Integer, ByVal
5. bool As Integer)
6. Private Declare Function DragQueryFile Lib "shell" (ByVal wParam As Integer,
7. ByVal Index As Integer, ByVal lpszFile As Any, ByVal BufferSize As Integer)
8. As Integer
9. Private Declare Sub DragFinish Lib "shell" (ByVal hDrop As Integer)
10. Const WM_DROPFILES = &H233
11. Add the following code to the Form_Load event for Form1:
12. Private Sub Form_Load()
13. msgblaster1.MsgList(0) = WM_DROPFILES
14. msgblaster1.hWndTarget = Me.hWnd
15. msgblaster1.MsgPassage(0) = 1
16. DragAcceptFiles Me.hWnd, True
17.
18. End Sub
19. Add a Message Blaster custom control to Form1. MsgBlaster1 is created by
default.

20. Add the following code to the MsgBlaster1_Message event for MsgBlaster1:
21. Private Sub MsgBlaster1_Message(MsgVal As Integer, wParam As Integer, lParam As
22. Long, ReturnVal As Long)
23. Dim hFilesInfo As Integer
24. Dim szFileName As String
25. hFilesInfo = wParam
26. wTotalFiles = DragQueryFile(hFilesInfo, &HFFFF, ByVal 0&, 0)
27. For wIndex = 0 To wTotalFiles
28. szFileName = Space$(50)
29. Retv% = DragQueryFile(hFilesInfo, wIndex, szFileName, 50)
30. list1.AddItem szFileName
31. Next wIndex
32. DragFinish (hFilesInfo)
33. End Sub
34. Compile the program. From Visual Basic's File menu, select Make EXE File to
create the executable file called DEMO.EXE.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


87: Sending Output to the Printer in Any
Order
May 15, 1995

Abstract
You can use the Visual Basic® Print method to send text to the default printer. This
article explains how you can send data to the printer in any order (that is, you can print a
line of text in the middle of the page and then print some other text at the top of page).

Outputting Data to the Printer


In a Visual Basic® application, you may need to create a hard copy of some data. To
send data to the default printer, you use Visual Basic's Print method. For example, to
send a line of text to the printer, you would issue a statement such as:
Printer.Print "This is a test"
When this statement is executed, Visual Basic will print the text on the printer. Note that
the text is printed at the coordinates specified by the CurrentX and CurrentY properties.
Each time you send data to the printer, Visual Basic automatically updates the CurrentX
and CurrentY properties. CurrentX is incremented each time a new character is sent to
the printer on the same line. When a new line is needed, the value in CurrentX is reset to
zero, and CurrentY is incremented by one to account for the new line.

Therefore, as the example program below shows, you can print to any specific physical
location on the paper. You can print a line of text in the center of the paper first and then,
by simply changing the CurrentX and CurrentY properties, print a line of text at the top
of the page.

Example Program
The example program below prints two lines of text on the default printer. The first line
is actually the second line to be physically transferred to the printer.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

3. Add the following code to the Click event for Command1:


4. Private Sub Command1_Click()
5. Printer.ScaleMode = 2
6. Printer.FontSize = 42
7. Printer.CurrentX = 40
8. Printer.CurrentY = 40

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


9. Printer.Print "This is the first line to be printed"
10.
11. Printer.CurrentX = 40
12. Printer.CurrentY = 12
13. Printer.FontSize = 14
14. Printer.Print "This is actually the second line to be printed"
15. Printer.EndDoc
16.
17. End Sub

88: Shrinking Icons Down to Size


July 1, 1995

Abstract
You can use the Windows® application programming interface (API) BitBlt function to
modify the size of an icon. This article explains how to enlarge or shrink an icon.

Modifying an Icon's Size


You can use the Windows® application programming interface (API) BitBlt function to
create an icon that is smaller or larger than the original icon. The BitBlt function copies a
memory device context to another memory device context. (A memory device context is
a block of memory that represents a display surface, such as an Image or Picture Box
control. See Tip # 31 in this series, "Creating the Windows Wallpaper Effect" for a
complete explanation of the BitBlt function.)
In the example program below, we first load an icon into an Image control. Then we
modify the Image control's Height and Width properties so the icon becomes 75 percent
smaller than its original size. The BitBlt function is then used to copy the icon stored in
the Image control to the Picture Box control.

Example Program
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X As
4. Integer, ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As
5. Integer, ByVal hSrcDC As Integer, ByVal XSrc As Integer, ByVal YSrc As
6. Integer, ByVal dwRop As Long) As Integer
7. Const SRCCOPY = &HCC0020
8. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Shrink Icon".

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


9. Add the following code to the Click event for Command1:
10. Private Sub Command1_Click()
11. Dim X As Integer
12. Dim Y As Integer
13. Dim W As Integer
14. Dim H As Integer
15. Dim Ret As Integer
16.
17. Image1 = LoadPicture("c:\vb\icons\misc\binoculr.ico")
18. Image1.Width = 0.75 * Image1.Width
19. Image1.Height = 0.75 * Image1.Height
20. Picture1.Width = Image1.Width
21. Picture1.Height = Image1.Height
22.
23. X = Image1.Left / Screen.TwipsPerPixelX
24. Y = Image1.Top / Screen.TwipsPerPixelY
25.
26. W = Picture1.Width / Screen.TwipsPerPixelX
27. H = Picture1.Height / Screen.TwipsPerPixelY
28.
29. Ret = BitBlt(Picture1.hDC, 0, 0, W, H, Form1.hDC, X, Y, SRCCOPY)
30. Picture1.Refresh
31.
32. End Sub
33. Add an Image control to Form1. Image1 is created by default. Set its Stretch
property to True.

34. Add a Picture Box control to Form1. Picture1 is created by default. Set its
AutoRedraw property to True.

89: Invoking the Microsoft Word Page


Setup Dialog Box
May 22, 1995

Abstract
From within a Visual Basic® application, you can instruct Microsoft® Word to display
its Page Setup dialog box. This article explains how this task can be accomplished by
creating a Word Basic macro and executing that macro from within Visual Basic.

Calling Word Basic Macros from Within Visual Basic


You can execute a Word Basic macro from within a Visual Basic® application to
perform any number of tasks. The example program below executes Microsoft® Word
and opens up a new document page. Then the program calls a Word Basic macro,
FPSdlg, to display the Word Page Setup dialog box.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


In order to execute Word from within a Visual Basic application, you must initiate a
connection to Word. This is done by calling the CreateObject function. This function
lets you create an OLE object, in this case Microsoft Word. When this function is
executed, it runs Word if it is not already running in memory. To transfer control to
Word, you can use the AppActivate function.

The next step is to call the Word Basic macro that you have previously created in Word.
In your Visual Basic program, you execute a Word Basic command with the
Object.Method syntax. For example, after executing Word, we want to start with a new
document. Therefore, we issue the statement wobjApp.FileNewDefault, where the
object has been defined as Word and FileNewDefault is the Basic command we want to
execute. A list of these Word Basic commands can be found in the WRDBASIC.HLP file
that is stored in the Word directory when you initially installed Microsoft Word.

After the new document screen has been displayed in Word, we want to actually execute
the Word Basic macro called CallFPS. To do this, we again issue a Word Basic
command called ToolsMacro. This command executes the specified macro file in Word.
When the macro has been terminated, we can close Word and return to our Visual Basic
program by issuing the Set wobjApp=Nothing statement. This statement removes the
object variable, which in turn causes Word to terminate.

Example Program
The following example program shows how you can invoke a Word Basic macro to call
up the Word Page Setup dialog box.
First, you must create the Word Basic macro that the Visual Basic program will call. Start
Word. From the Tools menu, select Macro. Type the name of the macro as FPSdlg and
click the Create command button.
Type the following as the macro's text. When you're done, save the macro so that it can
be used by all newly created Word documents.
Sub Main
Dim FPSdlg as FilePageSetup
GetCurValues FPSdlg
rc = Dialog(FPSdlg)
If rc <> 0 then
FilePageSetup FPSdlg
End If
End Sub
At this point, make sure the macro actually works from within Word. From the Tools
menu, select Macro. Click the name of the macro you want to execute—in this case, the
FPSdlg macro. Click the Run command button. Word should respond by popping up the
Page Setup dialog box. Click the Cancel button to return to the document screen. Exit
Word.
Now, you need to create the demonstration program in Visual Basic by following these
steps:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

3. Add the following code to the Click event for Command1:


4. Private Sub Command1_Click()
5. Dim wobjApp As Object
6. Set wobjApp = CreateObject("word.basic")
7. AppActivate "Microsoft Word"
8.
9. wobjApp.FileNewDefault
10. wobjApp.ToolsMacro "CallFPS", True
11. Set wobjApp = Nothing
12.
13. End Sub
To run the example program, press the F5 function key. Click the command button. After
a few seconds, Word will be executed and the FPSdlg macro will also be executed. The
program stops after it has displayed the Page Setup dialog box in Word. Click the Cancel
command button and you will be returned to Visual Basic's design environment.

90: Adding ToolTips to Visual Basic


Applications
May 22, 1995

Abstract
Many Windows®-based applications have a toolbox from which you can select a
command to execute. When you move the cursor (mouse pointer) over an item in the
toolbox, a ToolTip is displayed. This article shows how to add this ToolTip help feature
to your own Visual Basic® applications.

Creating ToolTips in Visual Basic


You can create a more professional-looking Visual Basic® application if you include the
ToolTips that are featured in the newest version of Microsoft® Word. ToolTips are small,
yellow description "balloons" that pop up on your screen when your cursor (mouse
pointer) is over a control. The ToolTip usually contains a short descriptive word or
phrase that describes the control's underlying purpose. It makes memorizing what icons
do a thing of the past.
The example program below shows the code you need to add to your application to create
and use ToolTips. Let's assume that you want to add a Command Button control to your
form. In the Tag property of the Command Button control you place the text that you
want displayed when the user moves the cursor over the control. This descriptive text can
be as long as you like, but keep in mind that the shorter the description, the better. The

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


AutoSize property of the Picture Box control must be set to True so that when the
program is running, the Picture Box will size itself according to the length of the
descriptive text you entered in the Tag property.
Next, you need to tell your Visual Basic application when to display the ToolTips
description. In the MouseMove event, you want to call the ToolTips function with the
statement:

ToolTip Me, Command1, True


where "Me" is the form the control resides on, "Command1" is the name of the individual
control, and True means you want to display the ToolTip for this control. If you don't
want to display a ToolTip for this particular control, set the last argument to False. This is
done in the form's MoveMove event so that no ToolTip is ever displayed while the cursor
is over the form itself. Once the Timer's interval has elapsed, the ToolTip will be
displayed for that control.

Example Program
This program shows how to add ToolTips to a Visual Basic application. Press the F5
function key to run the example program. You will see a Command Button control and
a Text Box control on the form. Move the cursor over the Command Button and the
ToolTip message "A command button control to click on" will be displayed. Move the
cursor over the Text Box control and the ToolTip message "A text box control" will be
displayed.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Private Sub Form_Load()
4. Timer1.Enabled = True
5. ToolTips Me, ToolTip, False
6. End Sub
7. Add the following code to the MouseMove event for Form1 (note that the Private
line must be typed as a single line of code):
8. Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single,
9. Y As Single)
10. ToolTips Me, ToolTip, False
11. End Sub
12. Add a Timer control to Form1. Timer1 is created by default. Set its Interval
property to 1.

13. Add the following code to the Timer_Event for Timer1:


14. Private Sub Timer1_Timer()
15. ToolTip.Visible = True
16. ToolTip.ZOrder 0
17. Timer1.Enabled = False
18. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


19. Add a Picture Box control to Form1. Picture1 is created by default. Set the
following properties for Picture1:
AutoSize True
BackColor &H0000FFFF& (yellow)
Height 255
Left 480
Name ToolTip
Top 480
Visible False
Width 975

7. Add a Command Button control to Form1. Command1 is created by default. Set


its Tag property to "A command button control to click on".

8. Add the following code to the Click event for Command1:


9. Private Sub Command1_Click()
10. ToolTips Me, Command1, False
11. End Sub
12. Add the following code to the MouseMove event for Command1 (note that the
Private line must be typed as a single line of code):
13. Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single,
14. Y As Single)
15. ToolTips Me, Command1, True
16. End Sub
17. Add a Text Box control to Form1. Text1 is created by default. Set its Tag
property to "A text box".

18. Add the following code to the Click event for Text1:
19. Private Sub Text1_Click()
20. ToolTips Me, Text1, False
21. End Sub
22. Add the following code to the MouseMove event for Text1 (note that the Private
line must be typed as a single line of code):
23. Private Sub Text1_MouseMove(Button As Integer, Shift As Integer, X As Single,
24. Y As Single)
25. ToolTips Me, Text1, True
26. End Sub
27. Create a new function called ToolTips. Add the following code to this function:
28. Sub ToolTips(Frm As Form, Ctl As Control, OnOff As Integer)
29. If OnOff Then
30. Frm.ToolTip.Cls
31. Frm.ToolTip.Print " " & Ctl.Tag & " "
32. Frm.ToolTip.Width = Frm.ToolTip.TextWidth(Ctl.Tag & " ")

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


33. If Ctl.Top + Ctl.Height + Frm.ToolTip.Height + 40 < Frm.ScaleHeight Then
34. Frm.ToolTip.Top = Ctl.Top + Ctl.Height + 40
35. Else
36. Frm.ToolTip.Top = Ctl.Top - Frm.ToolTip.Height - 40
37. End If
38. If Ctl.Left + Frm.ToolTip.Width < Frm.ScaleWidth Then
39. Frm.ToolTip.Left = Ctl.Left
40. Else
41. Frm.ToolTip.Left = Ctl.Left - Frm.ToolTip.Width + Ctl.Width
42. End If
43. Frm.Timer1.Enabled = True
44. Else
45. Frm.ToolTip.Visible = False
46. Frm.ToolTip.ZOrder 1
47. Frm.Timer1.Enabled = False
48. End If
49. End Sub

91: Determining If a Form Is Loaded


May 22, 1995

Abstract
A Visual Basic® application may contain many different forms. This article presents a
function that can be used to determine if a form is currently loaded into memory.

Is the Form Loaded in Memory?


Because a Visual Basic® application can contain several forms, you need to be able to
determine if a form is actually loaded into memory. The function presented in the
example program below tests to see if a form is loaded. This function will not load the
form—it simply tests to see if it is already in memory.

Example Program
This program shows how to find out if a specific form is already loaded in a running
Visual Basic application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. From the Insert menu, select Form. Form2 is created by default.

3. From the Insert menu, select Form. Form3 is created by default.

4. Add the following code to the Form_Load event for Form1:


5. Private Sub Form_Load()
6. Form2.Show
7. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Add a Command Button control to Form1. Command1 is created by default.

9. Add the following code to the Click event for Command1:


10. Private Sub Command1_Click()
11. Dim X As Integer
12. X = IsFormLoaded(Form2)
13. If X Then
14. MsgBox "Form2 is loaded"
15. End If
16. X = IsFormLoaded(Form3)
17. If X = False Then
18. MsgBox "Form3 is not loaded"
19. End If
20. End Sub
21. Create a new function called IsFormLoaded. Add the following code to this
function:
22. Function IsFormLoaded(FormToCheck As Form) As Integer
23. Dim Y As Integer
24.
25. For Y = 0 To Forms.Count - 1
26. If Forms(Y) Is FormToCheck Then
27. IsFormLoaded = True
28. Exit Function
29. End If
30. Next
31. IsFormLoaded = False
32. End Function
When you run this program, click the Command Button. A message box will pop up on
the screen displaying the "Form2 is loaded" message. Click the OK command button. A
second message box is immediately shown displaying the "Form3 is not loaded"
message.

92: Using the Shell Statement to


Execute MS-DOS Programs
May 22, 1995

Abstract
From within a Visual Basic® application, you can execute an MS-DOS® program. This
article explains how to use the Shell statement in conjunction with the Windows®
application programming interface (API) GetNumTasks function to execute an MS-DOS
program.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Running MS-DOS Programs
A very popular set of utilities called PKZIP and PKUNZIP can be found on bulletin
board systems throughout the world. These two utilities are used to compress and
decompress a group of related files. The resulting ZIP file can then be distributed as one
single entity. Because these programs are used by people daily, you may need to allow
your user to execute these MS-DOS programs (or an entirely different MS-DOS
program) from within your Visual Basic® application program.
Visual Basic's Shell function can be used to execute another program. To run a program,
you would issue the statement:
X = Shell("program name",[windowstate]])
The program name must be a valid MS-DOS or Windows®-based-application name and
may optionally include any command-line parameters needed by the program. The
second parameter tells Visual Basic how to execute the program. There are five possible
values for this argument, as follows:
1 Normal size, program retains focus
2 Minimized, program retains focus
3 Maximized, program retains focus
4 Normal size, Visual Basic application retains focus
5 Minimized, program does not retain focus

While the MS-DOS program is executing, you can use the SendKeys statement to send
specific keystrokes to the application. In this manner, you can actually control what the
MS-DOS program does or provide some required input.
In addition, you can determine when the secondary program has terminated by using the
GetNumTasks function. To use the GetNumTasks function in your Visual Basic
application, you must include the following Declare statement in the Global Module or
General Declarations section of your form:
Private Declare Function GetNumTasks Lib "Kernel" () As Integer
The GetNumTasks function does not require any arguments but simply returns an
integer value set to the number of tasks that are currently running under Windows.
Therefore, to determine when your MS-DOS program has finished executing, call
GetNumTasks first, saving the value it returns in a variable. After the MS-DOS program
has finished executing, call the GetNumTasks function a second time to find out if the
number of tasks has decreased by a value of one. If the value has decreased, you know
that your MS-DOS program has finished executing and you can return control to your
Visual Basic application.

Example Program
This program shows how you can use the Shell function and the GetNumTasks function
to execute an MS-DOS program. This program assumes that you have the PKUNZIP

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


program stored in the UTILS directory and that you have previously created a destination
directory called DESTDIR on your hard drive.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following statements to the General Declarations section of Form1:


3. Dim ActiveApps As Integer
4. Private Declare Function GetNumTasks Lib "Kernel" () As Integer
5. Add a Command Button control to Form1. Command1 is created by default.

6. Add the following code to the Click event for Command1:


7. Private Sub Command1_Click()
8. Dim AppDir As String
9. Dim Zip As String
10. Dim Y As Integer
11. Dim X As Integer
12.
13. AppDir = "c:\destdir"
14. ActiveApps = GetNumTasks()
15.
16. Zip = "c:\utils\pkunzip " & "c:\destdir\" & "test.zip" & " " & AppDir
17. X = Shell(Zip, 2)
18. SendKeys "%{enter}EXIT%{ }n"
19.
20. Do While GetNumTasks() <> ActiveApps
21. Y = DoEvents()
22. Loop
23. MsgBox "Pkunzip is finished", 0, "Demo Program"
24.
25. End Sub

93: Retrieving the Names of Installed


Printers
May 22, 1995

Abstract
The Windows® initialization file, WIN.INI, contains a list of all printers attached to the
computer system. This article contains an example program that retrieves the name of
each printer stored in the WIN.INI initialization file.

Using GetProfileString and GetPrivateProfileString


The Devices section of the WIN.INI initialization file contains the names of all printers
attached to your computer system. You can retrieve this list of printer names by using
two Windows® application programming interface (API) functions.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The Windows API GetProfileString and GetPrivateProfileString functions can be used
to retrieve the name of a printer as stored in the WIN.INI file. For a complete discussion
of these functions, see the articles listed in the "Additional References" section of this
article.

Example Program
This program retrieves the names of all installed printers from the WIN.INI initialization
file. The printer names are displayed in a List Box control.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant, Declare, and Type statements to the General
Declarations section of Form1 (note that each Declare statement must be typed as
a single line of text):
3. Option Explicit
4. Private Type WindowsDevice
5. WindowsDeviceUserName As String
6. WindowsDeviceShortName As String
7. WindowsDevicePortName As String
8. End Type
9. Private Declare Function GetProfileString Lib "Kernel" (ByVal lpAppName
10. As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal
11. lpReturnedString As String, ByVal nSize As Integer) As Integer
12. Private Declare Function GetPrivateProfileString Lib "Kernel" (ByVal lpAppName
13. As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal
14. lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As
15. String) As Integer
16. Private Declare Function GetProfileKeys Lib "Kernel" Alias "GetProfileString"
17. (ByVal lpAppName As String, ByVal lpKeyName As Long, ByVal lpDefault As
18. String, ByVal lpReturnedString As String, ByVal nSize As Integer) As Integer
19. Private Declare Function GetPrivateProfileKeys Lib "Kernel" Alias
20. "GetPrivateProfileString" (ByVal lpAppName As String, ByVal lpKeyName As
21. Long, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal
22. nSize As Integer, ByVal lpFileName As String) As Integer
23. Const WINDOWS_SECTION_NAME = "windows"
24. Const DEVICES_SECTION_NAME = "devices"
25. Const DEVICE_KEY_NAME = "device"
26. Const NO_PRINTER = "(none)"
27. Add a Text Box control to Form1. Text1 is created by default.

28. Add a Command Button control to Form1. Command1 is created by default.

29. Add the following code to the Click event for Command1 (note that the
List1.AddItem statement must be typed as a single line of code):
30. Private Sub Command1_Click()
31. Dim OrgPrinter As WindowsDevice
32. Call GetDefaultPrinter(OrgPrinter)
33. Text1.Text = OrgPrinter.WindowsDeviceUserName
34.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


35. Dim NumPrinters As Integer
36. ReDim InstalledPrinters(0) As WindowsDevice
37. Call GetInstalledPrinters(InstalledPrinters())
38. For NumPrinters = 1 To UBound(InstalledPrinters)
39. List1.AddItem InstalledPrinters(NumPrinters).WindowsDeviceUserName + "
40. on " + InstalledPrinters(NumPrinters).WindowsDevicePortName
41. Next
42. List1.AddItem NO_PRINTER, 0
43. End Sub
44. Create a new function called GetDefaultPrinter. Add the following code to this
function:
45. Private Sub GetDefaultPrinter(recDefaultPrinter As WindowsDevice)
46. Dim StrPos As Integer
47. Dim DefaultPrinter As String
48. Dim RC As Integer
49. DefaultPrinter = GetString(WINDOWS_SECTION_NAME, DEVICE_KEY_NAME, "", "")
50. StrPos = InStr(DefaultPrinter, ",")
51. recDefaultPrinter.WindowsDeviceUserName = Left$(DefaultPrinter, StrPos - 1)
52. DefaultPrinter = Mid$(DefaultPrinter, StrPos + 1)
53. StrPos = InStr(DefaultPrinter, ",")
54. recDefaultPrinter.WindowsDeviceShortName = Left$(DefaultPrinter, StrPos - 1)
55. recDefaultPrinter.WindowsDevicePortName = Mid$(DefaultPrinter, StrPos + 1)
56. End Sub
57. Create a new function called GetInstalledPrinter. Add the following code to this
function (note that the InstalledPrinter lines must be typed as a single line of
code):
58. Private Sub GetInstalledPrinters(recInstalledPrinters() As WindowsDevice)
59. Dim StrPos As Integer
60. Dim PrtSub As Integer
61. Dim InstalledPrinter As String
62. ReDim PrinterNames(0) As String
63. Call GetKeyNames(DEVICES_SECTION_NAME, PrinterNames(), "")
64. ReDim recInstalledPrinters(UBound(PrinterNames))
65. For PrtSub = 1 To UBound(PrinterNames)
66. InstalledPrinter = GetString(DEVICES_SECTION_NAME, PrinterNames(PrtSub),
67. "", "")
68. StrPos = InStr(InstalledPrinter, ",")
69. recInstalledPrinters(PrtSub).WindowsDeviceUserName =
70. PrinterNames(PrtSub)
71. recInstalledPrinters(PrtSub).WindowsDeviceShortName =
72. Left$(InstalledPrinter, StrPos - 1)
73. InstalledPrinter = Mid$(InstalledPrinter, StrPos + 1)
74. StrPos = InStr(InstalledPrinter, ",")
75. If StrPos > 0 Then
76. recInstalledPrinters(PrtSub).WindowsDevicePortName =
77. Left$(InstalledPrinter, StrPos - 1)
78. Else
79. recInstalledPrinters(PrtSub).WindowsDevicePortName =
80. InstalledPrinter
81. End If
82. Next
83. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


84. Create a new function called GetString. Add the following code to this function
(note that the Function and KeyValueLength lines must each be typed as a single
line of code):
85. Function GetString(SectionName As String, KeyName As String, DefaultValue
86. As String, ProfileName As String) As String
87. Dim KeyValueLength As Integer
88. Dim KeyValue As String
89. KeyValue = Space$(256)
90. If Trim$(ProfileName) = "" Then
91. KeyValueLength = GetProfileString(SectionName, KeyName, DefaultValue,
92. KeyValue, Len(KeyValue))
93. Else
94. KeyValueLength = GetPrivateProfileString(SectionName, KeyName,
95. DefaultValue, KeyValue, Len(KeyValue), ProfileName)
96. End If
97. GetString = Left$(KeyValue, KeyValueLength)
98. End Function
99. Create a new function called GetKeyName. Add the following code to this
function (note that the Sub and KeyNamesLength lines must each be typed as a
single line of code):
100. Sub GetKeyNames(SectionName As String, KeyNames() As String, ProfileName
101. As String)
102. Dim StrPos As Integer
103. Dim KeyCount As Integer
104. Dim Start As Integer
105. Dim KeyNamesLength As Integer
106. Dim KeyNameString As String
107. KeyNameString = Space$(1024)
108. If Trim$(ProfileName) = "" Then
109. KeyNamesLength = GetProfileKeys(SectionName, 0, "", KeyNameString,
110. Len(KeyNameString))
111. Else
112. KeyNamesLength = GetPrivateProfileKeys(SectionName, 0, "",
113. KeyNameString, Len(KeyNameString), ProfileName)
114. End If
115. KeyCount = 0
116. ReDim KeyNames(0)
117. If KeyNamesLength > 0 Then
118. KeyNameString = Left$(KeyNameString, KeyNamesLength)
119. If Right$(KeyNameString, 1) <> Chr$(0) Then
120. KeyNameString = KeyNameString + Chr$(0)
121. End If
122. KeyNamesLength = Len(KeyNameString)
123. Start = 1
124. Do
125. StrPos = InStr(Start, KeyNameString, Chr$(0))
126. If StrPos > 0 Then
127. KeyCount = KeyCount + 1
128. ReDim Preserve KeyNames(KeyCount)
129. KeyNames(KeyCount) = Mid$(KeyNameString, Start, StrPos - Start)
130. If StrPos < KeyNamesLength Then
131. Start = StrPos + 1
132. Else

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


133. Exit Do
134. End If
135. Else
136. Exit Do
137. End If
138. Loop
139. End If
140. End Sub
Run the example program by pressing the F5 function key. Click the Command Button
control. The name of the default printer is displayed in the Text Box control, and a list of
all installed printers is displayed in the List Box control.

94: Using Drag-and-Drop on Multiple


Items in a List Box Control
May 22, 1995

Abstract
The drag-and-drop functionality provided in many Windows®-based applications allows
you to copy an item from one program to another or from one control to another control
in the same application. This article explains how to use this drag-and-drop technique in
Visual Basic® to copy multiple items selected in a List Box control to another List Box
control.

Dragging Multiple List Box Items


Many Windows-based applications include drag-and-drop functionality. This means that
you can select an item, such as an entry in a List Box control, click on the item, and,
while holding the mouse button down, drag that item to another window or control and
drop it on its new location.
The example program below shows how you can add this drag-and-drop feature to your
Visual Basic® applications. This program allows you to select multiple items in the
source List Box control and drag the whole group of selected items to a second List Box
control all at one time.

Example Program
This program shows how to drag several items selected in one List Box control to
another List Box control. Run the example program by pressing the F5 function key.
From the first List Box control, click the mouse on several items to select (highlight)
them. While clicking each item, hold down the SHIFT key. When you want to drag the
selected items to the second List Box control, click once on the first List Box control and
hold the mouse button down while you drag the control to the second List Box. Release
the mouse button to drop the selected items onto the second List Box control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


While using this program, you can select the items from the first List Box either by
holding the SHIFT key down while you click on each entry, or by simply clicking the
mouse on each individual entry. If you hold the SHIFT key down when selecting entries,
those entries will remain selected (highlighted) in the first List Box control after the
items have been dropped onto the second List Box control. If the SHIFT key is not used,
one of the selected items will not retain its selected status after the drag-and-drop
operation has finished.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Option Explicit
4. Dim IG As Integer
5. Dim LIG(20) As Integer
6. Dim LGlobal As Long
7. Const VK_SHIFT = &H10
8. Add the following code to the Form_Load event for Form1:
9. Private Sub Form_Load()
10. Dim X As Integer
11. IG = 0
12. For X = 0 To 9
13. List1.AddItem "Item #" + Str$(X)
14. Next X
15. List1.DragMode = 0
16. LGlobal = 99999
17. End Sub
18. Add a List Box control to Form1. List1 is created by default. Set its MultiSelect
property to 1-Simple.

19. Add the following code to the MouseDown event for List1 (note that the Private
line must be typed as a single line of code):
20. Private Sub List1_MouseDown(Button As Integer, Shift As Integer, X As Single,
21. Y As Single)
22. LGlobal = List1.ListIndex
23. For X = 1 To IG
24. List1.Selected(LIG(X)) = True
25. Next X
26. List1.Drag
27. End Sub
28. Add a second List Box control to Form1. List2 is created by default. Set its
MultiSelect property to 1-Simple.

29. Add the following code to the DragDrop event for List2:
30. Private Sub List2_DragDrop(Source As Control, X As Single, Y As Single)
31. For X = 0 To List1.ListCount - 1
32. If X = LGlobal Then
33. List2.AddItem List1.List(X)
34. Else

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


35. If List1.Selected(X) Then
36. List2.AddItem List1.List(X)
37. End If
38. End If
39. Next X
40. LGlobal = 99999
41. IG = 0
42. End Sub

95: Deleting All Records from Every


Table in a Microsoft Access Database
May 22, 1995

Abstract
A Microsoft® Access® database may contain several tables, with each table holding
many records. This article explains how to delete all records from all tables associated
with a Microsoft Access database application.

Removing Records from Tables


There may be occasions when you need to delete all records in a table from a Microsoft®
Access® database application. For instance, if you have an inventory program that
contains a table of stock and another table of purchase orders, at the end of the year you
would need to remove these records in preparation for the next year's information. To
remove each record from both tables would be a tedious job.

You can create an Access Basic function that will remove every record from the specified
table. However, if your Microsoft Access database contains many tables, you need to
process each individual table associated with that specific Microsoft Access database.

Example Program
This example Access program shows how to delete all records from every table in a
Microsoft Access database application. Note that this function also processes those table
names that include space characters in them (Inventory Year1, for example).

1. Open the sample database ORDERS.MDB. (This database can usually be found
in the C:\ACCESS\SAMPAPPS directory.)
Note: The example program will permanently modify this Access database.
Therefore, you should copy ORDERS.MDB to another directory and run this
program on the temporary copy of ORDERS.MDB.

2. From the File menu, choose New, and select Module.

3. Enter the following code to create the DeleteAllRecords() function:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


4. Function DeleteAllRecords ()
5. Dim DB As Database
6. Dim X As Integer
7. Dim TDF As TableDef
8. DoCmd SetWarnings False
9. Set DB = CurrentDB()
10. For X = 0 To DB.TableDefs.Count - 1
11. Set TDF = DB.TableDefs(X)
12. If (TDF.Attributes And DB_SYSTEMOBJECT) = 0 Then
13. DB.Execute "Delete * From [" & DB.TableDefs(X).Name & "]"
14. End If
15. Next X
16. DoCmd SetWarnings True
17. End Function
18. From the View menu, choose Immediate Window.

19. In the Immediate Window, type the following line and press the ENTER key:
20. ?DeleteAllRecords()
This statement will execute the DeleteAllRecords() function. After a short time,
all records will be deleted from each table included in the ORDERS.MDB
database.

96: Centering a Form over


Another Form
May 29, 1995

Abstract
You can position a form so it appears centered within another form. This article explains
how to center a form within its parent form. This same technique can be applied to
centering controls such as Picture Box controls over other controls.

Centering Forms in Visual Basic


When developing an application in Visual Basic®, you may need to position a form so it
is centered over another form. You can position a form in this way by using Visual
Basic's Left, Top, Height, and Width properties.
The Left property defines the position of the form's left edge and the Top property
defines the position of the form's top edge. In the same manner, the Width and Height
properties define how wide and high the form is. It is easy enough to center a form on a

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


container by calculating the width and height of the form and dividing that value by two
to center it within the control.
To center a form within a parent form, take the width of Form1 and Form2, subtract these
two values, and divide the result by two. Next, add Form1's width to the result to
determine the position of Form2's left edge within Form1. This will center Form2
horizontally within Form1.

In the same manner, you can center a form vertically within another form by using the
Top and Height properties of each form, dividing by two, and setting Form2's Top
property to the result.

Example Program
This program shows how to center a form over another form. After you run this example
program by pressing the F5 function key, click the Command Button. The program
displays Form2 centered over its "parent," Form1.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

3. Add the following code to the Click event for Command1:


4. Private Sub Command1_Click()
5. Form2.Show
6. CentreFormWithParent Form2, Form1
7. End Sub
8. From the Insert menu, select Form to create a second form. Form2 is created by
default. Change this form's size so it is smaller than Form1.

9. Create a new function called CreateFormWithParent. Add the following code


to this function:
10. Sub CenterFormWithParent(aForm As Form, aParent As Form)
11. aForm.Left = aParent.Left + (aParent.Width - aForm.Width) / 2
12.
13. aForm.Top = aParent.Top + (aParent.Height - aForm.Height) / 2
14.
15. If (aForm.Left + aForm.Width) > Screen.Width Then
16. aForm.Left = Screen.Width - aForm.Width
17. Else
18. If aForm.Left < 0 Then aForm.Left = 0
19. End If
20.
21. If (aForm.Top + aForm.Height) > Screen.Height Then
22. aForm.Top = Screen.Height - aForm.Height
23. Else
24. If aForm.Top < 0 Then aForm.Top = 0
25. End If
26. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


97: Creating a Task List
May 29, 1995

Abstract
This article explains how you can determine what modules are currently running under
Microsoft® Windows® and create a task list based on that information.

Determine What Modules Are Currently Running


The TOOLHELP.DLL dynamic-link library (.DLL) file contains two Windows®
application programming interface (API) functions that can be used to create a list of
modules currently loaded under Windows. These are the ModuleFirst and ModuleNext
functions. To declare these functions within your program, include the following Declare
statements in the Global Module or General Declarations section of a Visual Basic®
form:
Private Declare Function ModuleFirst Lib "toolhelp.dll" (mdlentry As
ModuleEntry) As Integer
Private Declare Function ModuleNext Lib "toolhelp.dll" (mdlentry As ModuleEntry)
As Integer
Note that each Declare statement must be typed as a single line of text.
The ModuleFirst and ModuleNext functions are the key to traversing the chain of
loaded modules in Windows. The ModuleFirst function fills the specified structure with
information describing the first module in the list of currently loaded modules. The
ModuleNext function is then called to find the next module in the list.
The MODULEENTRY structure required by these two functions must be defined as
follows:
dwSize The size of the structure in bytes.
szModule The module's name (a null-terminated string).
hModule The module's handle.
wcUsage Used by GetModuleUsage function.
szExePath The module.
wNext The window.

Before you can use these two functions, however, you must initialize the dwSize field of
the MODULEENTRY structure. This value should be specified as the number of bytes
needed to store the information returned by ModuleFirst and ModuleNext.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


These functions return a value indicating the status of the function. The function was
successful if the returned value is nonzero; the function was not successful (or no more
modules were found in memory) if the value returned is zero.

Example Program
This program shows how to retrieve the module name and path for every running task
under Windows. The name of each module is displayed in the first List Box control,
while the full path of the module is displayed in the second List Box control.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
each Declare statement should be typed as a single line of text):
3. Private Declare Function ModuleFirst Lib "toolhelp.dll" (mdlentry As
4. ModuleEntry) As Integer
5. Private Declare Function ModuleNext Lib "toolhelp.dll" (mdlentry As ModuleEntry)
6. As Integer
7. Add a Command Button control to Form1. Add the following code to the Click
event for Command1:
8. Private Sub Command1_Click()
9. Dim Tmp As ModuleEntry
10. Dim Retn As Integer
11.
12. Tmp.dwSize = Len(Tmp)
13. Retn = ModuleFirst(Tmp)
14.
15. While Retn <> 0
16. If InStr(Tmp.szExepath, ".VBX") <> 0 Or InStr(Tmp.szExepath, ".DLL") <>
17. 0 Or InStr(Tmp.szExepath, ">DRV") <> 0 Then
18. List1.AddItem Tmp.szModule
19. List2.AddItem Tmp.szExepath
20. End If
21. Tmp.szExepath = ""
22. Retn = ModuleNext(Tmp)
23. Wend
24. End Sub
25. Add a List Box control to Form1. List1 is created by default.

26. Add a second List Box control to Form1. List2 is created by default.

27. From the Insert menu, select Module. Module1.Bas is created by default.

28. Add the following user-defined Type to Module1.Bas:


29. Type ModuleEntry
30. dwSize As Long
31. szModule As String * 10
32. hModule As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


33. wcUsage As Integer
34. szExepath As String * 256
35. wNext As Integer
36. End Type

98: Saving a Window's Client Area in


Bitmap Format
May 29, 1995

Abstract
You may need to create a bitmap (.BMP) image file that contains a window's client area.
This article explains how to save a window's client area to disk in a bitmap image file
format.

Saving Bitmap Images to Disk


You can save the contents of a window to a disk file in bitmap format by retrieving the
target window's rectangle area and then using several Windows® application
programming interface (API) functions to save that image to a device context such as a
Picture Box control.
The GetWindowRect function can be used to retrieve the bounding rectangle of a
window or form. This rectangle includes the window's borders, title bars, and other
attributes associated with a window. The Declare statement for the GetWindowRect
function is as follows:
Private Declare Sub GetWindowRect Lib "User" (ByVal hWnd As Integer, lpRect
As RECT)
The GetWindowRect function takes two arguments: an integer value containing the
window's handle and a pointer to a RECT rectangle structure. The RECT structure will
contain the dimensions of the window's rectangle area after the function is called.
The BitBlt function uses the rectangle that contains the image you want to save to disk to
copy the image from one device context to another. In this case, BitBlt is used to copy
the window's client area to the Picture Box control. Then the Save As dialog box is used
to save the contents of the Picture Box to disk as a .BMP file.

Example Program
This program shows how to save the client area of a form or window and save it as a
.BMP file.
1. Create a new project in Visual Basic®. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Private Declare Function GetActiveWindow Lib "User" () As Integer
4. Private Declare Function GetWindowDC Lib "User" (ByVal hWnd As Integer)
5. As Integer
6. Private Declare Sub GetWindowRect Lib "User" (ByVal hWnd As Integer, lpRect
7. As RECT)
8. Private Declare Function ReleaseDC Lib "User" (ByVal hWnd As Integer, ByVal hDC
9. As Integer) As Integer
10. Private Declare Function BitBlt% Lib "GDI" (ByVal hDestDC%, ByVal X%, ByVal Y%,
11. ByVal nWidth%, ByVal nHeight%, ByVal hSrcDC%, ByVal XSrc%, ByVal YSrc%, ByVal
12. dwRop&)
13. Const SRCCOPY = &HCC0020
14. From the Insert menu, select Form to create a second form. Form2 is created by
default. Set its Picture property to "C:\WINDOWS\ARCHES.BMP".

15. From the Insert menu, select Module to create a BASIC module. Module1.Bas is
created by default.

16. Add the following Type structure to Module1.Bas:


17. Type RECT
18. Left As Integer
19. Top As Integer
20. Right As Integer
21. Bottom As Integer
22. End Type
23. Add a Timer control to Form1. Timer1 is created by default.

24. Add a Picture Box control to Form1. Picture1 is created by default. Set its
AutoRedraw property to True.

25. Add a Command Button control to Form1. Command1 is created by default.

26. Add the following code to the Click event for Command1:
27. Private Sub Command1_Click()
28. SaveToPicture
29. End Sub
30. Create a new function called SaveToPicture. Add the following code to this
function:
31. Sub SaveToPicture()
32. Dim hDCCur As Long
33. Dim hWndCur As Long
34. Dim HWndOld As Long
35. Dim Tim As Double
36. Dim ThisRect As RECT
37. Dim DX As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


38. Dim DY As Long
39.
40. HWndOld = GetActiveWindow()
41. Form2.Show
42. hWndCur = Form2.hWnd
43. Tim = Timer + 0.5
44.
45. Do
46. DoEvents
47. Loop Until Timer >= Tim
48.
49. hDCCur = GetWindowDC(hWndCur)
50. Call GetWindowRect(hWndCur, ThisRect)
51. DX = ThisRect.Right - ThisRect.Left + 2: DY = ThisRect.Bottom - ThisRect.Top
52. +2
53.
54. With Picture1
55. .Width = Screen.TwipsPerPixelX * DX
56. .Height = Screen.TwipsPerPixelY * DY
57. Call BitBlt(.hDC, 0, 0, DX, DY, hDCCur, 0, 0, SRCCOPY)
58. .Picture = .Image
59. End With
60.
61. Call ReleaseDC(hWndCur, hDCCur)
62. Form2.Hide
63.
64. CommonDialog1.DefaultExt = "BMP"
65. CommonDialog1.DialogTitle = "Save Window As"
66. CommonDialog1.FileName = "*.BMP"
67. CommonDialog1.Action = 2
68.
69. If CommonDialog1.FileName <> Empty Then
70. SavePicture Picture1.Picture, CommonDialog1.FileName
71. End If
72. End Sub
73. Add a Common Dialog control to Form1. CommonDialog1 is created by default.
Run this program by pressing the F5 function key. Click once on the command button.
The ARCHES.BMP picture is displayed on Form2. Next, the Save File As dialog box
pops up on the screen. Type a filename for the .BMP file and Visual Basic will save
Form2's window (which contains the ARCHES.BMP picture) to a new bitmap file.

99: Adding Three-Dimensional Effects to


Visual Basic Forms
May 29, 1995

Abstract

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


You can use functions in the CTL3D.DLL dynamic-link library to add a three-
dimensional (3-D) look to any form. This article explains how to use this .DLL to create a
3-D form in a Visual Basic® application.

Creating 3-D Forms


The CTL3D.DLL dynamic-link library contains Windows® application programming
interface (API) functions you can use to create three-dimensional (3-D) message boxes
and common dialog boxes in your Visual Basic® applications. You can also use these
functions to create a 3-D form. The form must have a fixed-double-style border. In
addition, the form's MinButton and MaxButton properties must be set to False.

As the example program below shows, the CTL3D.DLL functions can enable your Visual
Basic application to display 3-D forms, message boxes, and common dialog boxes.

Example Program
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

3. Add the following code to the Click event for Command1:


4. Private Sub Command1_Click()
5. Form2.Show
6. End Sub
7. From the Insert menu, select Form to create a second form. Form2 is created by
default. Set the form's BorderStyle property to 3-Fixed Double, the MinButton
property to False, and the MaxButton property to False.

8. Add the following code to the Form_Load event for Form2:


9. Private Sub Form_Load()
10. Call Ctl3DForm(Me)
11. End Sub
12. From the Insert menu, select Module to create a new module. Module1.Bas is
created by default.

13. Add the following code to Module1.Bas (note that the Private lines must each be
typed as a single line of code):
14. Const SWW_HPARENT = -8
15. Const GWW_HINSTANCE = -6
16. Const GWW_HPARENT = -8
17. Const BUTTON_FACE = &H8000000F
18. Const FIXED_DOUBLE = 3
19. Const DS_MODALFRAME = &H80&
20. Const GWL_STYLE = (-16)
21. Private Declare Function Ctl3DSubClassDlgEx Lib "CTL3D.DLL" (ByVal hWnd As
22. Integer, ByVal Flags As Long) As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


23. Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
24. nIndex As Integer) As Long
25. Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
26. nIndex As Integer, ByVal dwNewLong As Long) As Long
27. Sub Ctl3DForm(frm As Form)
28. Dim hWnd As Integer
29. Dim Result As Integer
30. Dim lStyle As Long
31. Dim Flag1 As Long
32.
33. hWnd = frm.hWnd
34. Flag1 = 0
35.
36. If frm.BorderStyle = FIXED_DOUBLE Then
37. frm.BackColor = BUTTON_FACE
38. lStyle = GetWindowLong(hWnd, GWL_STYLE)
39. lStyle = lStyle Or DS_MODALFRAME
40. lStyle = SetWindowLong(hWnd, GWL_STYLE, lStyle)
41. Result = Ctl3DSubClassDlgEx(hWnd, Flag1)
42. End If
43. End Sub
Run the example program by pressing the F5 function key. Click the Command Button
to display Form2. Form2 is modified so that it has a three-dimensional look.

100: Printing a Form Multiple Times on


One Page
May 29, 1995

Abstract
This article explains how you can print a Visual Basic® form several times on a single
piece of paper.

Printing Forms on the Printer Device


You can use the Windows® application programming interface (API) StretchBlt
function to copy a form to another form multiple times. For instance, the example
program below uses the StretchBlt function to copy a form to a new form. The original
form is copied four times. The destination form then contains a copy of the original form
in its upper left, lower left, upper right, and lower right corners.

The technique presented in this article is useful for duplicating a form several times. For
example, if you designed a form to keep track of telephone messages, you could print
four messages per printed page, instead of using one piece of paper per telephone
message.
The StretchBlt function can be used to copy an image from one device context to
another. To use this function in your Visual Basic® application, include the following

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Declare statement in the General Declarations section of your form (note that it must be
typed as a single line of code):
Private Declare Function StretchBlt Lib "GDI" (ByVal hDC%, ByVal X%, ByVal y%,
ByVal nWidth%, ByVal nHeight%, ByVal hSrcDC%, ByVal XSrc%, ByVal YSrc%, ByVal
nSrcWidth%, ByVal nSrcHeight%, ByVal dwRop&) As Integer
The StretchBlt function requires the following arguments:
hDC An integer value containing the destination device context.
X,Y Integer values defining the rectangle's upper left corner for the destination
device context.
nWidth The width of the image.
nHeight The height of the image.
hSrcDC An integer value containing the source device context.
Xsrc, YSrc Integer values defining the rectangle's upper left corner for the source device
context.
nSrcWidth The width of the image.
nSrcHeight The height of the image.
dwRop The raster operation that is to be used.

In the example program below, the StretchBlt function is called four times to copy the
original Form1 to the destination Form2. Each time the copy operation is performed, the
destination is offset to the next quarter section of Form2.

Example Program
Before running this program, make sure your printer is online and ready to accept data.
Press the F5 function key to run the example program, which will print Form2 on the
paper four times.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

3. Add the following code to the Click event for Command1 (note that the X = lines
must each be typed as a single line of code):
4. Private Sub Command1_Click()
5. Dim W As Integer
6. Dim H As Integer
7. Dim X As Integer
8.
9. W = Form2.ScaleWidth / 2
10. H = Form2.ScaleHeight / 2
11. X = SetStretchBltMode(Form2.hDC, 3)
12. X = StretchBlt(Form2.hDC, 0, 0, W, H, Form1.hDC, 0, 0, Form1.ScaleWidth,
13. Form1.ScaleHeight, SRCCOPY)
14. X = StretchBlt(Form2.hDC, W, 0, W, H, Form1.hDC, 0, 0, Form1.ScaleWidth,

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


15. Form1.ScaleHeight, SRCCOPY)
16. X = StretchBlt(Form2.hDC, W, H, W, H, Form1.hDC, 0, 0, Form1.ScaleWidth,
17. Form1.ScaleHeight, SRCCOPY)
18. X = StretchBlt(Form2.hDC, 0, H, W, H, Form1.hDC, 0, 0, Form1.ScaleWidth,
19. Form1.ScaleHeight, SRCCOPY)
20. Form2.Refresh
21. Form2.PrintForm
22. End Sub
23. From Visual Basic's menu, select Insert. Select Form to create a new form. Form2
is created by default. Add a Text Box control to Form2 or otherwise put some
type of material on the form so you can see it printed on the paper.

24. Add the following code to the General Declarations section of Form1 (note that
each Declare statement should be typed as a single line of code):
25. Private Declare Function SetStretchBltMode Lib "GDI" (ByVal hDC%, ByVal
26. nStretchMode%) As Integer
27. Private Declare Function StretchBlt Lib "GDI" (ByVal hDC%, ByVal X%, ByVal y%,
28. ByVal nWidth%, ByVal nHeight%, ByVal hSrcDC%, ByVal XSrc%, ByVal YSrc%, ByVal
29. nSrcWidth%, ByVal nSrcHeight%, ByVal dwRop&) As Integer
30. Const SRCCOPY = &HCC0020

101: Using the Built-In Windows Icons


May 29, 1995

Abstract
There are several icons built into the Windows® operating system that are used by
Windows when displaying message boxes. This article explains how to use the built-in
icons in your own Visual Basic® applications.

Displaying the Built-In Windows Icons


In a Visual Basic® application, you can use the icons built into the Windows® operating
system. These include the hand, exclamation point, question mark, asterisk, and
application icons. The CONSTANT.TXT file defines these as follows:
Const IDI_APPLICATION = 32512&
Const IDI_HAND = 32513&
Const IDI_QUESTION = 32514&
Const IDI_EXCLAMATION = 32515&
Const IDI_ASTERISK = 32516&
Before you can use one of these icons in your Visual Basic application, you must load the
icon using the Windows application programming interface (API) LoadIcon function.
This function loads a specified icon into the device context. In the example program
below, we want to display the icon in a Picture Box control. Therefore, we must first
retrieve a device context for the Picture Box control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To retrieve a device context for a window, you use the Windows application
programming interface (API) GetWindowDC function, as follows:
Private Declare Function GetWindowDC Lib "User" (ByVal hWnd As Integer)
As Integer
(Note that this statement must be typed as a single line of code.)
This function requires only one argument—an integer value containing the window's
handle. The device context's handle is returned as an integer value or, if the function was
not successful, a value of zero is returned.
When you have finished using the device context you must remember to release the
device context. This can be done by calling the ReleaseDC function, passing it the handle
of the device context that you want to release.
After retrieving the device context, you can call the LoadIcon function to display the
specified icon in the device context. Because this is a built-in Windows icon, we set the
first argument to a value of zero. The second argument to the LoadIcon function is a
constant value telling the function which icon you want to load.

Example Program
This program shows how to use the built-in icons used by the Windows operating system.
This program displays the exclamation icon in the Picture Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Private Declare Function DrawIcon Lib "User" (ByVal hDC As Integer, ByVal X
4. As Integer, ByVal y As Integer, ByVal hIcon As Integer) As Integer
5. Private Declare Function LoadIcon Lib "User" (ByVal hInstance As Integer, ByVal
6. lpIconName As Any) As Integer
7. Private Declare Function GetWindowDC Lib "User" (ByVal hWnd As Integer) As
8. Integer
9. Private Declare Function ReleaseDC Lib "User" (ByVal hWnd As Integer, ByVal hDC
10. As Integer) As Integer
11. Const IDI_EXCLAMATION = 32515&
12. Add a Picture Box control to Form1. Picture1 is created by default. Set its
AutoRedraw property to True.

13. Add a Command Button control to Form1. Command1 is created by default.

14. Add the following code to the Click event for Command1:
15. Private Sub Command1_Click()
16. Dim hDCCur As Long
17. Dim hIcon As Integer
18. Dim X As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


19.
20. hDCCur = GetWindowDC(Picture1.hWnd)
21. hIcon = LoadIcon(0, IDI_EXCLAMATION)
22. X = DrawIcon(hDCCur, 0, 0, hIcon)
23. Call ReleaseDC(Picture1.hWnd, hDCCur)
24. End Sub

102: Modifying a Child Window's


Caption
June 5, 1995

Abstract
This article explains how you can modify the caption displayed in a multiple-document
interface (MDI) child window.

Changing the Caption of Child Windows or Forms


The Microsoft® Word for Windows® application allows you to have several documents
loaded into memory at one time. These text files are displayed in multiple-document
interface (MDI) child windows.
An MDI child window automatically inherits the parent window's caption. This caption is
inserted before the child window's own caption. As an example, if the parent window's
caption is MDIForm1 and the child window's caption is Form1, the child window's
caption, at run time, will be set to MDIForm1 - [Form1].
In your Visual Basic® application, you can set the child form's caption to a NULL string
so that only the caption of the parent window is displayed. However, the dash and bracket
characters must also be deleted. Because the caption is displayed in the non-client area of
a window, you must use a subclassing control to process the paint event yourself. The
Message Blaster custom control can be used to modify a child window's caption.
However, another solution can do the same thing without using a subclassing control.
Just use Visual Basic's string functions (Mid and InStr) to remove the unwanted text
from the child window's caption. This code, as shown below in the example program, is
placed in the child form's Resize event. Each time Windows needs to repaint the window,
the caption will be modified.

Example Program
This program shows how to modify a child window's caption text. Start this program by
pressing F5. The original caption is "MDIForm1 - [Form1]".
Start this program again after removing the Exit Sub statement from the Resize event (at
the beginning of the code listing). The caption for the MDIForm1 window now reads,
"MDIForm1 - Form1". The dash and bracket characters have been removed from the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


string. In addition, if you set Form1's caption to a NULL string, MDIForm1 will simply
display its own caption—even when Form1 is minimized.
1. Create a new project in Visual Basic. Form1 is created by default.

2. From the Visual Basic Insert menu, select MDI Form. MDIForm1 is created by
default.

3. Set Form1's MDIChild property to True. Modify the size of this form so that it is
smaller than the MDIForm1 form.

4. Add the following code to the Form_Load event for Form1:


5. Private Sub Form_Load()
6. Form1.Tag = Form1.Caption
7. MDIForm1.Tag = MDIForm1.Caption
8. Form1.WindowState = 2 'Maximize this form
9.
10. End Sub
11. Add the following code to the Resize event for Form1:
12. Private Sub Form_Resize()
13. ' Exit Sub
14.
15. Dim Cap As String
16. Dim Postn As Integer
17.
18. If (Form1.WindowState = 2) Then
19. Cap = MDIForm1.Caption
20.
21. MDIForm1.Caption = ""
22. Postn = InStr(Cap, "[")
23. If (Postn) Then
24. Mid(Cap, Postn, 1) = " "
25. End If
26.
27. Postn = InStr(Cap, "]")
28. If (Postn) Then
29. Mid(Cap, Postn, 1) = " "
30. End If
31.
32. Postn = InStr(Cap, "-")
33. If (Postn) Then
34. Mid(Cap, Postn, 1) = " "
35. End If
36.
37. Form1.Caption = ""
38. MDIForm1.Caption = Cap
39.
40. End If
41.
42. If (Form1.WindowState = 0) Then
43. Form1.Caption = Form1.Tag

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


44. MDIForm1.Caption = MDIForm1.Tag
45. End If
46.
47. End Sub

103: Preventing the CTRL+TAB


and CTRL+F6 Key Combinations from
Activating Child Windows
June 5, 1995

Abstract
A multiple-document interface (MDI) window can contain multiple child windows. A
user can switch between child windows by pressing the CTRL+TAB or CTRL+F6 key
combinations. This article explains how you can prevent the user from using these keys
to switch to another child window.

Using Message Blaster to Disable Keystrokes


Many Microsoft® Windows®-based applications use multiple-document interface (MDI)
windows to display several child windows to the user. For instance, Word for Windows
lets you work with several different documents at the same time. Each text file is
displayed in its own child window.

When users want to switch from one child window to another, they press either
CTRL+TAB or CTRL+F6. The next window in the list is then brought to the top of the
window list (that is, it becomes the currently active window).

In a Visual Basic® application, you can disable this window-switching by intercepting


the messages sent to Windows. The WM_SYSCOMMAND message triggers the event
that switches between child windows. The Message Blaster custom control can be used
to process this WM_SYSCOMMAND message in your Visual Basic program. You can
retrieve Message Blaster from the Microsoft Development Library. For information on
the Message Blaster custom control, see "Additional References" at the end of this
article.

The general idea, however, is to capture the WM_SYSCOMMAND that is sent to


Windows when the CTRL+F6 or CTRL+TAB combination is pressed. To do this, you
must register the Message Blaster control to the target control—in this case, the first
child window (Form1). To prevent a user from activating other child windows, execute
the following statement:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


MsgBlaster1=MsgPassage(0)
After you have disabled a child window in this manner, the user will not be able to
minimize or maximize the target window. In addition, the resize and move options are
also disabled.

Example Program
This program shows how to disable the CTRL+F6 and CTRL+TAB key combinations so
that the user cannot move to the next MDI child window. Run the example program by
pressing F5. The program displays two child windows (Form1 and Form2) within an
MDI document window. Normally, you can press the CTRL+TAB or CTRL+F6 keys to
switch between the child windows. The Message Blaster control has been used to disable
these two key combinations if you try to use them in Form1. Click Form2 to bring that
child window to the top. Unlike Form 1, the Form 2 child window will process the
CTRL+TAB and CTRL+F6 key combinations.
1. Create a new project in Visual Basic. Form1 is created by default.

2. From the Visual Basic Insert menu, select MDI Form. MDIForm1 is created by
default.

3. Set Form1's MDIChild property to True.

4. From the Visual Basic Insert menu, select Form. Form2 is created by default.

5. Set the Form 2 MDIChild property to True.

6. From the Visual Basic Tools menu, select Custom Controls. Add a Message
Blaster control to Form1. MsgBlaster1 is created by default.

7. Add the following code to the General Declarations section of Form1:


8. Option Explicit
9. Const WM_SYSCOMMAND = &H112
10. Add the following code to the Form_Load event for Form1:
11. Private Sub Form_Load()
12. MsgBlaster1.hWndTarget = Form1.hWnd
13. MsgBlaster1.MsgList(0) = WM_SYSCOMMAND
14. End Sub
15. Add the following code to the MsgBlaster1_Message event for Form1 (note that
the first two lines below must be typed as a single line of code):
16. Private Sub MsgBlaster1_Message(MsgVal As Integer, wParam As Integer,
17. lParam As Long, ReturnVal As Long)
18. MsgBlaster1.MsgPassage(0) = 0
19. End Sub
20. Add the following code to the Form_Load event for MDIForm1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


21. Private Sub MDIForm_Load()
22. Form1.Show
23. Form2.Show
24. End Sub

104: Creating a Form with a Thin Title


Bar
June 5, 1995

Abstract
Many Microsoft® Windows®-based applications include a Toolbox control. A toolbox is
a group of icons that the user can click to perform various operations within a running
application. These toolbox windows usually display a very small, thin title bar instead of
the normal-sized title bar. This article explains how you can create forms with thin title
bars in your Visual Basic® applications.

Using SendMessage and GetCursorPos to Create Title


Bars
You can design a form that contains a thin title bar. This is most often used in Toolbox
controls from which the user can click on an icon to perform a program operation.

In the example program below, we use a Label control, sized to fit at the top of our form.
This control, which will become the thin title bar, responds to both the Click and
MouseDown events. These two events allow the user to click on the Label control (that
is, the thin title bar) and drag the entire form to a new location on the screen. This gives
our Microsoft® Visual Basic® application the same functionality as a toolbox window.

To enable our user to drag the form to a new location on the screen, we need to determine
the cursor's current X and Y coordinates on the screen. You can use the Microsoft®
Windows® application programming interface (API) GetCursorPos function to retrieve
the cursor's current location.
To call the GetCursorPos function, you must first add its Declare statement to the
General Declarations section of your Visual Basic application. Following is the
declaration for the GetCursorPos function:
Private Declare Sub GetCursorPos Lib "User" (lpPoint As POINTAPI)
The GetCursorPos function requires only one argument—a POINTAPI structure. This
structure will hold the cursor's current position, which is reported in screen coordinate
values.

The cursor's horizontal position is stored in the X variable; the cursor's vertical position is
stored in the Y variable within the POINTAPI structure:
Type POINTAPI

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


X As Integer
Y As Integer
End Type
After the cursor's position has been retrieved, we issue the LSet statement to convert the
X and Y values to values that can be understood by the SendMessage function. In other
words, LSet converts the X and Y integers to a single long value.
Next, we issue two SendMessage commands to Windows. The first SendMessage
statement tells Windows that, because a MouseDown event has just occurred, it needs an
equivalent MouseUp event. The second SendMessage statement tells Windows that the
user has clicked the title bar. Windows then processes our thin title bar's Click and
MouseDown events as it would for a normal window.

Example Program
This program shows how to create a form with a small title bar.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Set the following properties for Form1:


ClipControls 0 'False
ControlBox 0 'False
MaxButton 0 'False
MinButton 0 'False

3. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statements must be typed as single lines
of text):
4. Private Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal
5. wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Any) As Long
6. Private Declare Sub GetCursorPos Lib "User" (lpPoint As POINTAPI)
7. Const WM_LBUTTONUP = &H202
8. Const WM_SYSCOMMAND = &H112
9. Const MOUSE_MOVE = &HF012
10. Add a Label control to Form1. Label1 is created by default. Set its Caption
property to "Thin Title Bar".
Note: For this example program, change the size of Form1 so that it resembles the
size and shape of a Toolbox window. Next, position the Label control at the top
of the form and adjust its size so that it isthe same size as a thin title bar.
11. Add the following code to the Click event for Label1:
12. Private Sub Label1_Click()
13. Dim mpos As POINTAPI
14. Dim P As ConvertPOINTAPI
15. Dim Ret As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


16.
17. Call GetCursorPos(mpos)
18. LSet P = mpos
19. Ret = SendMessage(Me.hWnd, WM_LBUTTONUP, 0, P.XY)
20. Ret = SendMessage(Me.hWnd, WM_SYSCOMMAND, MOUSE_MOVE, P.XY)
21.
22. End Sub
23. Add the following code to the MouseDown event for Label1 (note that the first
two lines must be typed as a single line of code):
24. Private Sub Label1_MouseDown(Button As Integer, Shift As Integer,
25. X As Single, Y As Single)
26. Dim mpos As POINTAPI
27. Dim P As ConvertPOINTAPI
28. Dim Ret As Integer
29.
30. Call GetCursorPos(mpos)
31. LSet P = mpos
32. Ret = SendMessage(Me.hWnd, WM_LBUTTONUP, 0, P.XY)
33. Ret = SendMessage(Me.hWnd, WM_SYSCOMMAND, MOUSE_MOVE, P.XY)
34. End Sub
35. From the Insert menu, select Module. Module1.Bas is created by default.

36. Add the following type declarations to Module1.Bas:


37. Type POINTAPI
38. X As Integer
39. Y As Integer
40. End Type
41. Type ConvertPOINTAPI
42. XY As Long
43. End Type
Run the example program by pressing F5. Form1 should be displayed on the screen with
the thin title bar appearing at the top of the form. You can drag the form by clicking on
the title bar, just as you would do with any other window that has a title bar.

105: Removing a Form's Title Bar


June 5, 1995

Abstract
This article explains how you can remove the title bar from a window or form from
within a Microsoft® Visual Basic® application.

Modifying the Style Attributes of a Window


You can use two Microsoft® Windows® application programming interface (API)
functions—GetWindowLong and SetWindowLong—to modify the appearance of a
window when your Visual Basic® application is running.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


These functions allow you to programmatically change one or more style bits associated
with a specific window. For example, you can remove a window's title bar by changing
the following style bits at run time:
WS_SYSMENU The window has a control menu on the left side of its title bar
WS_MINIMIZEBOX The window has a minimize box on the right side of its title bar
WS_MAXMIZEBOX The window has a maximize box on the right side of its title bar
WS_DLGFRAME The window has a double border, but does not have a title bar

First, we must first call GetWindowLong. This function reports the window style
associated with a window, among other pieces of information.
To use GetWindowLong, you must include its Declare statement in your program as
follows (note that the Declare statement must be typed as a single line of text):
Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer,
ByVal nIndex As Integer) As Long
The GetWindowLong function requires two arguments. The first argument is the
window's handle;the second argument specifies the type of information you want to
retrieve. In this case, we want to retrieve the window's style settings. The current window
style is returned as a long value after Get WindowLong is called.
After we have retrieved the current window style for the window, we need to save the
original style value that was just retrieved so that we can later restore the window's title
bar, if desired. This is done by testing for the individual title bar attributes and saving
each in turn to a new OriginalStyle variable. Next, we need to remove the attributes
associated with the window's title bar. These attributes are the Minimize and Maximize
buttons, the control menu, and the dialog box frame. We can remove them from the
original window style value that was just retrieved by using the bitwise AND NOT
function. Finally, we can call SetWindowLong to send this information to Windows,
which causes the title bar to be removed from the window.

Example Program
Thisprogram shows how you can remove and later restore a window's title bar.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Remove Title Bar".

3. Add the following code to the Click event for Command1:


4. Private Sub Command1_Click()
5. RemoveTitleBar Form2
6. Form2.Show
7. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Restore Title Bar".

9. Add the following code to the Click event for Command2:


10. Private Sub Command2_Click()
11. RestoreTitleBar Form2
12. Form2.Show
13. End Sub
14. From the Insert menu, select Form. Form2 is created by default. Adjust the size of
this form so that it is approximately half the size of Form1. Set its AutoRedraw
property to True and its Caption property to an empty (NULL) string.

15. Add a Command Button control to Form2. Command1 is created by default. Set
its Caption property to "OK".

16. Add the following code to the Click event for Command1:
17. Private Sub Command1_Click()
18. Form1.Show
19. Unload Form2
20. End Sub
21. From the Insert menu, select Module. Module1.Bas is created by default.

22. Add the following Constant and Declare statements to Module1.Bas (note that
each Declare statement must be typed as a single line of text):
23. Option Explicit
24. Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer,
25. ByVal nIndex As Integer) As Long
26. Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer,
27. ByVal nIndex As Integer, ByVal dwNewLong As Long) As Long
28. Const GWL_STYLE = (-16)
29. Const WS_DLGFRAME = &H400000
30. Const WS_SYSMENU = &H80000
31. Const WS_MINIMIZEBOX = &H20000
32. Const WS_MAXIMIZEBOX = &H10000
33. Create a new function called RemoveTitleBar to Module1.Bas. Add the
following code to this function:
34. Sub RemoveTitleBar(frm As Form)
35. Static OriginalStyle As Long
36. Dim CurrentStyle As Long
37. Dim X As Long
38. OriginalStyle = 0
39. CurrentStyle = GetWindowLong(frm.hWnd, GWL_STYLE)
40.
41. OriginalStyle = OriginalStyle Or (CurrentStyle And WS_DLGFRAME)
42. OriginalStyle = OriginalStyle Or (CurrentStyle And WS_SYSMENU)
43. OriginalStyle = OriginalStyle Or (CurrentStyle And WS_MINIMIZEBOX)
44. OriginalStyle = OriginalStyle Or (CurrentStyle And WS_MAXIMIZEBOX)
45.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


46. CurrentStyle = CurrentStyle And Not WS_DLGFRAME
47. CurrentStyle = CurrentStyle And Not WS_SYSMENU
48. CurrentStyle = CurrentStyle And Not WS_MINIMIZEBOX
49. CurrentStyle = CurrentStyle And Not WS_MAXIMIZEBOX
50.
51. X = SetWindowLong(frm.hWnd, GWL_STYLE, CurrentStyle)
52. frm.Refresh
53. End Sub
54. Create a new function called RestoreTitleBar to Module1.Bas. Add the
following code to this function:
55. Sub RestoreTitleBar(frm As Form)
56. Static OriginalStyle As Long
57. Dim CurrentStyle As Long
58. Dim X As Long
59.
60. CurrentStyle = GetWindowLong(frm.hWnd, GWL_STYLE)
61. CurrentStyle = CurrentStyle Or OriginalStyle
62. X = SetWindowLong(frm.hWnd, GWL_STYLE, CurrentStyle)
63. frm.Refresh
64. End Sub
Run this example program by pressing F5. Click the "Remove Title Bar" command
button. Form2 is displayed. Notice that the title bar has been removed from the form.
Click the OK command button, then click the Restore Title Bar command button. Form2
is displayed again, this time with its title bar intact.

106: Centering Text Vertically in a Text


Box Control
June 5, 1995

Abstract
The Microsoft® Visual Basic® Text Box control lets your user enter text that can later
be used by your application. This article explains how you can center the text that the
user types vertically within the Text Box control.

Vertically Centering Text in Visual Basic


In your application, you may need to display the text typed by the user, centered
vertically within the Text Box control. The only way to accomplish this task is to place
the Text Box control within a larger Picture Box control. The Text Box control allows
you to type text that can later be used by your Microsoft® Visual Basic® application. As
the user types the text, the text wraps to the next line (if the MultiLine property is set to
True).
The example program below centers the text in the text box by first setting the size of the
Text Box control to the same as the size of the Picture Box control. Whenever a Change

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


event is detected by the Text Box control, the text is redrawn in the control so that it
appears vertically centered.

Example Program
This program shows how to center text vertically within a Text Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Picture Box control to Form1. Picture1 is created by default. Set its
AutoRedraw property to True.

3. Add a Text Box control to Form1 over top of the Picture Box control. Text1 is
created by default. Set its MultiLine property to True.

4. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of text):
5. Private Declare Function SendMessage Lib "User" (ByVal hWnd As Integer,
6. ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
7. Const WM_USER = &H400
8. Const EM_GETLINECOUNT = WM_USER + 10
9. Dim NumLines As Integer
10. Add the following code to the Form_Load event for Form1:
11. Private Sub Form_Load()
12. Dim HT As Integer
13. Text1.Left = 0
14. Text1.Width = Picture1.Width
15. Text1.Height = Picture1.TextHeight("A")
16. Text1.Top = (Picture1.Height = Text1.Height) / 2
17. Text1.Visible = True
18. NumLines = 1
19. End Sub
20. Add the following code to the Change event for Text1:
21. Private Sub Text1_Change()
22. Dim Ret As Long
23. Dim HT As Integer
24. Ret = SendMessage(Text1.hWnd, EM_GETLINECOUNT, 0, ByVal 0&)
25. If Ret <> NumLines Then
26. HT = Picture1.TextHeight("A")
27. Text1.Height = HT * Ret
28. Text1.Top = (Picture1.Height - Text1.Height) / 2
29. NumLines = Ret
30. SendKeys "{PGUP}", True
31. Text1.SelStart = Len(Text1)
32. End If
33.
34. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


107: Detecting Double-Click
Events in Combo Box Controls
June 5, 1995

Abstract
When developing an application in Microsoft® Visual Basic®, you may want to let the
user double-click the edit portion of a Combo Box control. This article explains how to
process this double-click event by using the Message Blaster custom control.

Using Message Blaster to Detect Double-Clicks on


Combo Boxes
In a Microsoft® Visual Basic® application, you can use a Combo Box control to allow
your user to easily select an item. Unfortunately, the Combo Box control only responds
to single-click events, not to double-click events in the box's edit portion.
You can, however, use a subclassing control such as Message Blaster to detect when a
user has double-clicked the combo box. Before you can do this, you need to use two
Microsoft Windows® application programming interface (API) functions—GetWindow
and GetClassName.
The GetWindow function retrieves the handle of a window that has a specific
relationship to the source window. In other words, we need to determine the Combo
Box's handle. The Combo Box window is actually a sibling window of our Visual Basic
application's main form. Next, we need to call the GetClassName function to make sure
that the edit portion of the Combo Box is the window we are dealing with.
If you are using a combo box with its Style property set to 0 - Drop Down Combo,
detecting a double-click message is relatively straightforward. Just retrieve the handle of
the Combo Box's edit window, and then tell Message Blaster to intercept the Windows
WM_LBUTTONDBLCLK message.

On the other hand, if you are using a Combo Box with its Style property set to 1 - Simple
Combo, the procedure is a little different. Because the edit portion of the Combo Box is
already displayed, you need to call GetWindow to retrieve the handle of the edit
window.
After you have retrieved the edit window's handle, you must call the GetClassName
function. This function is called so that we can be certain we are processing a double-
click message for only the edit portion of the Combo Box.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
This program shows how your Visual Basic application can respond to a double-click
event when such an event is detected in the edit portion of a Combo Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. From the Insert menu, select Custom Control. Add the Message Blaster custom
control to this project.

3. Add a Message Blaster control to Form1. MsgBlast1 is created by default.

4. Add the following code to the Message event for MsgBlaster1 (note that the first
two lines of code must be typed as a single line of text):
5. Private Sub MsgBlaster1_Message(MsgVal As Integer, wParam As Integer,
6. lParam As Long, ReturnVal As Long)
7. MsgBox "Combo1 box double-clicked"
8. End Sub
9. Add a Combo Box control to Form1. Combo1 is created by default. Set its Style
property to 1-Simple Combo.

10. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of text):
11. Private Declare Function GetWindow Lib "User" (ByVal hWnd As Integer,
12. ByVal wCmd As Integer) As Integer
13. Private Declare Function GetClassName Lib "User" (ByVal hWnd As Integer,
14. ByVal lpClassName As String, ByVal nMaxCount As Integer) As Integer
15. Const WM_LBUTTONDBLCLK = &H203
16. Const GW_CHILD = 5
17. Const GW_HWNDNEXT = 2
18. Add the following code to the Form_Load event for Form1:
19. Private Sub Form_Load()
20. Dim hWndList As Integer
21. Dim hWndEdit As Integer
22. Dim Buf As String * 10
23. Dim X As Integer
24.
25. Combo1.AddItem "Item #1"
26. Combo1.AddItem "Item #2"
27. Combo1.AddItem "Item #3"
28.
29. hWndList = GetWindow(Combo1.hWnd, GW_CHILD)
30. Select Case Combo1.Style
31.
32. Case 0
33. MsgBlaster1.hWndTarget = hWndList
34. MsgBlaster1.MsgList(0) = WM_LBUTTONDBLCLK

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


35. Case 1
36. hWndEdit = GetWindow(hWndList, GW_HWNDNEXT)
37. Buf = ""
38. X = GetClassName(hWndEdit, Buf, Len(Buf))
39.
40. If StrComp(Trim(Buf), "Edit" & Chr$(0)) = 0 Then
41. MsgBlaster1.hWndTarget = hWndEdit
42. MsgBlaster1.MsgList(0) = WM_LBUTTONDBLCLK
43. End If
44. End Select
45.
46. End Sub
Run the example program by pressing F5. When you double-click the edit portion of the
Combo Box control, a message box will confirm this action. You can also change the
Style property of the Combo Box to 0 - Drop Down Combo to get the same effect.

108: Flashing Controls to Get the User's


Attention
June 12, 1995

Abstract
When developing an application in Microsoft® Visual Basic®, you can use the
BackColor property to change the background color of a control. This article explains
how you can temporarily flash a control's BackColor property to draw the user's
attention to a specific control.

Changing a Control's BackColor Property


When designing a Microsoft® Visual Basic® application , you place controls such as
List Boxes and Text Boxes on a form. At run time, you can move the focus to one of
these objects by using Visual Basic's SetFocus method. Users can then see that that
particular control needs to be addressed in some way. For example, if a Text Box
receives the focus, users know they must type some text into that control.
However, users may not actually notice that the focus has been set to a specific control
because the "rubberband" (highlighting) around the inside of the control is not that
obvious. To alert the user, you could change the background color of the control from
white to, say, red, to draw the user's attention to that control. When the control loses the
focus, you could reset the control's background color to white. This procedure, however,
means that the control would be a different color as long as that control retained the
focus. In some situations, this would not be appropriate.
A far better solution would be to change the control's background color for just a few
seconds. The example program below "flashes" a control by quickly changing the
control's background color three times in succession. The Timer function is used to cause

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


a short time delay in the program. Each time a 2-second interval elapses, the control's
color is changed from white to red, then back to white. The For-Next loop dictates how
many times the control is flashed. In this case, a value of 3 was used to flash the color
three times. This creates a very visual clue to draw the user's attention to that specific
control.

Example Program
This program shows how to highlight the control that has the focus. Run the example
program by pressing F5. Then click the Flash Command Button. Note that the
background color of the List Box control is changed to red and flashed three times.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Private Sub Form_Load()
4. List1.AddItem "Item #1"
5. List1.AddItem "Item #2"
6. List1.AddItem "Item #3"
7. End Sub
8. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Flash".

9. Add the following code to the Click event for Command1:


10. Private Sub Command1_Click()
11. FlashControl List1
12. End Sub
13. Add a List Box control to Form1. List1 is created by default.

14. Create a new function called FlashControl. Add the following code to this
function:
15. Sub FlashControl(C As Control)
16. Dim OldColor As Double
17. Dim Delay As Double
18. Dim X As Integer
19.
20. OldColor = C.BackColor
21. For X = 1 To 3
22. C.BackColor = QBColor(12)
23. Delay = Timer
24. While Timer - Delay < 0.2
25. DoEvents
26. Wend
27. C.BackColor = OldColor
28. Delay = Timer
29. While Timer - Delay < 0.2
30. DoEvents
31. Wend
32. Next X

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


33. C.SetFocus
34. End Sub

109: Modifying an MDI Form's


Border Style
June 12, 1995

Abstract
This article explains how you can create a multiple-document interface (MDI) form that
has a fixed border in a Microsoft® Visual Basic® application.

Retrieving and Setting a Form's BorderStyle


Every form you create when designing a Microsoft® Visual Basic® application can have
one of four border styles. Just set the form's BorderStyle property to one of the following
styles.
Property Style
0 None
1 Fixed Single
2 Sizeable
3 Fixed Double

An MDI child form, however, does not have a BorderStyle property. But by using the
Microsoft Windows® GetWindowLong and SetWindowLong application programming
interface (API) functions , you can change an MDI form's border style to a fixed border
style.
The GetWindowLong function retrieves information about the specified window's style
attributes and the SetWindowLong function modifies the specified window's style
attributes.
GetWindowLong requires only two arguments. The first argument is the target window's
handle. The second argument specifies the type of information you want to retrieve,
which is the style settings for the window.
After retrieving the window's current style settings, use the bitwise And Not function to
remove the WS_THICKFRAME attribute from the style settings value. Next, issue a call
to the SetWindowLong function to set the new style settings for the specified window.
This creates an MDI form that has a fixed border style.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
This program shows how to create an MDI form that has a fixed border. Run the example
program by pressing F5. The MDI form will be displayed with a fixed border.
1. Create a new project in Visual Basic. Form1 is created by default.

2. From the Insert menu, select MDI Form to create an MDI form. MDIForm1 is
created by default.

3. Set Form1's MDIChild property to True. Modify the size of this form so that it is
smaller than the MDIForm1 form.

4. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of text):
5. Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
6. nIndex As Integer) As Long
7. Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
8. nIndex As Integer, ByVal dwNewLong As Long) As Long
9. Const GWL_STYLE = (-16)
10. Const WS_THICKFRAME = &H40000
11. Add the following code to the Load event for MDIForm1 (note that the NewStyle
line must be typed as a single line of code):
12. Private Sub MDIForm_Load()
13. Dim CurStyle As Long
14. Dim NewStyle As Long
15. CurStyle = GetWindowLong(MDIForm1.hWnd, GWL_STYLE)
16. NewStyle = SetWindowLong(MDIForm1.hWnd, GWL_STYLE, CurStyle And
17. Not (WS_THICKFRAME))
18. End Sub

110: Sending a Click Event to a


Command Button Control
June 12, 1995

Abstract
In a Microsoft® Visual Basic® application, you can simulate a Click event to a
Command Button control. This article explains how to send a BN_CLICKED
notification message to a control.

Executing a BN_CLICKED Message

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


A user who wants to carry out a command in your Microsoft® Visual Basic®
application usually clicks a Command Button control. The code in the Command
Button's Click event is then executed.
There may be times, however, when you will want to initiate a Click event from within
your Visual Basic program. You can use the Microsoft Windows® application
programming interface (API) PostMessage function to send a BN_CLICKED
notification message to the parent of the Command Button control. This will call the
button's Click event.

As you can see from the example program below, the GetDlgCtrlID function retrieves
the Command Button's handle. Next, a call is made to the GetParent function, which
retrieves the handle of the window that the Command Button resides on. (In other
words, we must retrieve the parent window's handle.)
The last step is to execute a PostMessage function. PostMessage sends a BN_CLICKED
notification message to the parent window, which then processes the Click event for the
Command Button.

When you run the example program below, the second Command Button's Click event
is executed. However, the second Command Button does not receive the focus—only its
code is executed.

Example Program
This program shows how to send a Command Button click to the Windows operating
system.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of text):
3. Const BN_CLICKED = 0
4. Const WM_COMMAND = &H111
5. Private Declare Function GetDlgCtrlID Lib "User" (ByVal hWnd As Integer) As
6. Integer
7. Private Declare Function GetParent Lib "User" (ByVal hWnd As Integer) As Integer
8. Private Declare Function PostMessage Lib "User" (ByVal hWnd As Integer, ByVal
9. wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Long) As Integer
10. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Send".

11. Add the following code to the Click event for Command1:
12. Private Sub Command1_Click()
13. ClickButton Command2.hWnd
14. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


15. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Receive".

16. Add the following code to the Click event for Command2:
17. Private Sub Command2_Click()
18. MsgBox "Command2 was CLICKED!"
19. End Sub
20. Create a new function called ClickButton. Add the following code to this
function:
21. Sub ClickButton(ByVal hWnd As Integer)
22. Dim Button As Integer
23. Dim ParentHwnd As Integer
24. Dim X As Integer
25.
26. Button = GetDlgCtrlID(hWnd)
27. ParentHwnd = GetParent(hWnd)
28. X = PostMessage(ParentHwnd, WM_COMMAND, Button, BN_CLICKED * &H10000 +
hWnd)
29. End Sub
Run the example program by pressing F5. Click the Send Command Button. The Click
event for the second Command Button control is immediately executed (the message
box is displayed).

111: Using Different Fonts in List Boxes


That Have Tab Stops
June 12, 1995

Abstract
The Microsoft® Visual Basic® List Box control lets you add individual items to create a
list of data. This article explains how to add tab stops to create multicolumn items, no
matter what type of font or font size is used.

Using the GetDialogBaseUnits Function


When adding items to a List Box control, you can create columns of data by inserting a
tab stop within the control. However, the data will only be correctly aligned in the
columns if you use the default font and font size used by the List Box control.
As the example program below shows, you can use the Microsoft® Windows®
application programming interface (API) GetDialogBaseUnits function to determine the
width and height of the average character in the selected font. The width and height of the
character are returned in dialog base units. From these values, you can calculate the
average width of the characters in the selected font.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


After you know the width of the character set, you can add the tab stops to the List Box
control. Then, using whatever font and font size you want, you can add new items to the
control. The columns of data will appear in separate rows.

Example Program
This program shows how to add tab stops to a List Box control. No matter what font or
font size is used when adding items to the control, the columns will line up correctly.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of text):
3. Private Declare Function Getfocus Lib "User" () As Integer
4. Private Declare Function GetDialogBaseUnits Lib "User" () As Long
5. Private Declare Sub SendMessage Lib "User" (ByVal hWnd As
6. Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any)
7. Private Declare Sub APISetFocus Lib "User" Alias "SetFocus"
8. (ByVal hWnd As Integer)
9. Const WM_USER = &H400
10. Const LB_SETTABSTOPS = WM_USER + 19
11. Add the following code to the Form_Load event for Form1:
12. Private Sub Form_Load()
13. Dim TB As String * 1
14. Dim OldHandle As Integer
15. Dim ListHandle As Integer
16. Dim DlgWidthUnits As Integer
17. Dim I As Integer
18. ReDim TabStop(2) As Integer
19.
20. TabStop(0) = 10
21. TabStop(1) = 30
22. TabStop(2) = 50
23.
24. TB = Chr$(9)
25. Show
26. OldHandle = Getfocus()
27. List1.SetFocus
28. ListHandle = Getfocus()
29. DlgWidthUnits = (GetDialogBaseUnits() Mod 65536) / 2
30.
31. For I = 0 To 2
32. TabStop(I) = TabStop(I) * DlgWidthUnits
33. Next I
34.
35. Call SendMessage(ListHandle, LB_SETTABSTOPS, 3, TabStop(0))
36. Call APISetFocus(OldHandle)
37. List1.AddItem "Item" + TB + "Quan." + TB + "Price"
38. List1.AddItem "Disks" + TB + "10" + TB + "$9.50"
39. List1.AddItem "Paper" + TB + "12" + TB + "$22.50"
40. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


41. Add a List Box control to Form1. List1 is created by default.

112: Preventing ListIndex Property from


Triggering a Click Event
July 1, 1995

Abstract
When you change the ListIndex property of a Combo Box control, the Click event is
automatically triggered. This article explains how to suppress this Click event.

Suppressing Click Events in Combo Box Controls


The Combo Box control in Microsoft® Visual Basic® lets you create a list of items that
the user can click to select. Within a Visual Basic program, you can select a specific item
in a Combo Box control by using its ListIndex property. However, each time the
ListIndex property is changed, a Click event is also triggered.

Each time an item is selected in a Combo Box control, a CB_SETCURSEL message is


sent to the control by the Windows® application programming interface (API). This
message selects or deselects the specific item in the Combo Box control.

To prevent a Click event from being triggered each time the ListIndex property is
changed, you need only send the CB_SETCURSEL message directly to the control. The
Click event is not generated, but the individual item is still selected or deselected.

Example Program
The example program below shows how to set the ListIndex property of a Combo Box
control without generating a Click event.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as one single line
of code):
3. Private Declare Function SendMessageByString Lib "User" Alias "SendMessage"
4. (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal
5. lParam As String) As Integer
6. Const WM_USER = &H400
7. Const CB_SETCURSEL = (WM_USER + 14)
8. Add the following code to the Form_Load event for Form1:
9. Private Sub Form_Load()
10. Combo1.AddItem "Item #1"
11. Combo1.AddItem "Item #2"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


12. Combo1.AddItem "Item #3"
13. End Sub
14. Add a Combo Box control to Form1. Combo1 is created by default.

15. Add the following code to the Click event for Combo1:
16. Private Sub Combo1_Click()
17. MsgBox "Combo1 was Clicked!"
18. End Sub
19. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Will Click".

20. Add the following code to the Click event for Command1:
21. Private Sub Command1_Click()
22. Combo1.ListIndex = 1
23. End Sub
24. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Will Not Click".

25. Add the following code to the Click event for Command2:
26. Private Sub Command2_Click()
27. SetComboListIndex Combo1, 1
28. End Sub
29. Create a new function called SetComboListIndex. Add the following code to this
function:
30. Sub SetComboListIndex(cboCombo As ComboBox, iIndex As Integer)
31. Dim R As Integer
32. R = SendMessageByString(cboCombo.hWnd, CB_SETCURSEL, iIndex, "")
33. End Sub
Run the example program by pressing the F5 function key. Click the "Will Click"
command button. A message box is displayed indicating that the Click event was
triggered when the ListIndex property was changed. Click the "Will Not Click"
command button. The item in the Combo Box is selected but a Click event is not
triggered.

113: Creating Transparent Forms


July 1, 1995

Abstract
When developing an application in Microsoft® Visual Basic®, you may need to design a
transparent form. This article explains how to create transparent forms.

Using Transparent Forms

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


A transparent form is a form that, when displayed, does not cover up the underlying
windows beneath it. The Microsoft® Windows® application programming interface
(API) function SetWindowLong can be used to change the style settings of a form or
window. You can create a transparent window by setting the WS_EX_TRANSPARENT
style bit.

To use the SetWindowLong function within your Microsoft Visual Basic® program,
include the following Declare statement in the General Declarations section of a form
(note that it must be typed as a single line of code):
Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
nIndex As Integer, ByVal dwNewLong As Long) As Long
The SetWindowLong function takes the following arguments.
hWnd An integer value containing the window's handle.
nIndex An integer value that describes the type of information you want to set.
This value may be one of the following:
GWL_EXSTYLE Set the extended window style
GWL_STYLE Set the window style
GWL_WNDPROC The window function's address
dwNewLong A long value containing the new style bits to be given to the window.

Because you want to make the specified form transparent, you call the SetWindowLong
function with the nIndex argument set to GWL_EXSTYLE, specifying the transparency
style bit.
When you run the example program shown below, the form will be displayed in
transparent mode.

Example Program
This program shows how to create a transparent form.
1. Create a new project in Visual Basic. Form1 is created by default. Set the form's
Picture property to the ARCHES.BMP bitmap file (usually found in the
\WINDOWS directory).

2. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Show Form".

3. Add the following code to the Click event for Command1:


4. Private Sub Command1_Click()
5. Form2.Show
6. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


7. From the Visual Basic Insert menu, select Form to create a new form. Form2 is
created by default. Change the size of Form2 so that it is the same size as Form1.
Position Form2 so that it is on top of Form1.

8. Add the following Constant and Declare statements to the General Declarations
section of Form2 (note that the Declare statement must be typed as one single line
of code):
9. Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
10. nIndex As Integer, ByVal dwNewLong As Long) As Long
11. Const WS_EX_TRANSPARENT = &H20&
12. Const GWL_EXSTYLE = (-20)
13. Add the following code to the Form_Load event for Form2:
14. Private Sub Form_Load()
15. Dim Ret As Long
16. Ret = SetWindowLong(Form2.hWnd, GWL_EXSTYLE, WS_EX_TRANSPARENT)
17. End Sub
Run the example program by pressing F5. Click the "Show Form" command button.
Form2 is displayed directly over Form1. It appears as if only Form1 is displayed because
Form2 has the transparent attribute.

114: Preventing a Portion of a Text


Box from Scrolling
July 1, 1995

Abstract
The Microsoft® Windows® application programming interface (API) SendMessage
function can be used to send messages to Microsoft Visual Basic® controls such as Text
Boxes. This article explains how you can prevent text from scrolling in a Text Box
control. The nonscrolling portion of the control can also be made invisible to the user.

Sending Messages to Text Box Controls


The Text Box control in Microsoft® Visual Basic® is best described as a mini-word-
processing program. When the MultiLine property of the Text Box control is set to True,
the lines of text wrap around to the next line. As the amount of text typed into the Text
Box increases, the text within the control will scroll upwards. This means that the text
becomes invisible. The text, however, remains in the control—it is not deleted.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The Microsoft Windows® application programming interface (API) SendMessage
function can be used to prevent a portion of the Text Box control from scrolling out of
view. When you first create the Text Box control, its client area is the formatting
rectangle (that is, the area where text can be typed.) The EM_SETRECTNP message can
be sent to the control to limit the formatting rectangle to a specific area of the Text Box's
client area.
In the example program below, you want the second half of the Text Box control to
remain intact. You don't want the text to scroll out of view. Therefore, you retrieve the
height of the Text Box control from its Height property and divide this value by two.
This gives us the coordinates of the bottom half of the Text Box. The result is then sent to
the SendMessage function, which tells EM_SETRECTNP to prevent that rectangular
area from being scrolled.

You may want to substitute the EM_SETRECT message for the EM_SETRECTNP
message. Using EM_SETRECT stops Windows from redrawing the text in the formatting
rectangle area. This results in the text being invisible within the Text Box control.

Example Program
This program shows how to temporarily freeze a specific portion of a Text Box control.
The frozen portion contains the text that will not be scrolled out of view.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as one single line
of code):
3. Private Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal
4. wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
5. Const WM_USER = &H400
6. Const EM_SETRECTNP = WM_USER + 4
7. Const EM_SETRECT = WM_USER + 3
8. Add the following code to the Form_Load event for Form1:
9. Private Sub Form_Load()
10. Text1.Text = "This is the first paragraph that we want to show."
11. Text1.Text = Text1.Text & " in the Text Box control"
12. Text1.Text = Text1.Text & Chr$(13) & Chr$(10) & "This is the second paragraph that we"
13. Text1.Text = Text1.Text & " want to freeze so that text cannot be scrolled"
14. End Sub
15. Add the following code to the Click event for Form1:
16. Private Sub Form_Click()
17. Dim R As RECT
18. Dim X As Long
19.
20. ScaleMode = 3
21.
22. R.Left = 0

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


23. R.Top = 0
24. R.Right = Text1.Width
25. R.Bottom = Text1.Height / 2
26.
27. X = SendMessage(Text1.hWnd, EM_SETRECTNP, 0, R)
28.
29. End Sub
30. From the Visual Basic Insert menu, click Module to create a new module.
Module1.Bas is created by default.

31. Add the following TYPE structure to Module1.Bas:


32. Type RECT
33. Left As Integer
34. Top As Integer
35. Right As Integer
36. Bottom As Integer
37. End Type
38. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.
Run the example program by pressing F5. The Text Box is displayed on Form1. Click
the mouse once in the form. Try typing new text into the Text Box control. Notice that
the text in the bottom half of the Text Box control does not scroll down as you add more
text to the beginning of the Text Box control.
Modify the program by sending an EM_SETRECT message to the Text Box instead of
an EM_SETRECTNP message. Run the program a second time. The second half of the
Text Box is not shown, even though the text is actually stored in the control as usual.

115: Performing Smart Searches in


Combo Box Controls
July 1, 1995

Abstract
The Combo Box control in Microsoft® Visual Basic® allows your user to easily select
items by clicking the desired entry in the control. This article explains how you can add a
smart search feature to the Combo Box control.

Searching Combo Box Controls Quickly


In a Microsoft® Visual Basic® application, you can use a Combo Box control to provide
a list of items that the user can select from. If the desired item is not in the Combo Box
control, the user can also add an additional item to the list.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The drop-down style of a Combo Box control appears only as a single Text Box control
with a separate arrow button to the right of the box. Clicking the arrow causes the box to
drop down so that the user can see the actual entries stored in the Combo Box control.
If the number of entries in the Combo Box control is relatively small, the user is able to
quickly locate the desired item. However, if there are many entries in the list, scrolling
through the entire list may not be the most efficient method for the user to find a specific
item. In this situation, it would be much quicker to allow the user to perform a "smart
search" for the desired item.

A smart search means that the user can type the first few letters of an entry, and the first
entry in the list that matches these characters will be displayed in the edit portion of the
Combo Box control.
The example program below shows how to implement a smart search routine in your
Visual Basic application. The trick to doing this search lies in the KeyPress event of the
Combo Box control.
Each time the user presses a key on the keyboard, the KeyPress event is triggered. These
keystrokes can be trapped and acted upon in whatever fashion you want. As an example,
in the smart search routine we automatically ignore all keyboard characters that have an
ASCII value of less than 32 or greater than 127. This lets us process only alphabetic
characters (A–Z, a–z), numeric characters (0–9), and punctuation characters (exclamation
point, comma, and so forth).

It is a simple matter to save the characters that the user types to a string variable such as
FindString and then to use the Microsoft Windows® application programming interface
(API) SendMessage function to execute a CB_FINDSTRING message to the Combo
Box control.
The CB_FINDSTRING message lets you search a Combo Box control for an entry that
matches the target string. This function requires two arguments—the number of the item
in the control from which you want the search to start, and the string you want to find. To
search the entire Combo Box control, you set the first argument to a value of -1.
After you have executed the CB_FINDSTRING message, it will return the number of the
matching entry. You can then use this to display the result to the user in the edit portion
of the Combo Box control.

Example Program
This program shows how to perform a "smart search" with a Combo Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As
Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
4. Const CB_ERR = (-1)
5. Const CB_FINDSTRING = &H14C
6. Add the following code to the Form_Load event for Form1:
7. Private Sub Form_Load()
8. Combo1.AddItem "French fries"
9. Combo1.AddItem "Hamburgers"
10. Combo1.AddItem "Milkshakes"
11. Combo1.AddItem "Onion rings"
12. Combo1.AddItem "Ice"
13. Combo1.AddItem "Ice cream"
14. End Sub
15. Add a Combo Box control to Form1. Combo1 is created by default. Set its Style
property to 0-Dropdown.

16. Add the following code to the KeyPress event for Combo1:
17. Private Sub Combo1_KeyPress(KeyAscii As Integer)
18. Dim CB As Long
19. Dim FindString As String
20.
21. If KeyAscii < 32 Or KeyAscii > 127 Then Exit Sub
22.
23. If Combo1.SelLength = 0 Then
24. FindString = Combo1.Text & Chr$(KeyAscii)
25. Else
26. FindString = Left$(Combo1.Text, Combo1.SelStart) & Chr$(KeyAscii)
27. End If
28.
29. CB = SendMessage(Combo1.hWnd, CB_FINDSTRING, -1, ByVal FindString)
30.
31. If CB <> CB_ERR Then
32. Combo1.ListIndex = CB
33. Combo1.SelStart = Len(FindString)
34. Combo1.SelLength = Len(Combo1.Text) - Combo1.SelStart
35. End If
36. KeyAscii = 0
37. End Sub
Run the example program by pressing F5. Type the text you want to find. As you enter
each character, the KeyPress event is triggered. For example, type the letter i. The item
ice appears in the Combo Box's edit window. Try again, this time typing the word ice
followed by a space character. The Combo Box displays ice cream. Try typing a word
that is not in the list, such as potato. The Combo Box will respond by finding the closest
match—in this case, the Onion rings entry.

116: Sending Data to the Printer in


Landscape or Portrait Mode

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


July 1, 1995

Abstract
In a Microsoft® Visual Basic® application, you can send data to the printer by using the
Print method. The printed output appears in the default portrait orientation. This article
shows how to change the orientation of the printer from its default portrait mode to
landscape mode.

Sending Commands to the Printer


When you create a report in a Microsoft® Visual Basic® application, the output is
usually sent to the printer in portrait mode. However, by using two Microsoft Windows®
application programming interface (API) functions, you can tell the printer to print the
report in landscape mode.

Most printers manufactured today support a number of functions such as changing the
print orientation to landscape mode. You tell the printer driver to select landscape
printing by sending specific control code commands to the device. In Windows
terminology, these control code commands are called Escape operations.
The example program below shows how to use the Windows API Escape function to
change the orientation of the printer. When you run this program, notice that a blank
piece of paper is not ejected when the setting takes effect.

How do we prevent the blank sheet of paper from being ejected? The AbortDoc function
tells Windows to ignore the previous print request. This generates a printer error, which is
trapped by the On Error Resume Next statement. Therefore, the printer is set to the new
orientation without ejecting a blank piece of paper.
The Escape function can be used to send specific control codes to a printer device. The
CONSTANT.TXT file contains a list of the most commonly used escape codes that can
be used with printers, display screens, and other devices.
To use the Escape function within your program, include the following Declare
statement in the General Declarations section of your form (note that this Declare
statement must be typed as a single line of text):
Private Declare Function Escape Lib "GDI" (ByVal hDC As Integer, ByVal nEscape
As Integer, ByVal nCount As Integer, lpInData As Any, lpOutData As Any) As
Integer
The Escape function requires five arguments, as follows.
hDC An integer value containing the device context's handle
nEscape An integer value containing the specific escape code to be sent to the
device context
nCount An integer value set to the size of the lpInData argument
lpInData Varies—see below

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


lpOutData Varies—see below

The arguments lpInData and lpOutData are set according to which escape code is being
sent to the printer. Because we want to set the printer to either landscape or portrait mode,
we specify the nEscape argument as GETSETPRINTORIENT. The
GETSETPRINTORIENT operation requires that the arguments lpInData and lpOutData
point to a 20-byte structure. To actually set the orientation, the first long value in this
structure must be set to the specific orientation you want to use.
After the escape code (landscape or portrait) is sent to the printer, you must use the
Windows API AbortDoc function. This function tells the printer to abort the print
request. Calling the AbortDoc function sets the printer to the new mode. All subsequent
output to the printer will then print in whichever print orientation you selected. This
means that you may have to issue another Escape statement to reset the printer to portrait
mode to return the printer to its default state.

Example Program
This program shows how to set the printer to landscape (or portrait) mode.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Private Declare Function AbortDoc Lib "GDI" (ByVal hDC As Integer) As Integer
4. Private Declare Function Escape Lib "GDI" (ByVal hDC As Integer, ByVal
5. nEscape As Integer, ByVal nCount As Integer, lpInData As Any, lpOutData As
6. Any) As Integer
7. Const PORTRAIT = 1
8. Const LANDSCAPE = 2
9. Const GETSETPAPERORIENT = 30
10. Const NULLVALUE = 0&
11. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Landscape".

12. Add the following code to the Click event for Command1:
13. Private Sub Command1_Click()
14. PrintOrient LANDSCAPE, "This is landscape printing."
15. End Sub
16. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Portrait".

17. Add the following code to the Click event for Command2:
18. Private Sub Command2_Click()
19. PrintOrient PORTRAIT, "This is portrait printing."

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


20. End Sub
21. Create a new procedure called PrintOrient. Add the following code to this
procedure:
22. Sub PrintOrient(Mode As Integer, PrintThis As String)
23. Dim Orient As OrientStructure
24. Dim Ret As Integer
25. Dim X As Integer
26.
27. Printer.Print ""
28. Orient.Orientation = Mode
29. X = Escape(Printer.hDC, GETSETPAPERORIENT, Len(Orient), Orient, NULLVALUE)
30. On Error Resume Next
31. Ret = AbortDoc(Printer.hDC)
32. On Error Resume Next
33.
34. Printer.EndDoc
35. Printer.Print PrintThis
36. Printer.EndDoc
37. End Sub
38. From the Visual Basic Insert menu, click Module to create a new module.
Module1.Bas is created by default.

39. Add the following Type structure to Module1.Bas:


40. Type OrientStructure
41. Orientation As Long
42. Pad As String * 16
43. End Type
Run the example program by pressing F5. Click the Landscape command button to print
a test sheet in landscape mode. Next, click the Portrait command button to print a test
sheet in portrait mode. Notice that each time you change the orientation of the printer, a
blank sheet of paper is not ejected needlessly.

117: Changing a Menu's Shortcut Key at


Run Time
July 1, 1995

Abstract
When you use the Menu Editor in Microsoft® Visual Basic®, you can assign a shortcut
or accelerator key to each individual menu entry. This article shows how you can change
this shortcut key at run time within your Visual Basic application.

Setting Shortcut Keys in Visual Basic Menus


The Microsoft® Visual Basic® Menu Editor lets you easily design a menu system for
your application. When your program is run, the user can click a menu entry to perform

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


some kind of operation. As an alternative to using the mouse to select a menu entry, the
user can type a keystroke combination such as ALT+S to invoke the menu selection.
These keystroke combinations are called access or accelerator keys.
After you have designed your menu structure in the Visual Basic Menu Editor, you may
decide to change the access key for a specific menu entry at run time. The example
program below changes the ALT+F access key for the File menu to ALT+E.

To change a menu entry's access key at run time, you need to process the keystrokes at
the form-level. That is, the KeyDown event for the underlying form will need to be
monitored.
The KeyDown event in Visual Basic is triggered each time a user presses a key on the
keyboard. The event is triggered for the control that has the focus. In this case, the control
is the form that the menu is attached to.
The KeyDown event tells you which key or combination of keys was just pressed on the
keyboard. The KeyCode argument gives you a unique number that identifies each
individual key on the keyboard. For example, if the KeyCode value returned is 9, you
know that the TAB key was just pressed.

In the example program below, you use the KeyDown event to determine if the user
pressed the CTRL+E keystroke combination. If CTRL+E was pressed, the program
displays a message box telling you that the File menu item was selected. In all other
cases, the KeyDown event simply ignores the incoming keystrokes.

Example Program
This program shows how to change a menu item's shortcut key from within a Visual
Basic application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Option Explicit
4. Dim ShortCut As String * 1
5. From the Visual Basic Tools menu, click Menu Editor to create a single menu
item. In the Caption field, type "&File", and in the Name field, type "mnuFile".
Click OK to create the menu structure and to return to the design mode in Visual
Basic.

6. Add the following code to the Form_Load event for Form1:


7. Private Sub Form_Load()
8. Command1.Caption = "Change ShortCut"
9. KeyPreview = True
10. End Sub
11. Add the following code to the KeyDown event for Form1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


12. Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
13. If Shift And 2 <> 2 Then Exit Sub
14. If KeyCode = Asc(ShortCut) Then
15. mnuFile_Click
16. End If
17. End Sub
18. Add the following code to the Click event for mnuFile:
19. Private Sub mnuFile_Click()
20. MsgBox "Menu was selected"
21. End Sub
22. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Change Item".

23. Add the following code to the Click event for Command1:
24. Private Sub Command1_Click()
25. ShortCut = "E"
26. mnuFile.Caption = "Fil" & "&" & LCase$(ShortCut)
27. End Sub
Run the example program by pressing F5. Notice the menu at the top of the form. The
menu entry says "File" with the letter F underlined. The letter F is the menu entry's
access key. Click the command button. The menu changes to "File" with the letter e
designated as the access key. Press CTRL+E on the keyboard; a message box pops up
saying that that menu item was just clicked.

118: Converting a Word for


Windows Document to RTF Format
July 1, 1995

Abstract
In a Microsoft® Visual Basic® application, you may need to convert a Microsoft Word
for Windows® version 6.0 document to rich-text format (RTF). This article explains how
this can be accomplished in Visual Basic.

Using OLE Automation in Visual Basic


You can use OLE Automation in a Microsoft® Visual Basic® application to perform
specific tasks in another Microsoft Windows®-based application. For example, you may
need to convert a Microsoft Word for Windows version 6.0 document to rich-text format
(RTF).

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The example program below shows how to run Word for Windows, load an existing
document into Word, and then save that document to disk in RTF format. The OLE
commands sent to Microsoft Word enable you to have total control over Word; that is,
from within Visual Basic you can tell Word exactly what to do and when to do it.
In this example program, you need some way to communicate with Microsoft Word. You
do this by creating an object variable for Word. In this case, you use WordBasic. Next,
you tell Microsoft Word that you want to load a specific document. Therefore, you tell
Word to carry out a FileOpen command to load the document into memory.

In the same manner, you use the object variable to tell Word to save the document (using
the WordBasic FileSaveAs command) in RTF format. You must specify both a file name
for the document and the file format in which you want to save the document. Because
you want to save the file as an RTF document, specify the value 6 for RTF.
After Word has saved the document in RTF format, you want to exit Word and return to
your Visual Basic application. This is done by setting the object variable to Nothing,
which closes the server application.

Example Program
This program shows how to use OLE Automation to convert a Microsoft Word for
Windows 6.0 document to RTF format from within a Visual Basic application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Convert to RTF".

3. Add the following code to the Click event for Command1:


4. Private Sub Command1_Click()
5. Dim Obj As Object
6. Set Obj = CreateObject("Word.basic")
7. Obj.FileOpen "c:\demo.doc"
8. Obj.FileSaveAs "c:\demo.rtf", 6
9. Set Obj = Nothing
10. MsgBox "Document converted to RTF format"
11. End Sub
Note This program assumes that you have a Word for Windows 6.0 document named
DEMO.DOC stored in the root directory of drive C. This document will be converted to
RTF format and stored on drive C as DEMO.RTF.
Run the example program by pressing F5. Click the "Convert to RTF" command button.
Visual Basic runs Microsoft Word, loads DEMO.DOC into memory, and saves a new
copy of the document to disk in RTF format. A message box is displayed when the whole
process has been completed.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


119: Creating a Resizable Control at Run
Time
July 1, 1995

Abstract
You can add many different controls, such as Text Boxes, to your Microsoft® Visual
Basic® forms. At design time, you can adjust the size and position of the control. This
article explains how the user can modify the size of these controls at run time from within
a Visual Basic application.

Resizing Controls at Run Time


When designing a Microsoft® Visual Basic® application, you simply add controls to
your form as needed. For example, the Text Box control gives your program the features
of a mini-word-processing program.
The size of the control must be set at design time. However, by using two Microsoft
Windows® application programming interface (API) functions (GetWindowLong and
SetWindowLong), you can let your user resize a control such as a Text Box at run time.
When you add a control such as a Text Box to your Visual Basic application, you are
essentially creating a new window. Every window created under the Windows operating
system has certain style attributes associated with it. For example, a Text Box control
may have a window style of ES_MULTILINE. This tells Windows that this control is a
multiline edit control.
Normally, a Text Box control cannot be resized at run time. However, by changing the
control's style attributes, the user will be able to adjust the physical size of the Text Box
while your program is running.
This is accomplished by calling the GetWindowLong and SetWindowLong functions.
First, you call the GetWindowLong function to retrieve the window's current style
attributes for the Text Box control. Next, you use the bitwise OR operator to set the
WS_THICKFRAME attribute for the Text Box control. A window that has a
WS_THICKFRAME attribute is drawn with a thick border around its perimeter. You can
use this border to change the size of the window.
The SetWindowLong function is then run, which tells Windows to modify the style
attribute of the Text Box control.

The final step is to anchor the newly sized Text Box so that its new position and size is
registered on the underlying form. The SetWindowPos function accomplishes this task.

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


This program shows how to create a resizable Text Box control at run time in Visual
Basic.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
4. nIndex As Integer) As Long
5. Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
6. nIndex As Integer, ByVal dwNewLong As Long) As Long
7. Private Declare Sub SetWindowPos Lib "User" (ByVal hWnd As Integer, ByVal
8. hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal CX
9. As Integer, ByVal CY As Integer, ByVal wFlags As Integer)
10. Const SWP_NOSIZE = &H1
11. Const SWP_NOZORDER = &H4
12. Const SWP_NOMOVE = &H2
13. Const SWP_DRAWFRAME = &H20
14. Const GWL_STYLE = (-16)
15. Const WS_THICKFRAME = &H40000
16. Add a Command Button control to Form1. Command1 is created by default.

17. Add the following code to the Click event for Command1:
18. Private Sub Command1_Click()
19. ResizeControl Text1, Form1
20. End Sub
21. Add a Text Box control to Form1. Text1 is created by default.

22. Create a new function called ResizeControl. Add the following code to this
function:
23. Function ResizeControl(ControlName As Control, FormName As Form)
24. Dim NewStyle As Long
25. NewStyle = GetWindowLong(ControlName.hWnd, GWL_STYLE)
26. NewStyle = NewStyle Or WS_THICKFRAME
27. NewStyle = SetWindowLong(Text1.hWnd, GWL_STYLE, NewStyle)
28. SetWindowPos ControlName.hWnd, FormName.hWnd, 0, 0, 0, 0, SWP_NOZORDER Or
SWP_NOSIZE Or SWP_NOMOVE Or SWP_DRAWFRAME
29. End Function
Run the example program by pressing F5 Click the command button. You can now make
the Text Box any size you want.

120: Minimizing Program Manager


When Visual Basic Is Run
July 1, 1995

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Abstract
You can minimize a specific Microsoft® Windows®-based application each time you
launch another Windows-based application. This article explains how you can minimize
Windows Program Manager each time you run Microsoft Visual Basic®. The same
technique can be applied to any other Windows-based program.

Launching Applications While Minimizing Others


In some situations, such as when you want to keep your desktop as clean as possible, you
may want to launch a specific application by first minimizing Microsoft® Windows®
Program Manager (or Windows Explorer). This can be done very easily in Microsoft
Visual Basic®.

Let's assume that each time you load Visual Basic in order to design a new project, you
want to minimize Program Manager. After you have finished working in Visual Basic,
you want Program Manager to be restored to its normal, maximized state.
To accomplish this task in Visual Basic, you need to use two Windows application
programming interface (API) functions. The FindWindow function returns the handle of
the window that matches the name of your target window. In this case, you want to find
the handle of the window belonging to Program Manager. Therefore, you tell
FindWindow to find the first window whose window name is "Program Manager".
When you have the Program Manager window handle, you use the WindowState
property in Visual Basic to minimize your program's window. This means that your
Visual Basic program runs quietly in the background, waiting for Visual Basic to be run.
Approximately once every second, the program checks the computer system to see
whether Visual Basic has been run. This is done in the Timer1 routine in the example
program that follows. If the FindWindow function determines that Visual Basic is
running in memory, it uses the ShowWindow function to minimize the Program
Manager window. Alternatively, if the FindWindow function finds that Visual Basic is
not running, it tells ShowWindow to maximize the Program Manager window.
You can modify this example program to monitor any Windows-based application. In
addition, you might want to add this program to your Windows Startup group so that it
runs each time you start Windows.

Example Program
This program shows how to minimize Program Manager each time you run Visual Basic.
When you quit Visual Basic, Program Manager is restored to its original, maximized
state.
1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Dim, Constant, and Declare statements to the General
Declarations section of Form1 (note that each Declare statement must be typed as
a single line of code):
3. Private Declare Function FindWindow Lib "User" (lpClassName As Any, lpWindowName
4. As Any) As Integer
5. Private Declare Function ShowWindow Lib "User" (ByVal hWnd As Integer, ByVal
6. nCmdShow As Integer) As Integer
7. Const SW_MINIMIZE = 6
8. Const SW_RESTORE = 9
9. Dim pm_hwnd As Integer
10. Dim vb_hwnd As Integer
11. Dim R As Integer
12. Dim Flag As Integer
13. Dim VBS As String
14. Add the following code to the Form_Load event for Form1:
15. Private Sub Form_Load()
16. Dim PMS As String
17.
18. PMS = "Program Manager"
19. VBS = "Microsoft Visual Basic - Project1 [design]"
20. pm_hwnd = FindWindow(ByVal 0&, ByVal PMS)
21. Form1.WindowState = 1
22. Flag = False
23. End Sub
24. Add a Timer control to Form1. Timer1 is created by default. Set its
TimerInterval property to 1.

25. Add the following code to the Timer1 event for Timer1:
26. Private Sub Timer1_Timer()
27. vb_hwnd = FindWindow(ByVal 0&, ByVal VBS)
28. If Flag = False Then
29. If vb_hwnd <> 0 Then
30. Flag = True
31. R = ShowWindow(pm_hwnd, SW_MINIMIZE)
32. End If
33. Else
34. If vb_hwnd = 0 Then
35. Flag = False
36. R = ShowWindow(pm_hwnd, SW_RESTORE)
37. End If
38. End If
39. End Sub
40. On the File menu in Visual Basic, click Make EXE File. Save the file as
MINPM.EXE.
On the File menu in Program Manager, click Run. Type the program's name as
MINPM.EXE and click OK. The MINPM program is now running in memory. When you
start Visual Basic, Program Manager will be minimized on the desktop. As soon as you
quit Visual Basic, Program Manager will be maximized.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


121: Modifying a Text Box's
Appearance at Run Time
July 1, 1995

Abstract
The Text Box control in Microsoft® Visual Basic® is a mini-word-processing program
that lets your user type either single or multiple lines of text. At design time, you set the
MultiLine property either to False (single) or to True (multiline). This property cannot be
changed at run time. However, during run time you can use Scroll Bar controls to give
the impression that your Text Box can be made single or multiline at run time, regardless
of the MultiLine property setting.

Changing a Control's Features at Run Time


When designing a Microsoft® Visual Basic® application, you can set the MultiLine
property of a Text Box to True or False. If this property is set to False, only a single line
of text can be typed in the Text Box control. If the MultiLine property is set to True,
many lines of text can be typed in the control. In addition, if the ScrollBars property is
set to 3-Both, you can scroll through the text both vertically and horizontally.

There's only one problem—the MultiLine property cannot be dynamically switched at


run time, which means that the Text Box is set to what it was in the design phase of the
program.

However, by using the Microsoft Windows® application programming interface (API)


SetScrollRange function, you can add code to your Visual Basic application that will
allow you to create a work-around solution. This enables you to change the Text Box's
appearance from single to multiline at run time.
The SetScrollRange function lets you set the minimum and maximum indicator positions
of a scroll bar. To use this function, add the following Declare statement to the General
Declarations section of your form (note that the Declare statement must be typed as a
single line of code):
Private Declare Sub SetScrollRange Lib "User" (ByVal hWnd As Integer, ByVal nBar
As Integer, ByVal nMinPos As Integer, ByVal nMaxPos As Integer, ByVal
bRedraw As Integer)
The SetScrollRange function requires five arguments, as follows.
hWnd An integer value containing the window or scroll bar's handle
nBar An integer value set to one of the following values:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


ESB_ENABLE_BOTH Both arrows enabled
ESB_DISABLE_LTUP Left or Up arrow disabled
ESB_DISABLE_RTDN Right or Down arrow disabled
ESB_DISABLE_BOTH Both arrows disabled
nMinPos An integer value containing the minimum indicator position
nMaxPos An integer value containing the maximum indicator position
bRedraw An integer value, when set to True, to redraw the scroll bar

To disable the scroll bars in this Visual Basic program, you simply call SetScrollRange
with the minimum and maximum position indicators set to the same value. When you
want to enable the scroll bars again, you call SetScrollRange with the minimum and
maximum position indicators set to 1 and 100, respectively. When you change the
position indictors to 1 and 100, you can scroll through the contents of the Text Box
control regardless of the MultiLine property setting. This gives the impression that the
Text Box control is MultiLine when it is indeed set to single-line status.

Example Program
This program shows how to create a Text Box control that can be switched at run time
from single-line to multiline status, with or without scroll bars.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of text):
3. Private Declare Sub SetScrollRange Lib "User" (ByVal hWnd As Integer,
4. ByVal nBar As Integer, ByVal nMinPos As Integer,
5. ByVal nMaxPos As Integer, ByVal bRedraw As Integer)
6. Const ESB_DISABLE_BOTH = 3
7. Const ESB_ENABLE_BOTH = 1
8. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True. Set its ScrollBars property to 3-Both.

9. Add a Command Button control to Form1. Command1 is created by default. Set


its Caption property to "Disable".

10. Add the following code to the Click event for Command1:
11. Private Sub Command1_Click()
12. Dim hWnd As Integer
13. Dim Min As Integer
14. Dim Max As Integer
15. Min = 1
16. Max = Min
17. Call SetScrollRange(Text1.hWnd, ESB_DISABLE_BOTH, Min, Max, 1)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


18. End Sub
19. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Enable".

20. Add the following code to the Click event for Command2:
21. Private Sub Command2_Click()
22. Dim hWnd As Integer
23. Dim Min As Integer
24. Dim Max As Integer
25. Min = 1
26. Max = 100
27. Call SetScrollRange(Text1.hWnd, ESB_ENABLE_BOTH, Min, Max, 1)
28. End Sub
Run the example program by pressing F5. Click the Text Box control and type several
lines of text. Notice that you can use the scroll bars to scroll through the text in the
control. Click Disable. Now you cannot use the scroll bars to scroll through the Text Box
control. To enable the scroll bars again, click Enable.

122: Comparing Object Variables in


Visual Basic
July 1, 1995

Abstract
The IS operator in Microsoft® Visual Basic® lets you determine whether the names of
two variables refer to the same object (a control or form). This article explains how you
can compare two object variables to determine whether they are the same entity.

Using the IS Operator


Microsoft® Visual Basic® allows you to dynamically create new objects at run time. For
example, if you wanted to create another copy of Form1 while your program is running,
you would issue a Dim statement to create an object variable of the Form type. The Set
statement lets you create a new instance of a form or control. The just-cloned copy of the
form or control inherits the same properties as the original form or control. Therefore, the
Set command assigns a variable name to the cloned form or control, which allows you to
keep track of the newly created objects. Later on in your Visual Basic application, you
can use the IS operator to determine whether two object variables refer to the same
control or form.
In the example program below, you use the statement:
If CopyOfForm Is Form1 Then...
to see whether the new instance of the form you just created is the same as the original
form. If it is, you display a message to that effect.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


When using object variables in your Visual Basic applications, you should be aware of
two caveats. First, you cannot compare two object variables by using the equal ("=")
operation. Second, the IS operator must be used within an If-Then statement, not from
within a While loop.

Example Program
This program shows how to compare two Visual Basic objects to see whether they are
identical.

1. Start a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Dim CopyOfForm As New Form1
4. Add a Command Button control to Form1. Command1 is created by default.

5. Add the following code to the Click event for Command1.


6. Private Sub Command1_Click()
7. Set CopyOfForm = Form1
8. CopyOfForm.Show
9. If CopyOfForm Is Form1 Then
10. MsgBox "CopyOfForm is a copy of Form1."
11. End If
12. End Sub
Run the example program by pressing F5. The Set command creates the two forms, even
though only one copy of the form is visible. A message box is displayed, confirming that
the two object variables (CopyOfForm and Form1) are the same entities (that is, both
forms have exactly the same property settings). Note that if you run the example program
by first removing the Set statement in the Click event of Command1, the comparison
does not find the two forms to be identical controls.

123: Displaying Records from a


Microsoft Access Database in an Outline
Control
July 1, 1995

Abstract

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The Outline control in Microsoft® Visual Basic® lets you create a hierarchical list of
items. This article explains how to populate the Outline control with information stored
in a Microsoft Access database file.

Extracting Fields from an Access Database


From within a Microsoft® Visual Basic® application, you can retrieve individual fields
from a Microsoft Access database and display these fields in an Outline control.
The OpenDatabase function loads the specified database into Microsoft Access. After
the file has been opened, you need to create a Snapshot variable for a specific table
within the database. To do this, you use the CreateSnapshot method. The Snapshot
allows you to read data from a table but does not allow you to change any data stored in
the file.
After the Snapshot variable has been created, you use a While-Wend loop to process each
record in the specified table. The example program below, for instance, retrieves each
Title field from the BIBLIO.MDB file and uses the AddItem method of the Outline
control to populate that control with the data from the Title field. After processing all the
records in the table, you must remember to close both the Snapshot and the Microsoft
Access database file.

Example Program
This program shows how to display specific fields from a Microsoft Access database in a
Visual Basic Outline control.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add an Outline control to Form1. Outline1 is created by default.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. Dim DB As Database
7. Dim SN As Snapshot
8. Dim X As Integer
9. Dim Y As Integer
10.
11. Set DB = OpenDatabase("C:\VB\BIBLIO.MDB")
12. Set SN = DB.CreateSnapshot("select * from titles order by Title")
13.
14. While Not SN.EOF
15. Outline1.AddItem SN.Fields("Title")
16. SN.MoveNext
17. Wend
18. SN.Close
19. DB.Close

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


20. End Sub
Run the example program by pressing F5. Click the command button. The program
retrieves the Title field's data from each record in the BIBLIO.MDB file and adds it to the
Outline control.

124: Determining Available Disk Drives


in Visual Basic
July 1, 1995

Abstract
The Drive List Box control in Microsoft® Visual Basic® reports the names of each drive
connected to the computer system. This article explains another method you can use to
determine which disk drives are available.

Retrieving Disk Drives


The Drive List Box control provided in Microsoft® Visual Basic® displays a list of all
available disk drives attached to the computer system. You can, however, retrieve this
same information without using a Drive List Box control in your application.
The Visual Basic CurDir$ function identifies your current drive and directory. The
syntax for the CurDir$ function is:
X$ = CurDir$
or
CurDir$ = "C:\TEMP"
When the first statement above is executed, CurDir$ retrieves the current path into the X
string variable. The second statement above tells CurDir$ to switch to the TEMP
directory on drive C. If you attempt to change to a nonexistent drive or directory, the
CurDir$ function will generate an error condition.

In the example program, you use a For-Next loop to cycle through all possible disk drives
that may be installed in the computer system. Each time through the loop, you ask
CurDir$ to change to a new, higher disk drive letter. When the function reports that it
cannot switch to the specified drive, you know that you have found the last available disk
drive.

Example Program
This program shows how to retrieve all valid disk drives without using a Drive List Box
control.
1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add a Text Box control to Form1. Text1 is created by default.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. Text1.Text = ""
7. For X = Asc("C") To Asc("Z")
8. On Error Resume Next
9. c$ = CurDir$(Chr$(X) & ":\")
10.
11. If Err Then
12. Err = 0 'no such drive
13. Else
14. Text1.Text = Text1.Text & " " & Chr$(X)
15. End If
16. Next X
17. End Sub
Run the example program by pressing F5. Click the command button. The Text Box
control shows which disk drives are installed in the computer system.

125: Retrieving a Compressed


File's Original Name
July 1, 1995

Abstract
When designing a Microsoft® Visual Basic® application, you may need to use an
installation program to compress and decompress files needed by your program. This
article explains how to retrieve the original file name of a compressed file.

Using the GetExpandedName API Function


The MS-DOS® COMPRESS.EXE program allows you to compress one or more files on
disk. Compressed files take up less space on your disk, and they are typically used to
create installation programs.
The Microsoft® Windows®-based LZEXPAND.DLL function library contains several
routines to open, close, read, and write compressed files. However, you need to first
determine the file's original, uncompressed name.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The Windows application programming interface (API) function GetExpandedName
can retrieve the original name of a compressed file. Compressed files are stored on disk
with the last character in the file name extension set to an underscore ("_") character.
When you run the COMPRESS.EXE program, you have the option of using the "/r"
command line parameter. This command line parameter tells COMPRESS.EXE to create
the file with an underscore character as the last character in the file name.
To use the GetExpandedName function in a Microsoft Visual Basic® application, add
the following Declare statement to the General Declarations section of your form (note
that this Declare statement must be typed as a single line of code):
Private Declare Function GetExpandedName Lib "LZEXPAND.DLL" (ByVal lpszSource
As String, ByVal lpszBuffer As String) As Integer
The GetExpandedName function requires two arguments, as follows.
lpszSource A string containing the compressed file's name
lpszBuffer A string to hold the complete file's name

After this function is called, a value greater than zero will be returned if the operation
was successful. If the function was not successful, a negative value will be returned.

Example Program
This program shows how to retrieve the full name of a file that was previously
compressed by the COMPRESS.EXE program.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that this Declare statement must be typed as a single line of code):
3. Private Declare Function GetExpandedName Lib "LZEXPAND.DLL" (ByVal lpszSource
4. As String, ByVal lpszBuffer As String) As Integer
5. Add a Text Box control to Form1. Text1 is created by default.

6. Add a Command Button control to Form1. Command1 is created by default.

7. Add the following code to the Click event for Command1:


8. Private Sub Command1_Click()
9. Dim Temp As String
10. Dim X As Integer
11.
12. Text1.Text = ""
13. Temp = Space$(128)
14. X = GetExpandedName("C:\TEMP\MYFILE.DL_", Temp)
15. Text1.Text = "Filename: " & Temp
16. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Note This program assumes that you have a previously compressed file called
MYFILE.DL_ stored in the C:\TEMP directory.
Run the example program by pressing F5. Click the command button. The full name of
the compressed file will be displayed in the Text Box control.

126: Sorting a List Box Control in


Descending Order
July 1, 1995

Abstract
The List Box control in Microsoft® Visual Basic® provides a method of sorting a list of
items in alphabetic order. This article explains how you can sort the items in a List Box
control in descending order.

Sorting in Descending Order


When developing an application in Microsoft® Visual Basic®, you can use the List Box
control to maintain a list of related items. For example, you may need to store a list of
vendor names in a List Box control.

You can sort the items contained in a List Box control by setting the control's Sorted
property to True. The items will be sorted in ascending alphabetic (A–Z) order. However,
if you want to sort the items in descending order, you need to use two List Box controls.
The example program uses two List Box controls. The first List Box contains the items
you want to sort. The Visible property of this List Box is set to False, because it is your
"working" List Box control. You don't want to see the contents of this control at run time.
In addition, the Sorted property of the List Box is set to True. Each item you add to this
List Box control is stored in ascending order.
The second List Box control used in this program will display the final result of your sort
routine. Its Visible property is set to True and its Sorted property is set to False.

In the example program below, you simply copy the desired items you want to sort from
the first List Box control to the second List Box control. To do this, you use a For-Next
loop. The ListCount property of a List Box control tells you how many entries are stored
in the control. You use this value (minus 1, because you start counting from zero) to step
backwards from the end of the list to the beginning. Each time you cycle through the For-
Next loop, you add the current item to the second List Box control. This stores the final
list of items in descending (Z–A) order in the second List Box control.

Example Program
This program shows how to reverse-sort the contents of a List Box control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Private Sub Form_Load()
4. List1.AddItem "Ten"
5. List1.AddItem "One"
6. List1.AddItem "Four"
7. List1.AddItem "Sixteen"
8. List1.AddItem "Forty"
9. End Sub
10. Add a List Box control to Form1. List1 is created by default. Set its Visible
property to False and its Sorted property to True.

11. Add a second List Box control to Form1. List2 is created by default. Set its
Sorted property to False.

12. Add a Command Button control to Form1. Command1 is created by default.

13. Add the following code to the Click event for Command1:
14. Private Sub Command1_Click()
15. Dim X As Integer
16. For X = List1.ListCount - 1 To 0 Step -1
17. List2.AddItem List1.List(X)
18. Next X
19. End Sub
Run the example program by pressing F5. Click the command button. The List Box
control is populated with the entries from the first (hidden) List Box control. These
entries are sorted in descending order, from Z through A.

127: Stamping Files with the


Current Date and Time
July 1, 1995

Abstract
While your Microsoft® Visual Basic® program is running, you may need to update the
date and time stamp for a particular file. This article explains how you can set a file's date
and time stamp to the current date and time setting.

Manipulating a File's Date and Time Information

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The FileDateTime function in Microsoft® Visual Basic® can tell you when a specific
file was first created or last modified. FileDateTime returns both the date and time
information as a string variable. However, Visual Basic does not offer a function to set a
file's date and time information.
The example program below shows how to update the date and time information for an
already existing file. First, you need to make sure the file exists. The Dir$ function will
return a NULL or empty string if it cannot find the specified file on the disk.
When you know the file exists, you can use the Visual Basic file manipulation
commands—Open, Get, Put, and Close—to force the operating system to update the
file's date and time stamp.
The technique is straightforward. You simply open the file, use the Get statement to
retrieve the first byte from the file, and then use the Put statement to write that same byte
back to the file. When you close the file, Microsoft Windows® automatically updates the
file's date and time information.

Example Program
This program shows how to update a file's date and time information from within a
Visual Basic application.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

3. Add the following code to the Click event for Command1:


4. Private Sub Command1_Click()
5. Dim FName As String
6. Dim F As String
7. Dim AnyThing As Integer
8. Dim X As Integer
9.
10. FName = "c:\test.doc"
11. F = Dir$(FName)
12. If F = "" Then GoTo NoSuchFile
13.
14. On Error GoTo FileError
15. X = FreeFile
16. Open FName For Binary As X
17. Get X, 1, AnyThing
18. Put X, 1, AnyThing
19. Close X
20. MsgBox "New time/date is: " & FileDateTime("c:\test.doc"), 16, "OK"
21. Exit Sub
22. FileError:
23. MsgBox "Unable to time-stamp file", 16, "Error"
24. Exit Sub
25. NoSuchFile:
26. MsgBox "That file does not exist!", 16, "Error"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


27. End Sub
Note This example program assumes you have a file named TEST.DOC in the root
directory of drive C.
Run the example program by pressing F5. Click the command button to update the file's
date and time information.

128: Calculating the Beginning and


Ending Date of a Month
July 1, 1995

Abstract
When developing an application in Microsoft® Visual Basic®, you may need to calculate
the specific date of the first and last days of a month. This article explains how to
accomplish this task in Visual Basic.

Manipulating Dates in Visual Basic


Microsoft® Visual Basic® offers many functions that you can use to determine what day
a specific date falls on, what month it is, and so on. You can use the DateValue function
to convert a date string, such as July 3, 1995, to a date serial number. This function
usually determines what day of the week a specific date falls on.
The DateSerial function converts a numeric value to a date serial number. The serial
number is a unique number that represents each possible date from January 1, 100 A.D.
through December 31, 9999. Therefore, you can easily calculate how many days elapsed
between two specific dates by using the DateSerial function.

In the example program below, you want to find the first and last dates in the month of
July, 1995. To do this, you convert the string date (July 3, 1995) to a date serial number.
Then you use the DateSerial function in conjunction with the Year and Month functions
to calculate the first date in the month of July. You repeat this routine to determine the
last date in the month of July.

After calling the DateSerial function, you must use the Visual Basic Year, Month, and
Day functions to extract and decode specific portions of information from the date serial
number. You must do this because the date serial number is encoded in a special format.
In the example program below, you use the Year and Month functions to determine
which date is the first day of the month. The Month function returns a value of 1 through
12 that represents the specified month. In a similar fashion, the Visual Basic Year
function returns the year from the encoded serial number.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The MonthEnd function in the example program actually calculates the next month's
first day. It then backtracks by one day to calculate the correct date for the last day of the
month.

Example Program
This program shows how to retrieve the first and last date for a specified month.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. Dim LastDay As Variant
7. Dim FirstDay As Variant
8. Dim ThisDate As Variant
9. text1.TEXT = ""
10. ThisDate = DateValue("July 3, 1995")
11. FirstDay = MonthBegin(ThisDate)
12. text1.TEXT = Str$(FirstDay) & Chr$(13) & Chr$(10)
13. LastDay = MonthEnd(ThisDate)
14. text1.TEXT = text1.TEXT & Str$(LastDay)
15. End Sub
16. Create a new function called MonthBegin. Add the following code to this
function:
17. Function MonthBegin(vbdate As Variant) As Variant
18. MonthBegin = DateSerial(Year(vbdate), Month(vbdate), 1)
19. End Function
20. Create a new function called MonthEnd. Add the following code to this function:
21. Function MonthEnd(vbdate As Variant) As Variant
22. MonthEnd = DateSerial(Year(vbdate), Month(vbdate) + 1, 0)
23. End Function
Run the example program by pressing F5. Click the command button. The first and last
days in the month of July are displayed as 7/1/95 and 7/31/95, respectively.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


129: Setting the Focus After
Removing an Item from the List Box
Control
July 1, 1995

Abstract
The List Box control in Microsoft® Visual Basic® lets you easily manage related pieces
of information. You use the AddItem method to add new items to the list and the
RemoveItem method to delete items from the list. This article explains how to set the
focus to the next item in the List Box control after deleting the currently selected item.

Setting the Focus to the Next Available Item


When using the List Box control in Microsoft® Visual Basic®, you can use the
AddItem method to add new items to the list or the RemoveItem method to delete items
from the list.

However, when you delete an item from the list, the focus does not automatically change
to the next available item in the list. The example program below shows how to do this.
The ListIndex property of a List Box control tells you which item was selected by the
user. Knowing this value, you can use the RemoveItem method to delete that specific
entry from the list. For example, if you select the third item in the List Box control, the
ListIndex property would be set to a value of two (the List Box control starts numbering
the entries from zero).
It is a simple matter, then, to set the focus to the next available item in the list by keeping
track of your position within the list. After deleting the selected item, you set the
ListIndex property to your current position minus one. You can then set the focus to this
newly selected item.

Example Program
This program shows how to delete an item from a List Box control and set the focus to
the next available item in the list.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Private Sub Form_Load()
4. List1.AddItem "Item #1"
5. List1.AddItem "Item #2"
6. List1.AddItem "Item #3"
7. List1.AddItem "Item #4"
8. List1.AddItem "Item #5"
9. End Sub
10. Add a List Box control to Form1. List1 is created by default.

11. Add a Command Button control to Form1. Command1 is created by default.

12. Add the following code to the Click event for Command1:
13. Private Sub Command1_Click()
14. Dim PositionInList As Integer
15. Dim NumberOfItemsInList As Integer
16. PositionInList = List1.ListIndex
17. NumberOfItemsInList = List1.ListCount
18. If NumberOfItemsInList > 0 Then
19. If PositionInList >= 0 Then
20. List1.RemoveItem PositionInList
21. Else
22. MsgBox "You must select an item to delete.", 48, "Error"
23. End If
24. Else
25. MsgBox "There are no items to delete.", 48, "Error"
26. End If
27. NumberOfItemsInList = List1.ListCount
28. If NumberOfItemsInList > 0 Then
29. If PositionInList = NumberOfItemsInList Then
30. List1.ListIndex = NumberOfItemsInList - 1
31. Else
32. List1.ListIndex = PositionInList
33. End If
34. End If
35. List1.SetFocus
36. End Sub
Run the example program by pressing F5. Five items will appear in the List Box control.
Notice that no items are selected. Click the Delete command button. A message box is
displayed, indicating that you must select an item before you can delete it.

Click the OK command button. Click the third item (Item #3) to select it. The item is
deleted from the List Box control, and the focus is moved to the next available item in
the list.

Notice that if you attempt to delete an item that does not exist in the List Box control, a
message box will be displayed, telling you that there are no items to delete.

130: Using the Undo Feature with a Text


Box Control

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


July 1, 1995

Abstract
Almost all Microsoft® Windows®-based applications provide an Edit menu on which
you can select the Undo command to reverse the most recently made changes to an edit
control. This article explains how you can add this functionality to your own Microsoft
Visual Basic® application.

Using SendMessage to Undo Edit Control Changes


When you modify the contents of an edit control in Microsoft® Visual Basic®, such as a
Text Box control, the data you add or delete is saved in an internal buffer by the
Microsoft Windows® operating system. You can use the Windows application
programming interface (API) SendMessage function to allow your user to retrieve the
modified text.
Let's assume that you have typed some text into a Text Box control. You now want to
delete some of that text. To do this, you select the text and press the DEL key. The text
you selected is removed from the Text Box control. You can retrieve this text within a
Visual Basic application by sending an EM_UNDO message to Windows. The
EM_UNDO message tells the operating system that you want to undo the last change you
made to the edit control. In this case, the edit control is the Text Box.
After the EM_UNDO message is sent, the original contents of the Text Box control are
restored. The modified text is still stored in the internal Windows buffer. Therefore, in
your application, you need to send an EM_EMPTYUNDOBUFFER message to clear or
delete the contents of this internal buffer. The EM_EMPTYUNDOBUFFER message
clears the undo flag, which means that you can no longer undo your last change to the
edit control.

As shown in the example program below, you can also determine whether an undo
operation can be performed on the edit control. The EM_CANUNDO message returns an
integer value set to True if there is text in the undo buffer, or zero if no text is available.
You can perform an undo operation only if the contents of an edit control have been
previously modified and the data is stored in the undo buffer.

Example Program
This program shows how to add the Undo and Redo editing features to your Visual Basic
application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Private Declare Function SendMessage Lib "User" (ByVal hWnd As Integer,
4. ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Long) As Long
5. Const WM_USER = &H400
6. Const EM_CANUNDO = WM_USER + 22
7. Const EM_EMPTYUNDOBUFFER = WM_USER + 29
8. Const EM_UNDO = WM_USER + 23
9. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

10. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Undo".

11. Add the following code to the Click event for Command1:
12. Private Sub Command1_Click()
13. Dim OK As Long
14. OK = SendMessage(Text1.hWnd, EM_UNDO, 0, 0&)
15. OK = SendMessage(Text1.hWnd, EM_EMPTYUNDOBUFFER, 0, 0&)
16. End Sub
17. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Redo".

18. Add the following code to the Click event for Command2:
19. Private Sub Command2_Click()
20. Dim OK As Long
21. OK = SendMessage(Text1.hWnd, EM_CANUNDO, 0, 0&)
22. If OK = 0 Then
23. MsgBox "Cannot undo the changes you made", 16, "Error"
24. End If
25. OK = SendMessage(Text1.hWnd, EM_UNDO, 0, 0&)
26. End Sub
Run the example program by pressing F5. Type some text into the Text Box control.
Assume that you typed the line, "We will go shopping on Monday and Tuesday." Select
the words "Monday and". Press DEL to delete the text. Click the Redo command button.
The original sentence is restored. Click Redo a second time. The modified sentence is
restored. The Redo function is similar to the cut-and-paste functions you see in word-
processing programs.
Select the words "Monday and" a second time and again delete the text. Click Undo to
restore the original text. Notice that clicking the Undo command button a second time
does nothing. This is because the Undo routine clears the undo flag and the edit buffer.
You can only undo one change at a time.

131: Determining Whether a DLL File Is


16-Bit or 32-Bit
July 1, 1995

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Abstract
When programming in Microsoft® Visual Basic®, you use the functions stored in
dynamic-link library (DLL) files to add greater scope to your applications. You must,
however, use the correct DLL file (16-bit or 32-bit) in your program. This article presents
a function that tells you what type of file you are dealing with.

Determining a File's Type


The Microsoft® Windows® application programming interface (API) allows you to
perform tasks in your Microsoft Visual Basic® applications that the Basic language itself
cannot do. To perform such a task, you must call a function stored in a dynamic-link
library (DLL) file.

However, you must take into consideration whether you are programming in a 16-bit or
32-bit environment. If you're running in a 16-bit environment, then you can only use 16-
bit DLL functions. However, if you're running in a 32-bit environment, you may be able
to make calls to 16-bit and 32-bit functions, depending on the environment in question.
Because your application may be run on many different operating systems (Windows
version 3.1, Windows 95, OS/2®, Windows NT™, and so on), you need to find out
whether files are 16-bit or 32-bit. Then you can determine which API functions can be
used in your Visual Basic program.
In the example program below, you can type the full path of a file you want to check.
When you click the command button, the program reads data from the header block of
the file and reports its file type.
Each time an operating system saves a file on disk, the operating system prefixes the file
with a header block as the first data stored in the file. This header block contains
information that can be used to identify the file's type. For example, an MS-DOS® file
has a header containing the two characters "MZ". When you run the ExeType function
on this file, you would know that it is either a .COM, .CMD, .PIF, or .BAT file if the file
contains the "MZ" signature in its header block.

Example Program
This program shows how you can identify individual file types.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant statements to the General Declarations section of


Form1:
3. Option Explicit
4. Const ordMSDOS = 1
5. Const ordWindows = 2
6. Const ordOS2_1 = 3
7. Const ordNTWin = 4

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Const ordNTChar = 5
9. Const ordDOSUnknown = 7
10. Const ordNotExe = 0
11. Const errNoFile = -1
12. Const errOS2_2 = -2
13. Const errWinOS2DLL = -3
14. Const errNEUnknown = -4
15. Const errNTNonIntel = -5
16. Const errNTDLL = -6
17. Add the following code to the Form_Load event for Form1:
18. Private Sub Form_Load()
19. Text1.TEXT = ""
20. End Sub
21. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

22. Add a Command Button control to Form1. Command1 is created by default.

23. Add the following code to the Click event for Command1:
24. Private Sub Command1_Click()
25. If Text1.TEXT = "" Then
26. Exit Sub
27. End If
28. Dim X As Integer
29. Dim FileToCheck As String
30. FileToCheck = Text1.TEXT
31. X = ExeType(FileToCheck)
32. If X = errNoFile Then
33. MsgBox "File does not exist", 16, "Error"
34. Exit Sub
35. End If
36. Select Case X
37. Case ordMSDOS
38. MsgBox "File is MSDOS EXE file", 16, "OK"
39. Case ordWindows
40. MsgBox "File is a Windows file", 16, "OK"
41. Case ordOS2_1
42. MsgBox "File is OS/2 1.x file", 16, "OK"
43. Case ordNTWin
44. MsgBox "File is NT Windows file", 16, "OK"
45. Case ordNTChar
46. MsgBox "File is NT character file", 16, "OK"
47. Case ordDOSUnknown
48. MsgBox "File is probably DOS extended file", 16, "OK"
49. Case ordNotExe
50. MsgBox "File is not MSDOS EXE file", 16, "OK"
51. Case errOS2_2
52. MsgBox "File is OS/2 LE file", 16, "OK"
53. Case errWinOS2DLL
54. MsgBox "File is a DLL executable but not by us", 16, "OK"
55. Case errNEUnknown
56. MsgBox "File is unknown NE system", 16, "OK"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


57. Case errNTNonIntel
58. MsgBox "File is unknown type - perhaps a RISC file", 16, "OK"
59. Case errNTDLL
60. MsgBox "File is executable, but not by us", 16, "OK"
61. End Select
62. End Sub
63. Create a new function called ExeType. Add the following code to this function:
64. Function ExeType(sSpec As String) As Integer
65. 'Check specified file to see if it is an
66. 'executable file. If it is, what kind is it?
67. Dim sNullChr As String
68. sNullChr = Chr$(0)
69. Dim hFile As Integer
70. hFile = FreeFile
71. 'Make sure the file exists on disk
72. Dim F As String
73. F = Dir$(sSpec)
74. If F = "" Then
75. ExeType = errNoFile
76. Exit Function
77. Else
78. Open sSpec For Binary Access Read Shared As hFile
79. End If
80. Dim sHeader As String * 128
81. Get hFile, 1, sHeader
82. 'MSDOS headers start with magic header "MZ"
83. Dim sMagic As String * 2
84. sMagic = Mid$(sHeader, 1, 2)
85. If sMagic <> "MZ" Then
86. 'Could still be a .BAT, .CMD, .PIF, or .COM file
87. 'but that's not our problem here
88. ExeType = ordNotExe
89. Exit Function
90. End If
91. 'Make an integer (Long to prevent overflows) out
92. 'of offset &H18 and &H19 and then see if offset
93. 'points beyond DOS header. If not, file is MSDOS
94. 'EXE. Since Basic strings are 1-based rather than
95. '0-based, all hex offsets into file must be
96. 'incremented by one.
97. Dim iData As Long
98. iData = Asc(Mid$(sHeader, &H20, 1)) * 256
99. iData = iData + Asc(Mid$(sHeader, &H19, 1)) + 1
100. If iData < &H40 Then
101. ExeType = ordMSDOS
102. Exit Function
103. End If
104. 'Get the offset of new .EXE header
105. iData = Asc(Mid$(sHeader, &H3E, 1)) * 256
106. iData = iData + Asc(Mid$(sHeader, &H3D, 1)) + 1
107. Get hFile, iData, sHeader
108. Close hFile
109. 'New .EXE headers start with magic header "NE"
110. Dim sMagic2 As String * 2
111. Dim sZero As String * 2

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


112. sMagic = Mid$(sHeader, 1, 2)
113. sMagic2 = Mid$(sHeader, 3, 2)
114. sZero = sNullChr & sNullChr
115. 'Check for Windows/OS2 format
116. If sMagic = "NE" Then
117. 'Get the executable file flags to check for DLL
118. iData = Asc(Mid$(sHeader, &HE, 1))
119. If iData And &H80 Then
120. 'This is a DLL (executable but not by us)
121. ExeType = errWinOS2DLL
122. Else
123. 'Get the operating system flags (byte, not word)
124. iData = Asc(Mid$(sHeader, &H37, 1))
125. If iData And &H2 Then
126. ExeType ordWindows 'Windows
127. ElseIf iData And &H1 Then
128. ExeType = ordOS2_1 'OS2 1.x
129. Else
130. ExeType = errNEUnknown 'Unknown NE system
131. End If
132. End If
133. 'Check for OS/2 2.x format (cannot execute from Windows or NT)
134. ElseIf sMagic = "LE" Then
135. ExeType = errOS2_2 'OS/2 LE
136. 'Check for NT format
137. ElseIf sMagic = "PE" And sMagic2 = sZero Then
138. 'Get processor flags
139. iData = Asc(Mid$(sHeader, &H5, 1))
140. Select Case iData
141. Case &H4C, &H4D, &H4E, &H4F 'NT for intel 386, 486, 586, 686
142. ExeType = ordNTWin 'NT Windows
143. Case Else
144. ExeType = errNTNonIntel 'Some sort of RISC or other
145. Exit Function
146. End Select
147. 'Get the EXE type flags
148. iData = Asc(Mid$(sHeader, &H18, 1))
149. If iData And &H20 Then
150. ExeType = errNTDLL 'executable, but not by us
151. Exit Function
152. End If
153. 'Get the subsystem flags to identify NT character
154. iData = Asc(Mid$(sHeader, &H5D, 1))
155. If iData = 3 Then ExeType = ordNTChar
156. 'Could also identify Posix files here
157. Else
158. 'This is an MSDOS file with a header, but it's not
159. 'an NE file. Many 16-bit DOS-extended executables fall
160. 'through here. It could also be a non-EXE file that
161. 'just happens to have "MZ" as its first two bytes.
162. ExeType = ordDOSUnknown 'probably DOS extended
163. End If
164. End Function
Run the example program by pressing F5. Type the name of a file in the Text Box and
click the command button. A message box will identify what type of file it is.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


132: Preventing Duplicate Items
from Being Added to List Box Controls
July 1, 1995

Abstract
You can maintain a list of items in the Microsoft® Visual Basic® List Box control. This
article explains how to add new items to the List Box control by first checking to see if
the entry already exists in the list.

Using SendMessage to Search for Duplicate Items


When developing an application in Microsoft® Visual Basic®, you can use the List Box
control to create and maintain a list of items. To add new items to the list, you use the
AddItem method. The AddItem method does not automatically report that a duplicate
item already exists in the List Box control—you must verify this before you actually add
the new item to the list.
You can search a List Box control for a specific item by using the Microsoft Windows®
application programming interface (API) SendMessage function. SendMessage allows
you to send a message to the operating system. In this case, you want to tell
SendMessage to issue an LB_FINDSTRING message to the List Box control.

The LB_FINDSTRING message lets you search a List Box control for an entry that
matches the target string. The first argument to this message defines the type of search
you want to perform. You need to specify a value of zero to begin the search operation at
the first entry in the List Box control. The second argument to the LB_FINDSTRING
message is a NULL-terminated string that is the actual item you want to search for.
If the LB_FINDSTRING message returns a value of –1 (minus 1), you know that the
target string was not found in the List Box control. You can then use the AddItem
method to add the new item to the List Box control. If the item already exists in the list,
however, you can simply display a message box or perform some other procedure to
inform the user that a duplicate entry already exists in the List Box control.

Example Program
This program shows how to determine if a List Box control already contains the item you
are trying to add to the control.

1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function SendMessageFind Lib "user32" Alias "SendMessageA"
4. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer, ByVal
5. lParam As String) As Long
6. Const WM_USER = &H400
7. Const LB_ERR = (-1)
8. Const LB_FINDSTRING = &H18F
9. Add the following code to the Form_Load event for Form1:
10. Private Sub Form_Load()
11. List1.AddItem "Item #1"
12. List1.AddItem "Item #2"
13. List1.AddItem "Item #3"
14. List1.AddItem "Item #4"
15. End Sub
16. Add a Text Box control to Form1. Text1 is created by default.

17. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Duplicate".

18. Add the following code to the Click event for Command1:
19. Private Sub Command1_Click()
20. CheckForDupes
21. End Sub
22. Create a new function called CheckForDupes. Add the following code to this
function:
23. Sub CheckForDupes()
24. Dim Ret As Long
25. Dim A As String
26. A = Text1.TEXT
27. Ret = SendMessageFind(List1.hwnd, LB_FINDSTRING, 0, (A))
28. If Ret = LB_ERR Then
29. List1.AddItem Text1.TEXT
30. Else
31. List1.ListIndex = Ret
32. MsgBox "Duplicate entry - cannot add to List Box", 16, "Error"
33. End If
34. End Sub
Run the example program by pressing F5. The List Box control has five items in it. Type
a new entry in the Text Box control. Click the Duplicate command button. The program
searches the List Box control for the entry you typed in the Text Box control. If the entry
was not found, the program adds it to the List Box control. Alternatively, if the entry
already exists in the List Box control, a message box is displayed informing you of this
fact.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


133: Using the SendMessage Function to
Scroll Contents of Text Box Controls
July 1, 1995

Abstract
In Microsoft® Visual Basic®, you can programmatically scroll through the contents of a
Text Box control without actually waiting for your user to click the Scroll Bar control.
You can do this by sending one of the scrolling messages to the system with the
Microsoft Windows® application programming interface (API) SendMessage function.

Scrolling Through a Text Box Control


In the example program below, the code attached to the Left command button scrolls the
contents of the Text Box control to the left by one character position. You accomplish
this by sending a WM_HSCROLL message to the Text Box control. When a user clicks
the horizontal scroll bar in the Text Box, this message is sent to the window. In this
program, however, you send the message when the user clicks the command button.
To control the direction of the scrolling action, you must tell the Microsoft® Windows®
application programming interface (API) SendMessage function to send a
WM_HSCROLL message to the Text Box control with a directional argument in the
wParam argument. The following shows the valid settings that you can be specify for the
wParam argument.
SB_LEFT Scroll to the left all the way
SB_RIGHT Scroll to the right all the way
SB_LINELEFT Scroll left one unit
SB_LINERIGHT Scroll right one unit

As you can see from the list above, to scroll the contents of the Text Box left by one
character, you set the wParam argument to SB_LINELEFT. To scroll the contents of the
Text Box right by one character, you set the wParam argument to SB_LINERIGHT.

Example Program
This program shows how you can scroll the contents of a Text Box control by using the
SendMessage function.

1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
4. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer,
5. ByVal lParam As Long) As Long
6. Const SB_LINERIGHT = 1
7. Const SB_LINELEFT = 0
8. Const WM_HSCROLL = &H114
9. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True. Set its ScrollBars property to 3-Both.

10. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Left".

11. Add the following code to the Click event for Command1:
12. Private Sub Command1_Click()
13. Dim X As Long
14. X = SendMessage(Text1.hwnd, WM_HSCROLL, SB_LINELEFT, ByVal 0&)
15. End Sub
16. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Right".

17. Add the following code to the Click event for Command2:
18. Private Sub Command2_Click()
19. Dim X As Long
20. X = SendMessage(Text1.hwnd, WM_HSCROLL, SB_LINERIGHT, ByVal 0&)
21. End Sub
Run the example program by pressing F5. Type some text into the Text Box control.
Click the Left command button. The text scrolls to the left by one character position.
Click the Right command button. The text scrolls to the right by one character position.

134: Creating Temporary Files


July 1, 1995

Abstract
When developing an application in Microsoft® Visual Basic®, you may need to create a
temporary file on disk. This article explains how to create temporary files in Visual Basic
version 4.0.

Using the GetTempFileName Function


You can create a new file on a specified disk drive using the Microsoft® Windows®
application programming interface (API) GetTempFileName function. Although the file

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


you create is referred to as a temporary file, you are responsible for physically deleting
the file from disk when you no longer need it.
To use the GetTempFileName function in Microsoft® Visual Basic®, you must include
the following Declare statement in your program (note that this Declare statement must
be typed as a single line of code):
Private Declare Function GetTempFileName Lib "kernel32" Alias "GetTempFileNameA"
(ByVal lpszPath As String, ByVal lpPrefixString As String, ByVal wUnique As
Long, ByVal lpTempFileName As String) As Long
The GetTempFileName function requires four arguments. The first argument must be
set to the drive and/or path where you want to create the new file. In the example
program shown below, the new file is created in the root directory on drive C.
The second argument required by the GetTempFileName function is the prefix you want
to assign to the file name. In other words, if you specify the prefix as being "TEST", the
function will create the first four letters of the new file name set to "TEST".
The third argument to the GetTempFileName function should be set to zero. This tells
the function to automatically generate a random number for the file name. This random
number is then appended to the prefix string to create a unique and complete file name.
The fourth argument to the GetTempFileName function requires a string buffer of at
least 256 characters in length to hold the temporary file's name.
After you call this function, the new file is created on the specified disk. It is important to
note that the file is not deleted when you quit your application—you must physically
delete the file from disk.

Example Program
This program shows how to create a temporary file from within your Visual Basic
application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that this Declare statement must be typed as a single line of code):
3. Private Declare Function GetTempFileName Lib "kernel32" Alias "GetTempFileNameA"
4. (ByVal lpszPath As String, ByVal lpPrefixString As String, ByVal wUnique As
5. Long, ByVal lpTempFileName As String) As Long
6. Add the following code to the Form_Load event for Form1:
7. Private Sub Form_Load()
8. Text1.TEXT = ""
9. End Sub
10. Add a Text Box control to Form1. Text1 is created by default.

11. Add a Command Button control to Form1. Command1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


12. Add the following code to the Click event for Command1:
13. Private Sub Command1_Click()
14. Dim FilePrefix As String
15. Dim NewFile As String * 256
16. FilePrefix = "TEST"
17. NewFile = GetTempName(FilePrefix)
18. Text1.TEXT = NewFile
19. End Sub
20. Create a new function called GetTempName. Add the following code to this
function:
21. Private Function GetTempName(TmpFilePrefix As String) As String
22. Dim TempFileName As String * 256
23. Dim X As Long
24. Dim DriveName As String
25. DriveName = "c:\"
26. X = GetTempFileName(DriveName, TmpFilePrefix, 0, TempFileName)
27. GetTempName = Left$(TempFileName, InStr(TempFileName, Chr(0)) - 1)
28. End Function
Run the example program by pressing F5. Click the command button to create a new
temporary file on drive C in the root directory. The name of the newly created file is
displayed in the Text Box control.

135: Preventing a User from


Editing the Contents of a Text Box
Control
July 1, 1995

Abstract
The Text Box control in Microsoft® Visual Basic® lets your user type text that can later
be used within your program. Alternatively, you may want to display some text but do
not want the user to be able to edit that text. This article explains how to make a Text
Box control's contents read-only.

Making a Text Box Control Read-Only


When developing a program in Microsoft® Visual Basic®, you may want to display
some data in a Text Box control. If the MultiLine property is set to True, the text will
automatically wrap to the next line. In addition, if the ScrollBars property of the Text
Box control is set to 3-Both (or 1-Vertical or 2-Horizontal), your user can scroll through
the control's contents.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


However, you might not want your user to be able to edit the text that is stored in the
Text Box control. You can set the contents of a Text Box control to read-only status by
using the Microsoft Windows® programming application interface (API) SendMessage
function.
The SendMessage function can be used to send an EM_SETREADONLY message to the
Text Box control. This makes the Text Box control read-only.

To use the SendMessage function within your program, include the following Declare
statement in the General Declarations section of your project (note that this Declare
statement must be typed as a single line of code):
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer,
ByVal lParam As Long) As Long
The SendMessage function requires four arguments, as follows.
hwnd A long value containing the handle of the Text Box control
wMsg A long value containing the message to be sent, in this case
EM_SETREADONLY
wParam An integer value set to True to set the control's read-only flag, or False to
remove the control's read-only flag
lParam A long value that should be set to zero (not used by EM_SETREADONLY)

After the program runs the SendMessage function, a long value is returned, indicating
success (if the value is nonzero) or false (if the value is zero).

Example Program
This program shows how to prevent a user from editing the contents of a Text Box
control without disabling the control itself.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
4. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer,
5. ByVal lParam As Long) As Long
6. Const WM_USER = &H400
7. Const EM_SETREADONLY = (WM_USER + 31)
8. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True and its ScrollBars property to 3-Both.

9. Add a Command Button control to Form1. Command1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


10. Add the following code to the Click event for Command1:
11. Private Sub Command1_Click()
12. Dim RetVal As Long
13. RetVal = SendMessage(Text1.hwnd, EM_SETREADONLY, True, ByVal 0&)
14. End Sub
Run the example program by pressing F5. Type some text in the Text Box control.
Notice that you can use the horizontal and vertical scroll bars to scroll through the text.
You can also make changes to the text itself. Click the command button. You can still use
the scroll bars or cursor keys to navigate within the Text Box control, but you cannot edit
its contents.

136: Using Keyboard and Mouse


Shortcuts
July 1, 1995

Abstract
This article describes several undocumented keyboard and mouse features of Microsoft®
Visual Basic®.

Using Undocumented Keyboard and Mouse Techniques


• When you are in the Microsoft® Visual Basic® code window, you can click in
the left margin of the window to select a specific line, or you can double-click in
the left margin to select an entire function or procedure. Similarly, you can press
the CTRL key while clicking in the left margin to select all functions.

• When designing an application, you can move a control's position within the form
up, down, left, or right by holding down CTRL while pressing the appropriate
arrow key.

• When designing an application, you can manually adjust the size of a control by
one pixel by holding down the SHIFT key while pressing the appropriate arrow
key.

• In the code window, pressing CTRL+SHIFT+F2 moves the cursor back to its
previous location.

• A context-sensitive menu is displayed for the toolbar, toolbox, form, control, code
window, debug window, and project window when you right-click that object.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


137: Removing Items from a Drive
List Box Control
July 1, 1995

Abstract
The Drive List Box control in Microsoft® Visual Basic® displays a list of all disk drives
attached to the computer system. This article explains how you can use the Microsoft
Windows® application programming interface (API) SendMessage function to remove a
selected drive entry from the Drive List Box control at run time in Visual Basic.

Deleting an Entry from a Drive List Box Control


You can display a list of all installed disk drives in a Microsoft® Visual Basic®
application by using the Drive List Box control. This control displays each drive found
on the computer system in alphabetic order.

You can use the Drive List Box control to enable a user to easily switch to a different
disk drive. You do this by clicking a specific entry in the control. The current default disk
drive, however, is not actually changed—you must use the ChDir function to do this.
The Drive List Box control provides a simple method that you can employ to select the
actual disk drive. Typically, you would use the Drive List Box control in conjunction
with the File List Box control to create a file-access system of some sort.
The example program below shows how to remove the currently selected drive entry
from the Drive List Box control. You can accomplish this by using the Microsoft
Windows® application programming interface (API) SendMessage function to send a
CB_DELETESTRING message to that control.
First, however, you must determine which entry is currently selected in the Drive List
Box control. The ListIndex property of this control will return the index number of the
currently selected item. After you have retrieved this value, you use SendMessage to
send a CD_DELETESTRING message to the Drive List Box control. This message, in
turn, removes that specific entry from the control.

Example Program
This program shows how to remove a selected item from a Drive List Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Const WM_USER = &H400
4. Const CB_DELETESTRING = (WM_USER + 4)
5. Private Declare Function SendMessageAny Lib "user32" Alias "SendMessageA"
6. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer,
7. lParam As Any) As Long
8. Add a Drive List Box control to Form1. Drive1 is created by default.

9. Add a Command Button control to Form1. Command1 is created by default.

10. Add the following code to the Click event for Command1:
11. Private Sub Command1_Click()
12. Dim X As Long
13. X = SendMessageAny(Drive1.hwnd, CB_DELETESTRING, Drive1.ListIndex, 0)
14. End Sub
Run the example program by pressing F5. Select one of the drive letters shown in the
Drive List Box control. Click the command button. The selected entry is removed from
the Drive List Box control.

138: Enabling and Disabling Scroll Bars


in a List Box
July 1, 1995

Abstract
When you write a program in Visual Basic®, it may be necessary to temporarily enable
or disable the scroll bars in a List Box control. This article explains how to use the
Windows® application programming interface (API) GetSystemMetrics function with a
Picture Box control to enable or disable the scroll bars in a List Box control.

Using the GetSystemMetrics Function


You can use the Windows® application programming interface (API)
GetSystemMetrics function to retrieve the system metrics (for example, the width and
height) of several elements of the Windows environment. To use this function in your
Visual Basic® program, you must include the following Declare statement in the General
Declarations section of your form (note that this Declare statement must be typed as a
single line of code):
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long)
As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The GetSystemMetrics function requires only one argument—a long value specifying
the type of information you want to retrieve. The following table shows the constant
values you must use to obtain the system metrics of a specific element in the Windows
environment.
Const SM_CXSCREEN = 0 Width of screen.
Const SM_CYSCREEN = 1 Height of screen.
Const SM_CXFULLSCREEN = 16 Width of window client area.
Const SM_CYFULLSCREEN = 17 Height of window client area.
Const SM_CYMENU = 15 Height of menu.
Const SM_CYCAPTION = 4 Height of caption or title.
Const SM_CXFRAME = 32 Width of window frame.
Const SM_CYFRAME = 33 Height of window frame.
Const SM_CXHSCROLL = 21 Width of arrow bitmap on horizontal scroll bar.
Const SM_CYHSCROLL = 3 Height of arrow bitmap on horizontal scroll bar.
Const SM_CXVSCROLL = 2 Width of arrow bitmap on vertical scroll bar.
Const SM_CYVSCROLL = 20 Height of arrow bitmap on vertical scroll bar.
Const SM_CXSIZE = 30 Width of bitmaps in title bar.
Const SM_CYSIZE = 31 Height of bitmaps in title bar.
Const SM_CXCURSOR = 13 Width of cursor.
Const SM_CYCURSOR = 14 Height of cursor.
Const SM_CXBORDER = 5 Width of window frame that cannot be sized.
Const SM_CYBORDER = 6 Height of window frame that cannot be sized.

Const SM_CXDOUBLECLICK = Width of rectangle around the location of the first


36 click. The second click must occur in the same
rectangular location.
Const SM_CYDOUBLECLICK = Height of rectangle around the location of the first
37 click. The second click must occur in the same
rectangular location.
Const SM_CXDLGFRAME = 7 Width of dialog frame window.
Const SM_CYDLGFRAME = 8 Height of dialog frame window.
Const SM_CXICON = 11 Width of icon.
Const SM_CYICON = 12 Height of icon.
Const SM_CXICONSPACING = Width of rectangles the system uses to position tiled
38 icons.
Const SM_CYICONSPACING = Height of rectangles the system uses to position tiled
39 icons.
Const SM_CXMIN = 28 Minimum width of window.
Const SM_CYMIN = 29 Minimum height of window.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Const SM_CXMINTRACK = 34 Minimum tracking width of window.
Const SM_CYMINTRACK = 35 Minimum tracking height of window.
Const SM_CXHTHUMB = 10 Width of scroll box (thumb) on horizontal scroll bar.
Const SM_CYVTHUMB = 9 Width of scroll box (thumb) on vertical scroll bar.
Const SM_DBCSENABLED = Returns a nonzero if the current Windows version
42 uses double-byte characters, otherwise returns zero.
Const SM_DEBUG = 22 Returns nonzero if the Windows version is a
debugging version.
Const Alignment of pop-up menus. If zero, left side is
SM_MENUDROPALIGNMENT aligned with corresponding left side of menu bar item.
= 40 If nonzero, left side is aligned with right side of
corresponding menu bar item.
Const SM_MOUSEPRESENT = Nonzero if mouse hardware is installed.
19
Const SM_PENWINDOWS = 41 Handle of Pen Windows dynamic-link library (DLL)
if Pen Windows is installed.
Const SM_SWAPBUTTON = 23 Nonzero if the left and right mouse buttons are
swapped.

In a Visual Basic application, you can selectively enable or disable the scroll bars in a
List Box by first retrieving the width of the scroll bar's arrow bitmap. You do this by
calling the GetSystemMetrics function with the value SM_CXVSCROLL for the Index
argument. The width of the arrow bitmap is returned as a long value by the
GetSystemMetrics function.
Next, a Picture Box control must be positioned directly over the scroll bars on the
window or form in your project. The Width property of the Picture Box is set to the
width of the arrow bitmap. By setting the Visible property of the Picture Box control to
True, the user can employ the scroll bars in the usual manner. However, if you set the
Visible property of the Picture Box control to False, the user cannot see the scroll bars.
Therefore, the scroll bars become temporarily disabled.

Example Program
This program shows how to hide the scroll bars of a List Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Const SM_CXVSCROLL% = 2
4. Private Declare Function GetSystemMetrics Lib "user32"
5. (ByVal nIndex As Long) As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


6. Add the following code to the Form_Load event for Form1:
7. Private Sub Form_Load()
8. Dim WD As Long
9. List1.Visible = False
10. WD = GetSystemMetrics(SM_CXVSCROLL%)
11.
12. Picture1.Width = WD * Screen.TwipsPerPixelX
13. Picture1.Left = List1.Left + List1.Width - Picture1.Width
14.
15. Picture1.Top = List1.Top
16. Picture1.Height = List1.Height
17. Picture1.Line (0, 0)-Step(0, Picture1.Height)
18. List1.Visible = True
19.
20. List1.AddItem "Vancouver"
21. List1.AddItem "Seattle"
22. List1.AddItem "London"
23. List1.AddItem "Paris"
24. End Sub
25. Create a new procedure called ShowBar. Add the following code to this
procedure:
26. Sub ShowBar()
27. Picture1.Visible = False
28. End Sub
29. Create a new procedure called HideBar. Add the following code to this
procedure:
30. Sub HideBar()
31. Picture1.Visible = True
32. Picture1.Line (0, 0)-Step(0, Picture1.Height)
33. End Sub
34. Add a List Box control to Form1. List1 is created by default. Set the following
properties for List1:
Height = 450
Left = 2760
Top = 1800
Width = 1215
35. Add a Picture Box control to Form1. Picture1 is created by default. Set the
following properties for Picture1:

AutoRedraw = True
BorderStyle = 0-None
Height = 450
Left = 2760
Top = 1800
Visible = False
Width = 1215

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


36. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Show".

37. Add the following code to the Click event for Command1:
38. Private Sub Command1_Click()
39. ShowBar
40. End Sub
41. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Hide".

42. Add the following code to the Click event for Command2:
43. Private Sub Command2_Click()
44. HideBar
45. End Sub
Run the example program by pressing F5. Click the Hide command button. The scroll
bars on the List Box control are no longer visible, but you can still scroll through the
contents of the List Box with the arrow keys. Click the Show command button to show
the scroll bars on the List Box control.

139: Retrieving a Disk's Volume Label


July 1, 1995

Abstract
Each time you format a disk in MS-DOS®, you are given the opportunity to assign a
unique name (called a volume label) to that disk. This article explains how to retrieve a
disk's volume label in a Microsoft® Visual Basic® application.

Using Dir$ Function to Retrieve Only Specific File


Names
The Dir$ function in Microsoft® Visual Basic® can retrieve the name of any file stored
on disk. To search for a specific file on disk, you pass the file's name to the Dir$ function
as:
FileName = Dir$("C:\AUTOEXEC.BAT")
If the AUTOEXEC.BAT file is not found on drive C, the Dir$ function will return an
empty string; otherwise, the file's name is returned. You can, of course, also search for
files by specifying a wildcard file name, such as AUT*.*, to find the name of each file
that begins with the AUT prefix characters stored on disk.
When you create a new file under the MS-DOS® or Microsoft Windows® operating
systems, that file is assigned a file attribute. A file may have one or more of the following
attributes assigned to it.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Normal 0 Data can be read from or written to the file.
Read Only 1 Data can be read from the file but not written to the file.
Hidden 2 The file cannot be seen in the directory list.
System 4 The file is a system file and is used only by the operating system.
Volume Label 8 The special name given to the disk. Only one volume label can be
assigned to each disk.
Directory 16 The file is a subdirectory.
Archive 32 The file has been modified since backup was last performed.

You can use any of these numeric file attribute values in conjunction with the Dir$
function to retrieve specific types of files.
In the example program below, you want to display the volume label name for drive C.
To do this, you run the statement:
TempBuffer = Dir$("C:*.*", ATTR_VOLUME)
This tells Dir$ that you want to retrieve the file that has its volume label attribute set.
Because only one file on each disk can have a volume label at any given time, you need
to run this statement only once to retrieve the disk's name.

Example Program
This program shows how to retrieve a disk's volume label.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant to the General Declarations section of Form1:


3. Const ATTR_VOLUME = &H8
4. Add a Text Box control to Form1. Text1 is created by default.

5. Add a Command Button control to Form1. Command1 is created by default.

6. Add the following code to the Click event for Command1:


7. Private Sub Command1_Click()
8. Dim TempBuffer As String
9. TempBuffer = Dir$("C:*.*",ATTR_VOLUME)
10. Text1.Text = TempBuffer
11. End Sub
Run the example program by pressing F5. Click the command button. The program will
display the volume label for drive C in the Text Box control, if such a file does indeed
exist.

140: Identifying CD-ROM Drives

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


July 1, 1995

Abstract
Within your Visual Basic® application, you can determine whether an attached drive is
actually a CD-ROM drive. This article explains how you can identify a CD-ROM drive
in Visual Basic.

Finding All CD-ROM Drives Attached to the Computer


System
From within a Visual Basic® application program, you can determine whether a disk
drive is actually a CD-ROM drive. To do this, however, you need to use a special
dynamic-link library (DLL) called VBASM.DLL.
The VBASM.DLL dynamic-link library allows you to perform low-level routines that
Visual Basic itself cannot perform. Written entirely in assembly language, VBASM is
available in the Microsoft® Development Library. (See Additional References at the end
of this article.)
The example program below tests each possible disk drive from 0 through 25 (for a total
of 26 possible disk drives) to see whether that particular drive is a CD-ROM drive. The
program does this by calling a low-level Int 2Fh multiplex interrupt function.

Function 150Bh, Int 2Fh, tells you whether or not the specified disk drive is a valid CD-
ROM drive. To call this function, you set the AX register to 150Bh and the CX register to
the number of the disk drive you want to check. The function will return with the BX
register set to ADADh if the MSCDEX.EXE device driver (that is, the CD-ROM driver)
is installed, and the AX register is set to a nonzero value if the specified disk is a CD-
ROM drive. It is, therefore, simply a matter of testing each possible disk drive, from 0
through 25, to determine exactly how many CD-ROM drives are attached to the computer
system.

Example Program
This program tests each installed disk drive to determine whether it is a CD-ROM drive.
In addition, the drive letter of each CD-ROM drive is displayed in the Text Box along
with the total number of CD-ROM drives that the program found.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


5. Private Sub Command1_Click()
6. Dim I As Integer
7. Dim DriveType As Integer
8. Dim Drive As String * 2
9. Dim TotalCDDrives As Integer
10. Dim Regs As VBREGS
11.
12. TotalCDDrives = 0
13. Text1.Text = ""
14.
15. For I = 0 To 25
16. Regs.AX = &H150B
17. Regs.BX = &H0
18. Regs.CX = I
19. Call vbInterrupt(&H2F, Regs, Regs)
20. If (Regs.BX = &HADAD) Then
21. Debug.Print Regs.AX
22. If (Regs.AX <> 0) Then
23. TotalCDDrives = TotalCDDrives + 1
24. Text1.Text = Text1.Text & Chr$(I + 65) & " is a CD-ROM drive" &
25. Chr$(13) & Chr$(10)
26. End If
27. End If
28. Next I
29. If (TotalCDDrives = 0) Then
30. Text1.Text = Text1.Text & "No CD-ROM drives were found."
31. Else
32. Text1.Text = Text1.Text & Chr$(13) & Chr$(10) & Str$(TotalCDDrives) &
33. "CD-ROM drives were found."
34. End If
35.
36. End Sub
37. Retrieve the VBASM.DLL and VBASM.TXT files from the Development
Library. Copy the VBASM.DLL file to the \WINDOWS\SYSTEM directory.

38. Add a new BAS module to your project. Copy the VBASM.TXT file to this new
BAS module.
Run the example program by pressing F5. Click the command button. In the Text Box
control, the program will display the drive letter of each CD-ROM drive found installed
in the computer system, in addition to a total count of the number of attached CD-ROM
drives.

141: Searching a List Box Control for a


Partial Match
August 6, 1995

Abstract

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The List Box control in Microsoft® Visual Basic® lets you display a list of items to the
user. This article explains how you can search a List Box control for a specific entry by
specifying a partial search string.

Finding Items in a List Box Control


The List Box control can be used within a Microsoft® Visual Basic® program to
maintain a list of items. While your program is running, you can use the ListCount
property of the List Box control to determine how many items are stored in the list. Then,
using the ListCount value, you can search through the contents of a List Box control to
find a specific item.

It's easy to write a procedure in Visual Basic to selectively find items in a List Box
control. For example, if you want to search the List Box control for the item "oranges,"
you can use a For-Next loop to check each entry in the List Box control to see whether it
matches the target string. To do this, use the following code:
For X = 0 To Lst.ListCount -1
If Lst.List(X) = "oranges" Then
'we found an item that matches.
End If
Next X
The code routine above tells us whether the item "oranges" was found in the List Box
control, but what happens if we want to find a partial item in the control? Let's suppose
that each item in the list contains a phrase such as "apples and oranges." You want to find
the item that contains the word "oranges." The above routine will only return a match if
the entire string matches the word "oranges."
To work around this problem, we can use the Visual Basic InStr function to parse each
entry in the List Box control. The InStr function will return the location within the larger
string where the target string is found. To use this search technique, you still need to
include a For-Next loop to examine each entry in the List Box control; however, you can
also add code to call the InStr function to determine whether a specific portion of an
entry matches your target string.

Each time the InStr function finds the target string in an entry in the List Box control, it
returns the target string's position within the entry. Just use the ListIndex property of the
List Box control to retrieve the entry that matches your target string.

Example Program
This program shows how to search a List Box control for a partially matching string.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1.


3. Private Sub Form_Load()
4. List1.AddItem "Apples and oranges"
5. List1.AddItem "Bananas and grapes"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


6. List1.AddItem "Peaches and corn"
7. End Sub
8. Add a List Box control to Form1. List1 is created by default.

9. Add a Text Box control to Form1. Text1 is created by default.

10. Add a Command Button control to Form1. Command1 is created by default.

11. Add the following code to the Click event for Command1.
12. Private Sub Command1_Click()
13. GetPartialString
14. End Sub
15. Create a new procedure called GetPartialString. Add the following code to this
procedure.
16. Sub GetPartialString()
17. Dim LittleString As String
18. Dim Item As Integer
19.
20. LittleString = Text1.Text
21. Item = GetMatch(List1, LittleString)
22.
23. If Item = -1 Then
24. MsgBox "No such entry found in List Box"
25. Else
26. List1.ListIndex = Item%
27. End If
28. End Sub
29. Create a new procedure called GetMatch. Add the following code to this
procedure.
30. Function GetMatch(Lst As ListBox, ByVal SearchStr As String) As Integer
31. Dim X As Integer
32.
33. For X = 0 To Lst.ListCount - 1
34. If InStr(Lst.List(X), SearchStr) Then
35. GetMatch = X
36. Exit Function
37. End If
38. Next X
39.
40. GetMatch = -1 'no match
41. End Function
Run the demonstration program by pressing F5. Three items are displayed in the List
Box control. Type a word such as "corn" in the Text Box control and click the command
button. The program highlights the "Peaches and corn" entry in the List Box control
because the word "corn" was found in this entry. Type the word "turnip" in the Text Box
control. After you click the command button, a message box is displayed that tells you no
such item was found.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


142: Determining Whether the Operating
System Supports Long File Names
August 6, 1995

Abstract
While developing an application in Microsoft® Visual Basic®, you may need to allow
your user to use long file names. This article explains how you can determine whether the
operating system supports long file names.

Using Long File Names in Windows 95


The Microsoft® Windows® 95 operating system lets you use long file names when
creating directories and files. Under other operating systems, you might store your word
processing files in a directory called C:\DOCS. Under Windows 95, however, you can
specify the directory name as C:\Documents for Legal Department. Using long file names
such as this can make your file system easier to navigate.
To determine whether your current operating system supports long file names, you can
use the GetVolumeInformation function in Visual Basic®. To use this function, include
the following Declare statement in the General Declarations section of your project (note
that the Declare statement must be typed as a single line of code):
Private Declare Function GetVolumeInformation Lib "kernel32" Alias
"GetVolumeInformationA" (ByVal lpRootPathName As String, ByVal
lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Long,
lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long,
lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String,
ByVal nFileSystemNameSize As Long) As Long
The GetVolumeInformation function retrieves information about the disk and/or file
system used on the specified disk drive. This function requires eight arguments, as
follows.
lpRootPathName A string containing the name of the disk's root
directory.
lpVolumeNameBuffer A string that will hold the disk's volume name.
nVolumeNameSize A long value containing the size of
lpVolumeNameBuffer.
lpVolumeSerialNumber A long value that will hold the volume serial
number.
lpMaximumComponentLength A long value containing the maximum length of a
file name component.
lpFileSystemFlags A long value containing the maximum length of a
file name component. A combination of the
following flags is used:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


FS_CASE_IS_PRESERVED File system preserves the case of file names when
saved to disk.
FS_CASE_SENSITIVE File system supports case-sensitive file names.
FS_UNICODE_STORED_ON_DISK File system supports Unicode™ in file names.
FS_PERSISTENT_ACLS File system preserves and enforces access control
lists (ACLs).
lpFileSystemNameBuffer A string to hold the file system's name (FAT,
HPFS, or NTFS).
nFileSystemNameSize A long value containing the length of
lpFileSystemNameBuffer.

After the program calls the GetVolumeInformation function, a value of True is returned
if the function was successful and all information about the disk/file system was
retrieved. A value of False is returned if the function was not successful.
After you have executed the GetVolumeInformation function, it returns the maximum
component length of a file name. An ordinary MS-DOS® file name (such as
COMMAND.COM) consists of eight characters followed by three characters. In this
case, the value returned by GetVolumeInformation would be 8.3. If the operating
system supports long file names, the value returned will be 255, regardless of the actual
length of the file name.

Example Program
This program shows how you can retrieve the volume name of a disk, and how to
determine whether the operating system supports long file names.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that this Declare statement must be typed as a single line of text):
3. Private Declare Function GetVolumeInformation Lib "kernel32" Alias
4. "GetVolumeInformationA" (ByVal lpRootPathName As String, ByVal
5. lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Long,
6. lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long,
7. lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String,
8. ByVal nFileSystemNameSize As Long) As Long
9. Add a Text Box control to Form1. Text1 is created by default.

10. Add a second Text Box control to Form1. Text2 is created by default.

11. Add a Command Button control to Form1. Command1 is created by default.

12. Add the following code to the Click event for Command1.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


13. Private Sub Command1_Click()
14. Dim nRet As Long
15. Dim VolName As String
16. Dim VolSN As Long
17. Dim MaxCompLen As Long
18. Dim VolFlags As Long
19. Dim VolFileSys As String
20. VolName = Space$(256)
21. VolFileSys = Space$(256)
22. nRet = GetVolumeInformation("C:\", VolName, Len(VolName), VolSN,
23. MaxCompLen, VolFlags, VolFileSys, Len(VolFileSys))
24. text1.Text = VolName
25. If MaxCompLen = 255 Then
26. Text2.Text = "Long file names are supported"
27. Else
28. Text2.Text = "Long file names are NOT supported"
29. End If
30. End Sub
Run the example program by pressing F5. Click the command button. The disk's volume
name is displayed in the first text box. A message is displayed in the second text box if
long file names are supported.

143: Determining Whether the Windows


95 Taskbar Is Visible or Hidden
August 31, 1995

Abstract
The Microsoft® Windows® 95 taskbar allows you to easily launch Windows-based
programs and to determine which applications are currently running. The taskbar can also
display status information, such as the current time. This article will explain how you can
determine, from within a Visual Basic® program, whether the taskbar is visible or
hidden.

Customizing the Windows 95 Taskbar


The taskbar in Microsoft® Windows® 95 is an area of the screen that contains icons that
let you easily switch from one application to another, launch new applications, or display
status information.
You can easily customize certain aspects of the taskbar. From the Start menu, choose
Settings/Taskbar. Windows 95 will display the current settings for the taskbar. For
example, if you set the autohide option, the taskbar is always hidden from view. To see
the taskbar, move the mouse pointer over the area of the screen where the taskbar is
located. The taskbar will immediately appear.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


You can use the SHAppBarMessage function in Visual Basic® to determine whether the
taskbar is visible or hidden. To use the SHAppBarMessage function in your program,
you must include the following Declare statement in the General Declarations section of
your program (note that this Declare statement must be typed as a single line of code):
Private Declare Function SHAppBarMessage Lib "shell32.dll"
(ByVal dwMessage As Long, pData As APPBARDATA) As Long
The SHAppBarMessage function requires two arguments. The first argument identifies
the appbar message you want to send. The dwMessage argument may be set to one of the
following values.
ABM_ACTIVATE Notifies the system that an appbar has been activated
ABM_GETAUTOHIDEBAR Retrieves the handle of the autohide appbar
associated with a particular edge of the screen
ABM_GETSTATE Retrieves the autohide and always-on-top states of
the window's taskbar
ABM_GETTASKBARPOS Retrieves the bounding rectangle of the window's
taskbar
ABM_NEW Registers a new appbar and specifies the message
identifier that the system should use to send
notification messages to the appbar.
ABM_QUERYPOS Requests a size and screen position for an appbar
ABM_REMOVE Unregisters an appbar, removing bar from the
system's internal list
ABM_SETAUOTOHIDEBAR Registers or unregisters an autohide appbar for an
edge of the screen
ABM_SETPOS Sets the size and screen position of an appbar
ABM_WINDOWPOSCHANGED Notifies the system when an appbar's position has
changed

The second argument required by the SHAppBarMessage function is a pointer to an


APPBARDATA structure. The actual contents of this structure depend on the message
you send to the system. Because we are retrieving the state of the taskbar, we don't need
to set any of the fields in the APPBARDATA structure. Instead, we must set the
dwMessage argument to the constant value ABM_GETSTATE to retrieve the current
state of the taskbar.

After we have called the SHAppBarMessage function, a value is returned indicating the
state of the taskbar. If this value is zero, we know the taskbar is not in autohide mode or
always-on-top mode. If the value returned is &H1, the taskbar is in autohide mode; if the
value returned is &H2, the taskbar is in always-on-top mode.

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


This program shows how to determine whether the taskbar in Windows 95 is visible or
hidden.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function SHAppBarMessage Lib "shell32.dll"
4. (ByVal dwMessage As Long, pData As APPBARDATA) As Long
5. Const ABS_ALWAYSONTOP = &H2
6. Const ABS_AUTOHIDE = &H1
7. Const ABM_GETSTATE = &H4
8. Add a Command Button control to Form1. Command1 is created by default.

9. Add the following code to the Click event for Command1.


10. Private Sub Command1_Click()
11. Dim IsThere As Integer
12.
13. IsThere = BarExists()
14. If IsThere = 0 Then
15. Text1.Text = "TaskBar not in auto-hide or always-on-top mode"
16. End If
17. If IsThere = ABS_ALWAYSONTOP Then
18. Text1.Text = "TaskBar always-on-top"
19. End If
20. If IsThere = ABS_AUTOHIDE Then
21. Text1.Text = "TaskBar in auto-hide"
22. End If
23. End Sub
24. Create a new function called BarExists. Add the following code to this function.
25. Function BarExists() As Integer
26. Dim Bardata As APPBARDATA
27. BarExists = SHAppBarMessage(ABM_GETSTATE, Bardata)
28. End Function
29. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

30. Add a new module to the project. Module1.Bas is created by default.

31. Add the following TYPE structures to Module1.Bas:


32. Type RECT
33. Left As Long
34. Top As Long
35. Right As Long
36. Bottom As Long
37. End Type
38. Type APPBARDATA

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


39. cbSize As Long
40. hwnd As Long
41. uCallbackMessage As Long
42. uEdge As Long
43. rc As RECT
44. lParam As Long ' message specific
45. End Type
Run the example program by pressing F5. Click the command button. If the Windows 95
taskbar is visible, a message to that effect is displayed in the text box. If the taskbar is
hidden, however, the text box will display the text "Taskbar in auto-hide." If neither the
Auto-Hide nor the Always-on-Top property of the taskbar is set, then the text box will
display the message "Taskbar is not in auto-hide or always-on-top mode."

144: Using Accelerator Keys with the


TabStrip Control
August 31, 1995

Abstract
The TabStrip control in Microsoft® Visual Basic® does not provide built-in support for
using accelerator keys. This article explains how you can add this functionality to your
Visual Basic application.

Adding Accelerator Key Support to the TabStrip


Control
The TabStrip control in Microsoft® Visual Basic® version 4.0 allows you to present
information to your user in an organized manner. The TabStrip control allows you to
present, or query the user for, information relating to a single concept. For example, if
your user must choose certain ptions to customize his or her Visual Basic program, you
could present these options by using one tab of a TabStrip control. Then, on another tab,
you could ask the user for his or her name, company, and so on.

The user can select a tab either by pressing the TAB key to move the focus to the next tab
or by clicking the desired tab. Although there is no direct support provided when using
the TabStrip control, you can add accelerator keys to the TabStrip control. This will
allow your user to switch the focus between tabs by pressing and holding down the ALT
key and then pressing another key.

In the demonstration program below, the TabStrip control displays three tabs: Control,
Settings, and Parameters. Notice that an ampersand ("&") has been used in the tabs'
Caption property. At run time, the character immediately following the ampersand will
be underlined. In the example program, the Control tab will be shown with the letter "C"
underlined. Under the Microsoft Windows® operating system, the underlined character
tells users that they can press the underlined character while holding down the ALT key

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


to invoke that option. In this case, the user would press and hold down ALT and then
press C to move the focus to the Control tab.
You can add this functionality to your Visual Basic program by trapping each key that is
pressed on the keyboard. This is done by first determining the ASCII keycode value of
the key that was just pressed. If this key is one of the keys you want to trap (C, S, or P in
our example program below), set the Selected property of that tab to True. This moves
the focus to that individual tab. On the other hand, if the ALT key was not held down or
if the key is not one of the accelerator keys, no action is performed.

Example Program
This program shows how to add support for accelerator keys to the Visual Basic
TabStrip control.
1. Create a new project in Visual Basic. Form1 is created by default. Set its
KeyPreview property to True.

2. Add the following code to the Form_Load event for Form1.


3. Private Sub Form_Load()
4. TabStrip1.Tabs(1).Caption = "&Control"
5. TabStrip1.Tabs.Add 2, , "&Settings"
6. TabStrip1.Tabs.Add 3, , "&Parameters"
7. End Sub
8. Add the following code to the KeyDown event for Form1.
9. Private Sub Form_KeyDown(keycode As Integer, shift As Integer)
10. AccessKey$ = "CSP"
11. Code% = InStr(AccessKey$, Chr$(keycode))
12. If shift = vbAltMask And Code% Then
13. TabStrip1.Tabs(Code%).Selected = True
14. End If
15. End Sub
16. Add a TabStrip control to Form1. TabStrip1 is created by default.

Run the example program by pressing F5. The TabStrip control is displayed with three
tabs set—one for Control, one for Settings, and one for Parameters. You can move the
focus from one tab to another by pressing and holding down the ALT key and then
pressing the appropriate accelerator key (the underlined character) for the new tab.

145: Inserting Tab Characters in the


Rich-Text Box Control

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


August 31, 1995

Abstract
The Rich-Text Box control allows you to create rich-text format (RTF) documents from
within your Microsoft® Visual Basic® application. However, when you want to insert a
tab character in the Rich-Text Box control, the focus is instead moved to the next control
in the tab order specified by the TabIndex property. This article explains how you can
insert the tab character into the Rich-Text Box control itself.

Setting a Control's Tab Order


At run time, a Microsoft® Visual Basic® user must press CTRL+TAB to insert a tab
character in a Rich-Text Box control. However, most people are accustomed to pressing
the TAB key. Whenever the TAB key is pressed from within a Rich-Text Box control,
the focus is immediately set to the next control on the form. The TabIndex property of a
control determines which control then receives the focus. This is not the effect you want.
When designing a form in Visual Basic, you can add controls such as Command
Buttons and Text Boxes to perform functions within your application. Each time you add
a new control to a form, Visual Basic assigns a new value to that control. This value is
saved in the control's TabIndex property. At run time, a user can press the TAB key to
move the focus from one control to another. The focus is moved to the control that has
the next highest TabIndex value.
You can change the value of a control's TabIndex property either during design time or
at run time. However, the control must have a TabStop property associated with it. The
TabStop property determines whether a user can press the TAB key to set the focus to
that specific control.
In the example program below, the TabStop property of all controls on the form is set to
False. This prevents the user from setting the focus to another control by using the TAB
key—even though the Rich-Text Box control has the focus. In this way, the Tab control
character is correctly inserted into the text of the Rich-Text Box control.

Example Program
This program shows how to insert a tab control character in the Rich-Text Box control in
Visual Basic version 4.0.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Rich-Text Box control to Form1. RichTextBox1 is created by default.

3. Add the following code to the GotFocus event for RichTextBox1.


4. Private Sub RichTextBox1_GotFocus()
5. On Error Resume Next

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


6. For Each Control In Controls
7. Control.TabStop = False
8. Next Control
9. End Sub
10. Add a Command Button control to Form1. Command1 is created by default.
Run the example program by pressing F5. Note that the focus is set to the Rich-Text Box
control. Type some text into this control. You can press the TAB key to insert that control
character into the text you are typing whenever necessary. You should also note that
pressing TAB does not move the focus to the Command Button control—you must click
the Command Button itself to move the focus to it. In other words, while the focus is set
to the Rich-Text Box control, you can press the TAB key and that character is inserted
into the Rich-Text Box control.

146: Retrieving the Printer Name


from the Windows 95 Registry
August 31, 1995

Abstract
Microsoft® Windows® 95 uses the registry to determine which applications and
hardware items are installed in the computer system. This article explains how you can
retrieve the name of the default printer from the registry from within a Visual Basic®
application.

Manipulating the Windows 95 Registry in Visual Basic


The Microsoft® Windows® 95 registry is a database of information that contains
configuration details about the hardware and software installed in your computer system.
Under Windows version 3.1, this information was maintained through initialization (.INI)
files.
The registry is composed of keys. Each key may contain a specific value or other
subkeys, which in turn may contain values or other subkeys. You can examine or modify
the contents of the registration database by using the Microsoft Win32® registry
application programming interface (API) functions in a Visual Basic program or by using
the Registry Editor (REGEDIT).
The example program below shows how to use the Win32 registry API functions to
retrieve the default printer's name from the registry.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The first step is to call the RegOpenKeyEx function.This function opens the specified
key in the registration database. In this case, you want to open the subkey that is
associated with the printer. This subkey is stored in the registry as:
SYSTEM\Current Control Set\Control\Print\Printers\Default

You also need to tell the RegOpenKeyEx function that you want to work with the
Default subkey. After the program calls this function, a value that is set to zero is
returned if the function was successful.
The next step is to retrieve the actual value stored for the key that you are interrogating.
Because you want to retrieve the name that is assigned to the default printer, you should
call the RegQueryValueEx function. You must tell this function that you want to
retrieve the value that was given to the Default subkey.

Finally, you must call the RegCloseKey function to release the handle of the key that you
have been accessing in the registration database. This terminates access to the registration
database and frees the handle for future use by the computer system.

Example Program
This program shows how to retrieve the name of the default printer from the Windows 95
registry.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Private Declare Function RegOpenKeyEx Lib "advapi32" Alias "RegOpenKeyExA"
4. (ByVal hKey As Long, ByVal lpSubKey As String, ByVal dwReserved As Long,
5. ByVal samDesired As Long, phkResult As Long) As Long
6. Private Declare Function RegQueryValueEx Lib "advapi32" Alias "RegQueryValueExA"
7. (ByVal hKey As Long, ByVal lpValueName$, ByVal lpdwReserved As Long, lpdwType
8. As Long, lpData As Any, lpcbData As Long) As Long
9. Private Declare Function RegCloseKey Lib "advapi32" (ByVal hKey As Long) As Long
10. Const HKEY_CURRENT_CONFIG As Long = &H80000005
11. Add a Text Box control to Form1. Text1 is created by default.

12. Add a Command Button control to Form1. Command1 is created by default.

13. Add the following code to the Click event for Command1.
14. Private Sub Command1_Click()
15. Dim PName As String
16. PName = GetCurrPrinter()
17. Text1.Text = PName
18. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


19. Create a new procedure called GetCurrPrinter. Add the following code to this
procedure.
20. Function GetCurrPrinter() As String
21. GetCurrPrinter = RegGetString$(HKEY_CURRENT_CONFIG,
"System\CurrentControlSet\Control\Print\Printers", "Default")
22. End Function
23. Create a new procedure called RegGetString. Add the following code to this
procedure.
24. Function RegGetString$(hInKey As Long, ByVal subkey$, ByVal valname$)
25. Dim RetVal$, hSubKey As Long, dwType As Long, SZ As Long
26. Dim R As Long
27. RetVal$ = ""
28. Const KEY_ALL_ACCESS As Long = &HF0063
29. Const ERROR_SUCCESS As Long = 0
30. Const REG_SZ As Long = 1
31. R = RegOpenKeyEx(hInKey, subkey$, 0, KEY_ALL_ACCESS, hSubKey)
32. If R <> ERROR_SUCCESS Then GoTo Quit_Now
33. SZ = 256: v$ = String$(SZ, 0)
34. R = RegQueryValueEx(hSubKey, valname$, 0, dwType, ByVal v$, SZ)
35. If R = ERROR_SUCCESS And dwType = REG_SZ Then
36. RetVal$ = Left$(v$, SZ)
37. Else
38. RetVal$ = "--Not String--"
39. End If
40. If hInKey = 0 Then R = RegCloseKey(hSubKey)
41. Quit_Now:
42. RegGetString$ = RetVal$
43. End Function
Run the example program by pressing F5. When you click the Command Button
control, the name of your default printer is displayed in the Text Box control.

147: Retrieving Multiple File Names from


the Common Dialog Control
August 31, 1995

Abstract
The Common Dialog File control lets you easily select one or more files in your
Microsoft® Visual Basic® application. This article shows how you can retrieve the
names of selected files from the Common Dialog control.

Using the Common Dialog Control


The Common Dialog File control in Microsoft® Visual Basic® allows you to provide a
user with access to the directory and file structure of the hard disk from within an
application. For example, if a user needs to select a text file, you can display a Common

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Dialog File control box that allows that user to browse various directories until he or she
finds the needed file.
By setting the Flags property of the Common Dialog control to the constant value
OFN_ALLOWMULTISELECT, you can make it possible for your user to select several
files to work with. Multiple files can be selected by clicking each file name while
pressing and holding down SHIFT or CTRL. The selected file names are highlighted.

To enable your Visual Basic program to work with files selected by the user, you need to
retrieve each file name from the control's Filename property. The file names selected by
the user are all stored in this property as one long string. Each file name is separated by a
space (32) character.
You can use the InStr function to search for the delimiting space character to retrieve
each file name from the Filename property of the Common Dialog. The InStr function
returns the location of the space character within the Filename property string. After you
have obtained the location of the space character, you can use the Mid function to
remove the individual file name entry from the string.

Example Program
This program shows how to retrieve all file names selected in a Common Dialog File
control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Common Dialog control to Form1. CommonDialog1 is created by default.

3. Add a Text Box control to Form1. Text1 is created by default.

4. Add a second Text Box control to Form1. Text2 is created by default. Set its
MultiLine property to True.

5. Add a Command Button control to Form1. Command1 is created by default.

6. Add the following code to the Click event for Command1:


7. Private Sub Command1_Click()
8. Dim I As Integer
9. Dim Y As Integer
10. Dim Z As Integer
11. Dim FileNames$()
12.
13. Const OFN_ALLOWMULTISELECT = &H200&
14.
15. CommonDialog1.filename = ""
16. CommonDialog1.Filter = "All Files|*.*"
17. CommonDialog1.Flags = OFN_ALLOWMULTISELECT
18. CommonDialog1.Action = 1

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


19.
20. CommonDialog1.filename = CommonDialog1.filename & Chr(32)
21.
22. Z=1
23. For I = 1 To Len(CommonDialog1.filename)
24. I = InStr(Z, CommonDialog1.filename, Chr(32))
25. If I = 0 Then Exit For
26. ReDim Preserve FileNames(Y)
27. FileNames(Y) = Mid(CommonDialog1.filename, Z, I - Z)
28. Z=I+1
29. Y=Y+1
30. Next
31.
32. If Y = 1 Then
33. Text1.Text = FileNames(0)
34. Else
35. Text2.Text = ""
36. For I = 0 To Y - 1
37. If I = 0 Then
38. Text1.Text = FileNames(I)
39. Else
40. Text2.Text = Text2.Text & UCase(FileNames(I)) & Chr$(13) & Chr$(10)
41. End If
42. Next
43. End If
44. End Sub
Run the example program by pressing F5. Click the Command Button. The Common
Dialog Box File control will be displayed on the screen. Then, select several files from
the file list by clicking a file name while pressing and holding down SHIFT or CTRL.
After you have selected he appropriate file(s), click OK. The file names will be displayed
in the second Text Box control, and the directory name will be displayed in the first Text
Box control.

148: Using OLE Automation to


Check Spelling
August 31, 1995

Abstract
Microsoft® Word for Windows® includes a spelling checker that you can invoke from
within your Microsoft Visual Basic® application. This article shows how you can use
OLE Automation in a Visual Basic program to check the spelling of text.

Spell Checking Text in Visual Basic

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Within a Microsoft® Visual Basic® program, you can use Microsoft Word for
Windows® as an OLE Automation server to check the spelling in a Text Box control.
Your Visual Basic program can send commands to Microsoft Word, which in turn carries
out those commands. When the spelling checker has finished its work, the focus of
control returns to your Visual Basic program.

In the example program below, the contents of the Text Box control need to be checked
for spelling. To do this, you execute a CreateObject statement to tell Microsoft
Windows to run Microsoft Word. Next, you need to tell Word to create a new document
and to copy the text from the Text Box to this document. You accomplish both of these
functions by running the WordBasic FileNew and Insert commands, respectively.

After the text has been copied to the Word document, it can be checked for spelling. You
run the ToolsSpelling command in Microsoft Word to start the spelling checker.
When you have finished checking the document for spelling errors, click the OK
command button to close the spelling checker in Microsoft Word. Then, run the
EditSelectAll and FileExit commands to copy the newly revised text back to your Visual
Basic Text Box, which ends the OLE Automation process.

Example Program
This program shows how to use the spelling checker in Microsoft Word from within a
Visual Basic application.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. Dim WB As Object
7. Dim OldText As String
8. Dim NewText As String
9. Dim I As Integer
10. Dim CH As String * 1
11.
12. NewText = ""
13. On Error Resume Next
14. Set WB = CreateObject("Word.Basic")
15.
16. If Err Then
17. MsgBox Error$
18. Exit Sub
19. End If
20.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


21. WB.FileNew
22. WB.Insert Text1.Text
23. WB.ToolsSpelling
24. WB.EditSelectAll
25. OldText = WB.selection()
26. WB.FileExit 2
27.
28. For I = 1 To Len(OldText)
29. CH = Mid$(OldText, I, 1)
30. NewText = NewText + CH
31. If CH = Chr$(13) Then NewText = NewText + Chr$(10)
32. Next I
33.
34. Text1.Text = NewText
35. End Sub
Run the example program by pressing F5. Type some text in the Text Box control and
then click the Command Button control. Visual Basic runs Microsoft Word, copies the
text from the Text Box control to a new document, and invokes Word's spelling checker.
After you have finished checking the text, the newly revised text is copied back to the
Text Box control.

149: Enumerating Disk Drives in


Visual Basic 4.0
August 31, 1995

Abstract
When you write a program in Microsoft® Visual Basic®, you may need to determine
which disk drives are installed in the computer system. This article shows how to
enumerate all disk drives within Visual Basic version 4.0.

Using the GetLogicalDriveString Function


You can use the Microsoft® Windows® application programming interface (API)
GetLogicalDriveString function in a Microsoft Visual Basic® program to find out
which disk drives are available. To use this function, you must include the following
Declare statement in the General Declarations section of your program:
Private Declare Function GetLogicalDriveStrings Lib "kernel32" Alias
"GetLogicalDriveStringsA" (ByVal nBufferLength As Long,
ByVal lpBuffer As String) As Long
The GetLogicalDriveString function requires two arguments, as follows.
nBufferLength A long value containing the maximum size of the lpBuffer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


lpBuffer A string buffer that will hold the drive letters

After the program calls this function, the lpBuffer is filled with entries that describe each
valid disk drive found in the computer system. The string is null-terminated. Each entry
in this string contains the drive letter, followed by a colon and a backslash character. For
example, if drive A is found, the string will contain the entry:
NULL a:\ NULL NULL
Notice that each entry is terminated by a null byte, and the last entry in the string is
terminated by two consecutive null bytes.
The example program below displays a list of all available disk drives in the Text Box
control. The program uses the InStr and Mid functions to extract each individual entry
from the lpBuffer string.

Example Program
This program shows how to create a list of all disk drives installed in the computer
system.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that this Declare statement must be typed as a single line of code):
Private Declare Function GetLogicalDriveStrings Lib "kernel32" Alias
"GetLogicalDriveStringsA" (ByVal nBufferLength As Long,
ByVal lpBuffer As String) As Long
1. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

2. Add a Command Button control to Form1. Command1 is created by default.

3. Add the following code to the Click event for Command1:


Private Sub Command1_Click()
Dim X As Long
Dim L As Integer
Dim BufLen As Long
Dim BufString As String * 256
BufLen = 256

Text1.Text = ""
X = GetLogicalDriveStrings(BufLen, BufString)
On Error GoTo Quit_Now

Do
X=Y+1
Z=Z+1
Y = InStr(X, BufString, "\")

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


p$ = Mid$(BufString, Y - 2, 3)
Text1.Text = Text1.Text & p$ & Chr$(13) & Chr$(10)
Loop Until Y = 0

Quit_Now:

End Sub
Run the example program by pressing F5. Click the OK command button. The Text Box
will contain a list of all available disk drives in the computer system.

150: Determining How Many Items Can


Be Displayed in the List Box Control
September 5, 1995

Abstract
The List Box control in Microsoft® Visual Basic® allows you to display a list of items
to the user of your program. When you design a List Box control in a Visual Basic
program, you can use the Microsoft Windows® application programming interface (API)
SendMessage function to determine the size of individual items in the control. This
article explains how you can calculate the number of items that can be displayed in the
control, based on the size of each entry.

Determining How Many Entries Can Be Stored in a List


Box
The List Box control allows you to display a list of items to your user. For example, you
can store a list of vendors in a List Box control. When your Microsoft® Visual Basic®
program is running, you can add or remove entries from the vendor list.
When you design your application, you can set the initial size of the List Box control.
Usually you would make the List Box large enough to display several entries. At run
time, you can programmatically determine how many entries you can display at one time
in the control. To do this, you need to determine the height of an entry in the List Box
control by sending an LB_GETITEMHEIGHT message to the control.
Because you want to find the height of an entry in the List Box control, you use the
Microsoft Windows® application programming interface (API) SendMessage function
to send an LB_GETITEMHEIGHT message to that control. After the program calls the
function, the entry's height is returned as a long value. Then, to calculate how many
entries can be displayed in the List Box control, you must divide the height of the List
Box by the height of the entry. When making this calculation, you first need to account
for the height (which is one pixel) of the top and bottom borders of the List Box.
Therefore, you subtract two from the height of the List Box control. Next, you divide the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


height of the List Box by the height of the entry. The result of this calculation gives you
the number of items you can display in the List Box control.

Example Program
This program shows how to calculate the number of items that can be displayed in a List
Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
4. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer,
5. ByVal lParam As Long) As Long
6. Const LB_GETITEMHEIGHT = &H1A1
7. Add the following code to the Form_Load event for Form1 (note that the Items =
line must be typed as a single line of code):
8. Private Sub Form_Load()
9. Dim ItemHeight As Long
10. Dim Items As Integer
11.
12. ItemHeight = SendMessage(List1.hwnd, LB_GETITEMHEIGHT, 0, 0&)
13. Items = (List1.Height - 2 * Screen.TwipsPerPixelY)
14. / (Screen.TwipsPerPixelY * ItemHeight)
15. Text1.Text = "Maximum # entries : " & Str(Items)
16.
17. List1.AddItem "Item #1"
18. List1.AddItem "Item #2"
19. List1.AddItem "Item #3"
20.
21. End Sub
22. Add a Text Box control to Form1. Text1 is created by default.

23. Add a List Box control to Form1. List1 is created by default.

24. Add a Command Button control to Form1. Command1 is created by default.

25. Add the following code to the Click event for Command1 (note that the Items =
line must be typed as a single line of code):
26. Private Sub Command1_Click()
27. Dim ItemHeight As Long
28. Dim Items As Integer
29. Dim X As Integer
30.
31. ItemHeight = SendMessage(List1.hwnd, LB_GETITEMHEIGHT, 0, 0&)
32. Items = (List1.Height - 2 * Screen.TwipsPerPixelY)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


33. / (Screen.TwipsPerPixelY * ItemHeight)
34. X = List1.ListCount
35. X=X+1
36. If X <= Items Then
37. List1.AddItem "Item #" & Str(X)
38. End If
39. End Sub
Run the example program by pressing F5. Three items are displayed in the List Box
control. In addition, the Text Box control shows the maximum number of items that can
be displayed in the List Box control at any one time. Each time you click the command
button, you add a new item to the List Box control. However, if the number of items
stored in the List Box control equals the maximum number of items that can actually be
displayed, you can add no more new entries to the list.

151: Finding Whole Words in a Text Box


Control
September 5, 1995

Abstract
When you design a Microsoft® Visual Basic® application, you can add a Text Box
control to your project. The Text Box control lets a user type text that can be manipulated
by your program. This article explains how you can add a word-search function to your
program.

Using the InStr Function


The Text Box control provided in Microsoft® Visual Basic® acts like a miniature word-
processing program. As new text is typed, it is appended to the text that already exists in
the control. Your user can edit existing text or delete text that is no longer needed.
However, that is the extent of the word-processing capability of the Text Box. You can,
however, use the InStr function to add your own search function to find words in a Text
Box control.
In the example program below, you use a FindMatch function to search the Text Box
control for a specific word. A message box is displayed telling you whether or not the
target word was found.
You can use the InStr function to isolate a specific piece of text within a larger piece of
text. When the search finds a specific word match, the InStr function identifies the target
text.

It is a simple task to direct the InStr function to search for a particular word in the Text
Box control. Let's assume you want to see whether the word dog is in the string, "He
owns a cat and a dog". To do this, you tell InStr to search for the target word by issuing a
statement such as:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


X = InStr("He owns a cat and a dog", "dog")
Because the word dog actually exists in the sentence, InStr will report where it found the
string dog. In the example program below, you assume that a word is defined by a space
character, both before and after the word. However, if the string ends with a period
character, then the InStr function will not find the word dog. This is because that word is
actually the characters d-o-g-period.
Therefore, you must take punctuation characters into account when you write a word-
search function. In the example program below, you isolate each word that is surrounded
by space characters. In addition, you isolate words that end with a linefeed, carriage
return, comma, period, or space. This enables you to determine whether a word exists in
the Text Box control, regardless of punctuation that may or may not be appended to the
end of the word.

Example Program
This program shows how to search a Text Box control for whole words.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

3. Add a Label control to Form1. Label1 is created by default. Set its Caption
property to "Find word:".

4. Add a second Text Box control to Form1. Text2 is created by default.

5. Add a Command Button control to Form1. Command1 is created by default.

6. Add the following code to the Click event for Command1:


7. Private Sub Command1_Click()
8. Dim X As Integer
9. X = FindMatch(Text1.Text, Text2.Text)
10. If X = 0 Then
11. MsgBox "Word not found"
12. Else
13. MsgBox "Word found"
14. End If
15. End Sub
16. Create a new function called FindMatch. Add the following code to this
function:
17. Function FindMatch(Str1 As String, Str2 As String) As Integer
18. Dim Match As Integer
19. Dim Char1 As String
20. Dim Char2 As String
21.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


22. Match = InStr(Str1, Str2)
23.
24. If Match <> 0 Then
25. Char1 = Mid$(Str1, Match - 1, 1)
26. If Codes(Char1) Then
27. Char2 = Mid$(Str1, Match + Len(Str2), 1)
28. If Codes(Char2) Then
29. FindMatch = True: Exit Function
30. End If
31. End If
32. End If
33.
34. FindMatch = False
35. End Function
36. Create a new function called Codes. Add the following code to this function:
37. Function Codes(PuncStr As String) As Integer
38. If PuncStr = "," Or PuncStr = "." Or PuncStr = " " Or PuncStr = Chr(10) Or PuncStr = Chr(13)
Or PuncStr = Chr(9) Then
39. Codes = True
40. Else
41. Codes = False
42. End If
43. End Function
Run the example program by pressing F5. Type some text in the first Text Box control.
In the second Text Box control, type a word that you want to search for in the first Text
Box. Click the command button to execute the search routine. A message box is
displayed, telling you whether the target word (in Text2) was found in the Text Box
(Text1).

152: Determining the Status of Virtual


Send

Keys on the Keyboard


September 5, 1995

Abstract
Within a Microsoft® Visual Basic® application, you can determine the status of any
virtual key on the keyboard. This article explains how to retrieve and set the status of
virtual keys.

Toggling the Status of a Specific Key on the Keyboard

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


In a Microsoft® Visual Basic® application, you can use the GetKeyboardState function
to retrieve the current status of any key on the keyboard. To use this function in your
program, you must add the following Declare statement to the General Declarations
section of your form:
Private Declare Sub GetKeyboardStateByString Lib "user32" Alias
"GetKeyboardState" (ByVal pbKeyState As String)
The SetKeyboardState function is used to change the status of one or more keys on the
keyboard. Its Declare statement is as follows:
Private Declare Sub SetKeyboardStateByString Lib "user32" Alias
"SetKeyboardState" (ByVal lppbKeyState As String)
Both the GetKeyboardState and SetKeyboardState functions require one argument—a
buffer large enough to hold the status of all 256 virtual keys on the keyboard. Each byte
in this array corresponds to one virtual key. If a toggle key, such as NUM LOCK, is off,
the low-order bit of its status byte is 0. On the other hand, if the toggle key is on, the low-
order bit is 1. For other virtual keys, the key is down if the high-order bit is 1, and the key
is up if the high-order bit is 0.
You can use the example program below to toggle the state of the NUM LOCK key. You
do this by first calling the GetKeyboardState function to retrieve the status of all 256
virtual keys. A 256-byte string holds this information. Next, you isolate (using the Mid$
function) the byte that corresponds to the NUM LOCK key.

When you want to turn the NUM LOCK key on, you set its status to 1. Alternatively, if
you want to toggle the NUM LOCK key off, you set its status to 0.
The final step is to tell the operating system that you have changed the status of a virtual
key on the keyboard. You do this by calling the SetKeyboardState function, which
copies the new keyboard status array to the operating system. When the program carries
out this function, the status of the NUM LOCK key is immediately changed.

Example Program
This program shows how to turn the NUM LOCK key on and off.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Private Declare Sub GetKeyboardStateByString Lib "user32" Alias
4. "GetKeyboardState" (ByVal pbKeyState As String)
5. Private Declare Sub SetKeyboardStateByString Lib "user32" Alias
6. "SetKeyboardState" (ByVal lppbKeyState As String)
7. Const VK_NUMLOCK = &H90
8. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "On".

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


9. Add the following code to the Click event for Command1:
10. Private Sub Command1_Click()
11. Dim NumLockKey As String * 256
12. NumLockKey = Space$(256)
13. GetKeyboardStateByString (NumLockKey)
14. Mid$(NumLockKey, VK_NUMLOCK + 1, 1) = Chr$(1)
15. Call SetKeyboardStateByString(NumLockKey)
16. End Sub
17. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Off".

18. Add the following code to the Click event for Command2:
19. Private Sub Command2_Click()
20. Dim NumLockKey As String * 256
21. NumLockKey = Space$(256)
22. GetKeyboardStateByString (NumLockKey)
23. Mid$(NumLockKey, VK_NUMLOCK + 1, 1) = Chr$(0)
24. Call SetKeyboardStateByString(NumLockKey)
25. End Sub
Run the example program by pressing F5. Click the On command button to turn on the
NUM LOCK key. Click the Off command button to turn off the NUM LOCK key.

153: Searching PATH for Specific Files


September 5, 1995

Abstract
The MS-DOS® PATH statement tells the operating system to look for files in specific
directories on your disk. This article explains how to find out whether a specific file
exists in one of the PATH directories.

Searching for Files


When you write a program in Microsoft® Visual Basic®, you may need to determine if a
specific file exists on a disk drive. At installation time, many software packages modify
the MS-DOS® PATH statement in the AUOTOEXEC.BAT file by adding a new
directory to the existing PATH directories. This allows an application to find its own
system or data files that it requires to run successfully. You can add a search routine to
your program to search these directories for an individual file.

The first step is to retrieve the entire path for the specified disk drive. The Visual Basic
CurDir$ function returns the current disk drive's path.
Next, you need to call two Microsoft Windows® application programming interface
(API) functions, GetWindowsDirectory and GetSystemDirectory. The
GetWindowsDirectory function retrieves the path of the Windows directory. Windows

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


stores its initialization files, help files, application files, and other files in this directory.
The GetSystemDiectory function retrieves the path of the Windows system directory.
Windows stores library, font, drive, and other system files in this directory.
In the example program below, you use all three functions mentioned above to build a
string (PathStr) that contains the directory names. The IsFileInPath function simply uses
the InStr function to extract each individual directory name from PathStr. Then you use
the Dir$ function to determine whether the target file exists in that directory.

Example Program
This program shows how to determine whether a specific file exists in one of the
directories in the PATH statement.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statements to the General Declarations section of


Form1 (note that each Declare statement must be typed as a single line of code):
3. Private Declare Function GetSystemDirectory Lib "kernel32" Alias
4. "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long)
5. As Long
6. Private Declare Function GetWindowsDirectory Lib "kernel32" Alias
7. "GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long)
8. As Long
9. Add a Text Box control to Form1. Text1 is created by default.

10. Add a Command Button control to Form1. Command1 is created by default.

11. Add the following code to the Click event for Command1:
12. Private Sub Command1_Click()
13. Dim DirStr As String
14. Dim FileToFind As String
15. Dim Flag As Integer
16.
17. FileToFind = Text1.Text
18. Flag = IsFileInPath(FileToFind, DirStr)
19. If Flag Then
20. MsgBox "File Exists in: " & DirStr
21. Else
22. MsgBox "File does not exist in PATH"
23. End If
24. End Sub
25. Create a new function called BuildSearchPath. Add the following code to this
function:
26. Sub BuildSearchPath(PathStr As String)
27. Dim RetVal As Integer
28. Dim Buffer As String * 128
29.
30. PathStr = CurDir$

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


31. RetVal = GetWindowsDirectory(Buffer, 128)
32. PathStr = PathStr & ";" & Mid$(Buffer, 1, RetVal)
33. Buffer = Space(128)
34. RetVal = GetSystemDirectory(Buffer, 128)
35. PathStr = PathStr & ";" & Mid$(Buffer, 1, RetVal)
36. PathStr = PathStr & ";" & App.Path
37. PathStr = PathStr & ";" & Environ$("PATH")
38. End Sub
39. Create a new function called IsFileInPath. Add the following code to this
function:
40. Function IsFileInPath(TheFile As String, DirName As String) As Integer
41. Dim Separator As Integer
42. Dim SearchStr As String
43. Dim Results As String
44.
45. Call BuildSearchPath(SearchStr)
46.
47. While Len(SearchStr) <> 0
48. Separator = InStr(SearchStr, ";")
49. If Separator <> 0 Then
50. DirName = Mid$(SearchStr, 1, Separator - 1)
51. SearchStr = Mid$(SearchStr, Separator + 1)
52. Else
53. DirName = SearchStr
54. SearchStr = ""
55. End If
56.
57. Results = Dir$(DirName & "\" & TheFile)
58. If Results <> "" Then
59. IsFileInPath = True
60. Exit Function
61. End If
62. Wend
63. IsFileInPath = False
64. End Function
Run the example program by pressing F5. Type the name of a file that you want to find in
the Text Box control. Click the command button. A message box will be displayed,
telling you whether the file was found in one of the PATH directories.

154: Terminating Windows 95 in Visual


Basic
September 5, 1995

Abstract
In a Microsoft® Visual Basic® program, you can use a Microsoft Windows® application
programming interface (API) function to reboot the computer system in various ways.
This article explains how to quit Windows 95 and shut down the computer system.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Shutting Down the Computer System
You can use the Microsoft® Windows® application programming interface (API)
ExitWindowsEx function to reboot a computer system from within a Microsoft Visual
Basic® application. To use this function, include the following Declare statement in the
General Declarations section of your form:
Private Declare Function ExitWindowsEx Lib "user32" (ByVal uFlags
As Long, ByVal dwReserved As Long) As Long
The ExitWindowsEx function takes two arguments—the shutdown operation you want
performed, and a parameter that is not used. You can use one or more combinations of
the following flags to tell the ExitWindowsEx function how you want to perform the
shutdown procedure.
EWX_FORCE All processes all forced to terminate.
EWX_LOGOFF All processes are forced to terminate and the user is logged off.
EWX_POWEROFF The computer system is shut down and, if supported by the power-
off feature, the computer is physically turned off.
EWX_REBOOT The computer system is shut down and rebooted.
EWX_SHUTDOWN The computer system is shut down to where it is safe to physically
turn the power off.

In the example program below, you use a combination of three of the above flags. This
flag combination (EWX_LOGOFF, EWX_FORCE, and EWX_REBOOT) tells Windows
95 to quit all currently executing processes, log the user off network connections, and
leave the computer system ready for the user to turn off.

Example Program
This program shows how to shut down the computer system.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function ExitWindowsEx Lib "user32" (ByVal uFlags As Long,
4. ByVal dwReserved As Long) As Long
5. Const EWX_LOGOFF = 0
6. Const EWX_SHUTDOWN = 1
7. Const EWX_REBOOT = 2
8. Const EWX_FORCE = 4
9. Const EWX_POWEROFF = 8
10. Const EWX_RESET = EWX_LOGOFF + EWX_FORCE + EWX_REBOOT
11. Add a Command Button control to Form1. Command1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


12. Add the following code to the Click event for Command1:
13. Private Sub Command1_Click()
14. Dim X As Long
15. X = ExitWindowsEx(EWX_RESET, dwReserved)
16. End Sub
Run the example program by pressing F5. Click the command button to reboot the
computer system.

155: Scrolling Text Horizontally in a


Picture Box Control
September 5, 1995

Abstract
This article explains how to scroll text horizontally within a Picture Box control in
Microsoft® Visual Basic®.

Creating a Scrolling Marquee Effect in Visual Basic


You can add visual effects to your Microsoft® Visual Basic® applications that add
interest or grab the user's attention. You can do this, for example, by adding a scrolling
message to your program to create a marquee effect.
A scrolling message is a string of text that is continually displayed in a control. To
achieve this effect in your program, you can use the Timer and Picture Box controls.
The Print function lets you display a character in a Picture Box control. The character is
printed at the current x- and y-coordinates within the control. Therefore, to display an
entire string in the Picture Box, you first need to extract each character from the target
string you want to display. Then you can use the Print function to display that character
in the Picture Box control.
To make the text in the Picture Box scroll continuously, you simply keep track of your
position within the target string. When you reach the end of the string, set the pointer to
the beginning of the string. Using the Timer control, you can send (that is, print) a
character to the Picture Box at specific time intervals. This causes the message to print
continuously in the Picture Box control.

Example Program
This program shows how to scroll text horizontally in a Picture Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Picture Box control to Form1. Picture1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Add a Timer control to Form1. Timer1 is created by default. Set its Interval
property to 250.

4. Add the following code to the Timer1_Event:


5. Private Sub Timer1_Timer()
6. ShowMessage
7. End Sub
8. Create a new function called ShowMessage(). Add the following code to this
function:
9. Sub ShowMessage()
10. Static MsgPtr As Integer
11. Static MyText As String
12. If Len(MyText) = 0 Then
13. MsgPtr = 1
14. MyText = "Welcome to Visual Basic programming!"
15. End If
16. Picture1.Cls
17. Picture1.Print Mid$(MyText, MsgPtr); MyText;
18. MsgPtr = MsgPtr + 1
19. If MsgPtr > Len(MyText) Then
20. MsgPtr = 1
21. End If
22. End Sub
Run the example program by pressing F5. The "Welcome to Visual Basic programming!"
message will scroll across the Picture Box control continuously until you quit the
program.

156: Adding New Icons to the


Windows 95 Taskbar
December 5, 1995

Abstract
The Microsoft® Windows® 95 taskbar allows you to quickly switch between
applications, launch other applications by using the Start button, and perform many other
similar tasks. When developing your own Microsoft Visual Basic® applications, you can
also add new icons (that is, programs) to the Windows 95 taskbar. This article explains
how you can add icons to and remove icons from the notification area of the Windows 95
taskbar.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Using the Shell_NotifyIcon Function to Add Taskbar
Icons
On a typical desktop in the Microsoft® Windows® 95 operating system, you can see that
the Clock applet is running and that there are no additional icons on the taskbar—you see
only the Clock applet icon. After you run the example program described in this article,
the new icon is added to the taskbar. When the mouse pointer is placed on the taskbar,
you can see that the new icon has been added to the taskbar.

It's easy to add a new icon to the taskbar. The example program below shows how to add
a new icon to the taskbar, perform functions relevant to that new icon, and then remove
the icon from the taskbar. All this functionality is accomplished by using the
Shell_NotifyIcon function. You can use the Shell_NotifyIcon function in a Visual Basic
application to modify the Windows 95 taskbar. To use this function, you must include the
following Declare statement in the General Declarations section of your form:
Declare Function Shell_NotifyIcon Lib "shell32.dll" Alias "Shell_NotifyIconA"
(ByVal dwMessage As Long, lpData As NOTIFYICONDATA) As Long
The Shell_NotifyIcon function requires only two arguments. The first argument is one of
the following three messages you want to send to the Windows 95 taskbar:
NIM_ADD Add a new icon to the taskbar.
NIM_DELETE Remove (delete) an icon from the taskbar.
NIM_MODIFY Modify an existing icon on the taskbar.

In each case, either a True value is returned if the message was executed successfully or a
False value is returned if an error occurred in the attempt to process the message.
The second argument required by the Shell_NotifyIcon function is the address of a
NOTIFYICONDATA structure. This structure contains the information used by the
Shell_NotifyIcon function to modify the taskbar as specified. This structure must be
defined as follows:
Type NOTIFYICONDATA
cbSize As Long
hwnd As Long
uID As Long
uFlags As Long
uCallbackMessage As Long
hIcon As Long
szTip As String * pnTOOLTIP_SZ
End Type
where:
cbSize The size of the NOTIFYICONDATA structure itself.
hWnd The handle of the window that will receive the notification
messages associated with an icon on the taskbar.
wID An application-defined identifier of the taskbar icon.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


uFlags An array of flags indicating which of the other structure members
contain valid data. The uFlags argument can be a combination of
the following:
NIF_ICON hIcon is valid.
NIF_MESSAGE uCallbackMessage is valid.
NIF_TIP szTip is valid.
uCallbackMessage An application-defined message
identifier.

When a mouse event occurs over the icon, the identifier is used for notification messages
sent to the hWnd window:
hIcon The handle of the taskbar icon
szTip The text for the taskbar icon's tooltip

Note that the Shell_NotifyIcon function is used to send a particular message to the
system. The individual message you send to the taskbar adds a new icon, deletes an
existing icon, or modifies an existing icon.

You can add a new icon to the Windows 95 taskbar by sending a NIM_ADD message.
The newly added icon appears on either the right side or the bottom of the toolbar. If the
Show Clock option of the taskbar is selected, the new taskbar icon is set to the immediate
left of the Clock applet icon. Each time you add a new icon to the taskbar, any and all
existing taskbar icons are shifted one position to the left.

The NOTIFYICONDATA structure must be used when adding, deleting, or modifying


icons on the taskbar. This structure contains the information necessary for each particular
message you want to send. When adding new icons to the taskbar, for example, the
NOTIFYICONDATA structure must contain the handle of the new icon, the identifier
of the icon, and optionally, the text for the icon's tooltip. In addition, if your application
needs to receive mouse messages for the taskbar icon, then the NOTIFYICONDATA
structure must also include the identifier of the callback message that should be sent to
your application's window.
To process the incoming callback messages, however, you must use a subclassing control
such as Message Blaster. This third-party control will allow your Visual Basic
application to detect when the system has received a mouse message for your taskbar
icon. The wParam argument contains the identifier of the taskbar icon that received the
mouse message, and the lParam argument contains the actual message. This functionality
allows you to insert several icons on the taskbar from within a single Visual Basic
application. However, it is highly recommended that you not go overboard when adding
these new icons to the taskbar.
To add a new icon to the taskbar, you need to store information about the icon in the
NOTIFYICONDATA structure. In the example program below, you use the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


CreateNewIcon function to set the values in the NOTIFYICONDATA structure for
your new taskbar icon. This function is shown here:
Sub CreateNewIcon(OurWindow As Object, OurMsg As Long, OurToolTip As String)
On Error Resume Next
Dim X As Long
NewIcon.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
NewIcon.szTip = OurToolTip & Chr$(0)
NewIcon.hWnd = OurWindow.hWnd
NewIcon.uID = OurWindow.Icon
NewIcon.uCallbackMessage = OurMsg
NewIcon.hIcon = OurWindow.Icon
NewIcon.cbSize = Len(NewIcon)
X = Shell_NotifyIcon(NIM_ADD, NewIcon)
End Sub
In the routine shown above, notice that several steps have been taken before you actually
run the Shell_NotifyIcon function. The cbSize field of the NOTIFYICONDATA
structure is set to the actual size of the structure. This is not an optional step. You also tell
your application that you want to capture and later process all mouse messages received
by the taskbar icon of your application. In addition, you tell the Shell_NotifyIcon
function that you want to use the icon specified in the form's Icon property (this is the
icon you will see on the taskbar when the example program is run). You also tell
Shell_NotifyIcon which messages you need to process—in this case, you want your icon
to display a tooltip when the mouse pointer is moved over the icon, you want the icon
itself to be displayed on the taskbar, and you want to process incoming mouse messages.

Alternatively, when you want to remove an icon from the taskbar, you send a
NIM_DELETE message to your window. You do not need to modify the contents of the
NOTIFYICONDATA structure because that structure already contains the data used by
the program to add the new icon to the taskbar. After the NIM_DELETE message is
processed by the system, the icon is removed from the taskbar.

Example Program
This program shows how to add a new icon to the Windows 95 taskbar. In addition, it
shows how to remove the newly added icon and to receive callback messages, using the
Message Blaster custom control, from the new taskbar icon.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Set the following properties to the specified values for Form1:


BorderStyle: 2-Sizable
MaxButton: True
MinButton: True
ShowInTaskBar: True
WhatsThisHelp: False
WindowState: 0-Normal

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Select a new icon for this form (in the Icon property) to any icon that you want.
This is the icon that appears on the taskbar when you run the example program.
3. Add the following code to the Form_Load event for Form1:
4. Private Sub Form_Load()
5. On Error Resume Next
6. MessageBlaster1.hWndTarget = Me.hwnd
7. MessageBlaster1.AddMessage WM_USER, POSTPROCESS
8. CreateNewIcon Me, WM_USER, "My Little App"
9. End Sub
10. Add a Message Blaster control to Form1. MessageBlaster1 is created by default.

11. Add the following code to the MessageBlaster1_Message event (note that the
Private statement must be typed as a single line of code):
12. Private Sub MessageBlaster1_Message(ByVal hwnd As Long, ByVal Msg As Long,
13. wParam As Long, lParam As Long, nPassage As Integer, lReturnValue As Long)
14. On Error Resume Next
15. Select Case lParam
16. Case WM_LBUTTONDOWN
17. MsgBox "My Little App is running!", , App.Title
18. Case WM_RBUTTONDOWN
19. PopupMenu mnuMain, 0, , , mnuClose
20. Case WM_USER + 1
21. End
22. End Select
23. End Sub
24. From the Visual Basic Tools menu, select Menu Editor. Create a menu with the
following items:
Caption: &Main
Name: mnuMain
Caption: &Date
Name: mnuDate
Caption: &Time
Name: mnuTime
Caption: &Close
Name: mnuClose
25. Add the following code to the Click event for mnuClose:
26. Private Sub mnuClose_Click()
27. On Error Resume Next
28. DeleteOldIcon
29. SendMessage hwnd, WM_USER, 0, WM_USER + 1
30. End Sub
31. Add the following code to the Click event for mnuDate:
32. Private Sub mnuDate_Click()
33. MsgBox "Today is: " & Date, , App.Title

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


34. End Sub
35. Add the following code to the Click event mnuTime:
36. Private Sub mnuTime_Click()
37. MsgBox "Time is: " & Time, , App.Title
38. End Sub
39. From the Visual Basic Insert menu, select Module to create a new module.
Module1.Bas is created by default.

40. Add the following code to Module1.Bas (note that the Declare statement must be
typed as a single line of code):
41. Option Explicit
42.
43. Declare Function Shell_NotifyIcon Lib "shell32.dll" Alias "Shell_NotifyIconA"
44. (ByVal dwMessage As Long, lpData As NOTIFYICONDATA) As Long
45. Public Const NIF_ICON = &H2
46. Public Const NIF_MESSAGE = &H1
47. Public Const NIF_TIP = &H4
48. Public Const NIM_ADD = &H0
49. Public Const NIM_DELETE = &H2
50. Public Const MyToolTip As Integer = 64
51.
52. Type NOTIFYICONDATA
53. cbSize As Long
54. hWnd As Long
55. uID As Long
56. uFlags As Long
57. uCallbackMessage As Long
58. hIcon As Long
59. szTip As String * MyToolTip
60. End Type
61.
62. Public NewIcon As NOTIFYICONDATA
63. Create a new subroutine called CreateNewIcon. Add the following code to this
subroutine:
64. Sub CreateNewIcon(OurWindow As Object, OurMsg As Long, OurToolTip As String)
65. On Error Resume Next
66. Dim X As Long
67. NewIcon.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
68. NewIcon.szTip = OurToolTip & Chr$(0)
69. NewIcon.hWnd = OurWindow.hWnd
70. NewIcon.uID = OurWindow.Icon
71. NewIcon.uCallbackMessage = OurMsg
72. NewIcon.hIcon = OurWindow.Icon
73. NewIcon.cbSize = Len(NewIcon)
74. X = Shell_NotifyIcon(NIM_ADD, NewIcon)
75. End Sub
76. Create a new subroutine called DeleteIcon. Add the following code to this
subroutine:
77. Sub DeleteOldIcon()
78. On Error Resume Next

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


79. Dim X As Long
80. X = Shell_NotifyIcon(NIM_DELETE, NewIcon)
81. End Sub
82. Create a new routine called Main. Add the following code to this function:
83. Sub Main()
84. Load Form1
85. End Sub
86. From the Visual Basic Tools menu, select Options. From the Options windows,
select Project. Change the Startup Form to Sub Main. Click OK to save the
change to the project.
Run the example program by pressing F5. Notice the icon for the program is on the
taskbar. Move the cursor over this taskbar icon and the tooltip text "My Little App"
appears. When you use the left mouse button to click the icon, a message box appears
that tells you "My Little App is running!". Click the OK command button. When you
click the right mouse button on the icon, a pop-up menu appears, listing three options:
display the current date, display the current time, and close the application. Note that
when you close the application, the icon is removed from the Windows 95 taskbar.

157: Centering a Form on the


Screen
December 5, 1995

Abstract
This article explains how to center forms on the screen in your Microsoft® Visual
Basic® application.

Using the Height and Width Properties to Center Forms


You can add visual appeal to your Microsoft® Visual Basic® application by creating
well-designed forms. One such enhancement is the ability to center your forms both
horizontally and vertically on the screen.
Every form created in Visual Basic is set to a certain default size. You can change the
size of the form, at both design time and run time, by changing the form's Height and
Width properties.
In addition, you can use the Height and Width properties of the form to physically center
the form on the screen. Like forms, the screen has Height and Width properties, but
these are not normally changed.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To center a form horizontally on the screen, you need to calculate the difference between
the width of the screen and the width of the form, and then divide this result by two. This
gives you the horizontal position for the form.
Likewise, to center a form vertically on the screen, you calculate the difference between
the height of the screen and the height of the form, and then divide the result by two. This
gives you the vertical position for the form.

Once you have calculated where on the screen the form should be placed, you use the
Move method to position the form in the center of the screen.

Example Program
This program shows how to center a form on the screen.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Private Sub Form_Load()
4. CenterForm Form1
5. End Sub
6. Create a new subroutine called CenterForm. Add the following code to this
subroutine:
7. Sub CenterForm(frm As Form)
8. frm.Move (Screen.Width - frm.Width) \2, (Screen.Height - frm.Height) \2
9. End Sub
Run the example program by pressing F5. Form1 appears in the center of the screen.

158: Retrieving the Windows


Directory
December 5, 1995

Abstract
The Microsoft® Windows® directory contains such files as Windows-based application
files, initialization files, and Help files. This article explains how to retrieve the path of
the Windows directory from within your Microsoft Visual Basic® application.

Using the GetWindowsDirectory Function

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


From within a Microsoft® Visual Basic® application, you can determine the path of the
Microsoft Windows® directory. To do this, you use the Windows application
programming interface (API) GetWindowsDirectory function. You must include the
following Declare statement in the General Declarations section of your form:
Private Declare Function GetWindowsDirectory Lib "kernel32"
Alias "GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long)
As Long
The GetWindowsDirectory function requires two arguments: a buffer that will hold the
path of the directory after the function is called, and the length of the directory's buffer.
You must make sure that the buffer is long enough to hold the path—otherwise, an error
will occur.
After calling this function, the path of the Windows directory is stored in the lpBuffer
argument.

Example Program
This program shows how to retrieve the path of the Windows directory.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that the Declare statement must be typed as a single line of code):
3. Private Declare Function GetWindowsDirectory Lib "kernel32" Alias
4. "GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long)
5. As Long
6. Add a Text Box control to Form1. Text1 is created by default.

7. Add a Command Button control to Form1. Command1 is created by default.

8. Add the following code to the Click event for Command1:


9. Private Sub Command1_Click()
10. Dim DirName As String
11. DirName = GetWindowsDir()
12. text1.Text = DirName
13. End Sub
14. Create a new function called GetWindowsDir. Add the following code to this
function:
15. Function GetWindowsDir() As String
16. Dim Temp As String
17. Dim Ret As Long
18. Const MAX_LENGTH = 145
19.
20. Temp = String$(MAX_LENGTH, 0)
21. Ret = GetWindowsDirectory(Temp, MAX_LENGTH)
22. Temp = Left$(Temp, Ret)
23. If Temp <> "" And Right$(Temp, 1) <> "\" Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


24. GetWindowsDir = Temp & "\"
25. Else
26. GetWindowsDir = Temp
27. End If
28. End Function
Run the example program by pressing F5. The path of the Windows directory appears in
the Text Box control.

159: Setting the Minimum Size of a


Window
December 5, 1995

Abstract
In your Microsoft® Visual Basic® application, you can establish the absolute minimum
size for a form. This article explains how to allow a user to resize a form only if the form
is larger than the minimum size.

Allowing or Preventing the Resizing of Forms


When you design a Microsoft® Visual Basic® application, you add forms and controls to
your project. The form is actually a window that holds other controls such as Command
Button controls. You can modify the size of the form when you initially design your
program.
In addition, the user of your application may want the flexibility of resizing your
application's form. You can allow a form to be resized by setting the form's BorderStyle
property to Sizable. This is the default style for newly created forms.
The only drawback to letting the user resize your form is that it can destroy the layout of
your application. The user may resize the form to be larger or smaller, which may cover
up other windows that need to be visible.
In the example program below, the user can resize a form to make it larger, but not
smaller. The Height and Width properties of the form are set to an absolute minimum
size.
Each time the user tries to resize the form, the Form_Resize event is activated. In this
event, you check to make sure that the form is not minimized or iconized and then make a
test of the form's current size.

If the current height or width of the form is greater than the minimum size, you allow the
form to be resized. On the other hand, if the user tries to shrink the form's size to a much
smaller size, you prevent the change from taking place.

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


This program shows how to prevent a form (window) from being resized to a smaller
size.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Set the following properties for Form1:

Height = 2250
Left = 1260
Top = 1965
Width = 4515
3. Add the following code to the Form_Resize event for Form1:
4. Private Sub Form_Resize()
5. MIN_WIDTH = 4515
6. MIN_HEIGHT = 2250
7. If WindowState <> 1 Then
8. If Width < MIN_WIDTH Then Width = MIN_WIDTH
9. If Height < MIN_HEIGHT Then Height = MIN_HEIGHT
10. End If
11. End Sub
Run the example program by pressing F5. When you try to change the size of Form1 to
make it smaller, the code in the Resize event prevents the form's size from changing. You
can make the form larger, but not smaller.

160: Retrieving the Versions of MS-DOS


and Windows
December 5, 1995

Abstract
This article explains how to retrieve the version numbers of MS-DOS® and/or the
Microsoft® Windows® operating system installed on your computer system.

Using the GetVersion Function


There may be times when you need to retrieve the version of MS-DOS® and/or the
Microsoft® Windows® operating system installed on your computer system. This
information is useful when you need to perform a task and a check must be made to
ensure that the task is supported under MS-DOS or Windows.
The Windows application programming interface (API) GetVersion function can be used
to retrieve both the MS-DOS and the Windows versions installed on the computer
system. To use this function, you must add the following Declare statement to the
General Declarations section of your form:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Private Declare Function GetVersion Lib "kernel32" () As Long
After calling the GetVersion function, a long value is returned. This value contains the
major and minor version numbers of Windows in the low word. The high word contains
the major and minor version numbers of MS-DOS.
Once you have retrieved the version information, it is a simple matter to isolate the high
and low bytes of each word and convert these values to the version numbers.

Example Program
This program shows how to retrieve the version of MS-DOS and Windows installed in
the computer system.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1:
3. Private Declare Function GetVersion Lib "kernel32" () As Long
4. Add a Command Button control to Form1. Command1 is created by default.

5. Add the following code to the Click event for Command1:


6. Private Sub Command1_Click()
7. Dim WinMajor As Integer
8. Dim WinMinor As Integer
9. Dim DosMajor As Integer
10. Dim DosMinor As Integer
11. Dim RetLong As Long
12. Dim LoWord As Integer
13. Dim HiWord As Integer
14.
15. RetLong = GetVersion()
16. Call GetHiLoWord(RetLong, LoWord, HiWord)
17.
18. Call GetHiLoByte(LoWord, WinMajor, WinMinor)
19. Call GetHiLoByte(HiWord, DosMinor, DosMajor)
20.
21. Text1.Text = "Windows version:" & WinMajor & "." & WinMinor
22. Text2.Text = "DOS version:" & DosMajor & "." & DosMinor
23.
24. End Sub
25. Add a Text Box control to Form1. Text1 is created by default.

26. Add a second Text Box control to Form1. Text2 is created by default.

27. Create a new function called GetHiLoByte. Add the following code to this
function:
28. Sub GetHiLoByte(X As Integer, LoByte As Integer, HiByte As Integer)
29. LoByte = X And &HFF&

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


30. HiByte = X \ &H100
31. End Sub
32. Create a new function called GetHiLoWord. Add the following code to this
function:
33. Sub GetHiLoWord(X As Long, LoWord As Integer, HiWord As Integer)
34. LoWord = CInt(X And &HFFFF&)
35. HiWord = CInt(X \ &H10000)
36. End Sub
Run the example program by pressing F5. The major and minor version numbers of
Windows appear in the first Text Box control. The major and minor version numbers of
MS-DOS appear in the second Text Box control.

161: Forcing the Common Dialog Control


to Save Files to a Specific Disk Drive
December 5, 1995

Abstract
The Microsoft® Visual Basic® Common Dialog control allows you to display a Save As
dialog box in your Visual Basic application so that a user can save a file to disk. This
article explains how to prevent a user from saving files to a disk drive that is not installed
on the computer system.

Preventing the Selection of Nonexistent Disk Drives


The Microsoft® Visual Basic® Common Dialog control provides six dialog boxes that
you can use in your application. The Action property of the Common Dialog control
determines which of the six (Open Disk File, Save As, Color, Font, Print, and Help)
dialog boxes appears. One of these dialog boxes is the Save As dialog box. This dialog
box appears when you need to save a file to disk.
The Save As dialog box lets the user of your application type the name he or she wants to
assign to the file that is to be saved to disk. Note that the Common Dialog control does
not actually save the data to the file—it simply provides an easy way for the user to select
a file with which to work. The user can specify any disk drive, even one that is not
actually installed on the computer system. This "flexibility," however, may create
problems.

You can require that a user save the file to a specific disk drive by first testing for a drive
designation. The name of the file the user types is stored in the Common Dialog control's
Filename property. It is a simple matter to check the first letter of this string to determine
which disk drive the user wants to save the file to. You can then modify the drive
designation to suit your needs.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


In the example program below, you use the Ucase and Left functions provided in Visual
Basic to test the first character of the string stored in the Filename property. If this letter
is not the letter A, you know the user typed a different drive letter. In this case, an error
message appears indicating that the user must save the file to drive A only. If the user
types the correct disk drive letter, the program continues running.

Example Program
This program shows how to force the Common Dialog Save As dialog box to save a file
to a specific disk drive.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Common Dialog control to Form1. CommonDialog1 is created by default.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. Call Save_DriveA
7. End Sub
8. Add a Text Box control to Form1. Text1 is created by default.

9. Create a new function called Save_DriveA. Add the following code to this
function:
10. Function Save_DriveA()
11. Dim X As Integer
12.
13. Do
14. CommonDialog1.Action = 2 'save file
15. If UCase(Left(CommonDialog1.filename, 1)) <> "A" Then
16. MsgBox "You must save file to drive A only"
17. Else
18. Exit Do
19. End If
20. DoEvents
21. Loop
22.
23. X = FreeFile
24. Open CommonDialog1.filename For Output As #X
25. Print #X, Text1.Text
26. Close #X
27. MsgBox "File has been saved to drive A"
28.
29. End Function
Run the example program by pressing F5. Click the Command Button control. The
Common Dialog Save As dialog box appears on the screen. Type the name you want to
assign to the file, and click the Save button. If you specified a disk drive other than drive

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


A, a message box appears. You are then returned to the Common Dialog Save As dialog
box to type a new filename. You must type the filename by specifying drive A as the
destination disk drive. The program will then save the contents of the Text Box control to
the specified file.

162: Determining Whether a File Exists


December 5, 1995

Abstract
This article explains how to determine whether a file exists on a disk drive in a
Microsoft® Visual Basic® application.

Using the OpenFile Function


You can use the Microsoft® Windows® application programming interface (API)
OpenFile function in a Microsoft Visual Basic® application to determine whether a file
actually exists on a disk drive. To use this function, the following Declare statement
should be included in your project:
Private Declare Function OpenFile Lib "kernel32" (ByVal lpFileName As String,
lpReOpenBuff As OFSTRUCT, ByVal wStyle As Long) As Long
The OpenFile function requires three arguments. The first argument is a string
containing the full path of the file to test. The second argument is an OFSTRUCT
structure, which contains information about the file after the OpenFile function is called.
The third argument is the action you want the OpenFile function to take.

The third argument, wStyle, tells the OpenFile function the action that the function is to
perform. Because you want to find out whether a given file exists, you call the OpenFile
function with the wStyle argument set to OF_EXIST. If the file does not exist, the
OpenFile function will return an error code of 2—File Not Found.
When the OpenFile function is run, it writes information about the file to the
OFSTRUCT structure. Therefore, if an error occurs, you must retrieve the actual error
code from the OFSTRUCT structure itself. In the example program below, you use the
statement:
If OpenFileStructure.nErrCode = FILE_NOT_FOUND Then
After testing for the "File Not Found" error, you can indicate to the user whether or not
the file exists.

Example Program
This program shows how to determine whether a file already exists on the disk drive.
1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Declare statement to the General Declarations section of
Form1 (note that this Declare statement must be typed as a single line of text):
3. Private Declare Function OpenFile Lib "kernel32" (ByVal lpFileName As String,
4. lpReOpenBuff As OFSTRUCT, ByVal wStyle As Long) As Long
5. Add a Command Button control to Form1. Command1 is created by default.

6. Add the following code to the Click event for Command1:


7. Private Sub Command1_Click()
8. Dim TestFile As String
9. Dim Ret As Integer
10.
11. TestFile = "c:\auto.bat"
12. Ret = FileExists(TestFile)
13.
14. If Ret Then
15. MsgBox "File already exists"
16. Else
17. MsgBox "File does not exist"
18. End If
19. End Sub
20. Create a new function called FileExists. Add the following code to this function:
21. Function FileExists(FileName As String) As Integer
22. Dim RetCode As Integer
23. Dim OpenFileStructure As OFSTRUCT
24.
25. Const OF_EXIST = &H4000
26. Const FILE_NOT_FOUND = 2
27.
28. RetCode = OpenFile(FileName$, OpenFileStructure, OF_EXIST)
29. If OpenFileStructure.nErrCode = FILE_NOT_FOUND Then
30. FileExists = False
31. Else
32. FileExists = True
33. End If
34. End Function
35. From the Visual Basic Insert menu, select Module to create a new module.
Module1.Bas is created by default.

36. Add the following Type and Constant statements to Module1.Bas:


37. Public Const OFS_MAXPATHNAME = 128
38.
39. Type OFSTRUCT
40. cBytes As Byte
41. fFixedDisk As Byte
42. nErrCode As Integer
43. Reserved1 As Integer
44. Reserved2 As Integer
45. szPathName(OFS_MAXPATHNAME) As Byte
46. End Type

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Run the example program by pressing F5. Click the Command Button control. A
message box appears. If the file "C:\AUTO.BAT" exists on drive C, the message box
indicates that the target file does exist. If the file was not found on the disk, however, the
message box indicates that the file does not exist.

163: Emulating a Double-Click


Event in the Directory List Box Control
December 5, 1995

Abstract
This article explains how to allow the user of your Microsoft® Visual Basic® application
use the ENTER key, instead of double-clicking with the mouse, to select a directory.

Using the ENTER Key to Select Directories


The Microsoft® Visual Basic® Directory List Box control displays a list of all
directories stored on the specified disk drive. You can select a specific directory to work
with by double-clicking its entry in the control. The selected directory can then be
manipulated by your Visual Basic application.

You may, however, want to select a directory from the Directory List Box control by
pressing the ENTER key instead of double-clicking with the mouse. This functionality
can be accomplished by monitoring the KeyPress event of the Directory List Box
control.
Whenever a key is pressed on the keyboard, a KeyPress event is triggered in the control
that has the focus. A special number representing that particular key is stored in the
KeyPress event's KeyAscii variable. You can then test the KeyAscii variable to determine
whether a specific key, such as ENTER, was pressed on the keyboard.
In the example program below, each time a KeyPress event is triggered, the focus is set to
the Directory List Box control. The KeyAscii variable is then tested to determine
whether ENTER (represented by the number 13) was pressed. Next, a
WM_LBUTTONDBLCLK (double-click) message is sent to the Directory List Box
control by using the Microsoft Windows® application programming interface (API)
SendMessage function. The KeyAscii variable is then set to a value of zero, which
prevents the beep from being played on the computer's speaker. Finally, the default
directory is changed to the newly selected directory.

Example Program

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


This program shows how to emulate a double-click event in a Directory List Box control
with the ENTER key.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each statement must be typed as a single line of code):
3. Private Declare Function GetFocus Lib "user32" () As Long
4. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
5. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer,
6. ByVal lParam As Long) As Long
7. Const WM_LBUTTONDBLCLK = &H203
8. Add the following code to the Form_Load event for Form1:
9. Private Sub Form_Load()
10. Text1.Text = ""
11. Text1.Text = CurDir$
12. End Sub
13. Add a Text Box control to Form1. Text1 is created by default.

14. Add a Directory List Box control to Form1. Dir1 is created by default.

15. Add the following code to the Dir1_KeyPress event for Dir1:
16. Private Sub Dir1_KeyPress(KeyAscii As Integer)
17. Dim R As Long
18. Dim DirHwnd As Integer
19. Dim X As String
20. If KeyAscii = 13 Then
21. Dir1.SetFocus
22. DirHwnd = GetFocus()
23. R = SendMessage(DirHwnd, WM_LBUTTONDBLCLK, 0, 0)
24. KeyAscii = 0
25. End If
26. X = Dir1.Path
27. ChDir X
28. Text1.Text = CurDir$
29. End Sub
Run the example program by pressing F5. The Directory List Box control displays a list
of directories found on your hard drive. The name of the default directory appears in the
Text Box control.
Select a directory from the Directory List Box control by pressing the first letter of the
directory name or by clicking the directory name. Press ENTER. The currently selected
directory is now the default directory.

164: Copying Files from One Directory to


Another

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


December 5, 1995

Abstract
This article explains how to copy files from one directory to another in a Microsoft®
Visual Basic® application.

Using the FileCopy Statement


In a Microsoft® Visual Basic® application, you can use the FileCopy statement to copy
a file to a different directory and/or disk drive. The FileCopy statement requires two
arguments: the name of the file you want to copy, and the name to be given to the new
file. The target name can include the path of a directory or a specific disk drive.
However, note that the FileCopy statement does not generate any warning errors if the
target file already exists. In such situations, the target file overwrites the existing file.

Unfortunately, the FileCopy statement does not allow you to specify a wildcard source
filename. Using MS-DOS®, you could copy a group of files by issuing a command such
as:
COPY *.* C:\NEWFILES
This command tells MS-DOS to copy all the files in the current directory to the
NEWFILES directory on drive C.
To accomplish this same task in Visual Basic, you must use the Dir$ function to retrieve
the name of each individual file in the source directory. Then you use the FileCopy
statement to copy that individual file to the target directory.

A While-Wend routine can be used to quickly retrieve the names of all files in the target
directory. As shown in the CopyFile subroutine in the example program below, the Dir$
function returns the name of each file it finds. When Dir$ returns an empty text string
(""), you know that all files have been processed.

Example Program
This program shows how to copy all files from the source directory to the destination
directory.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Private Sub Form_Load()
4. text1.Text = ""
5. text2.Text = ""
6. End Sub
7. Add a Label control to Form1. Label1 is created by default. Set its Caption
property to "Source directory:".

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Add a second Label control to Form1. Label2 is created by default. Set its
Caption property to "Destination directory:". Position this Label control just
below Label1.

9. Add a Text Box control to Form1. Text1 is created by default. Position the Text
Box control so that it is directly adjacent to the first Label control.

10. Add a second Text Box control to Form1. Text2 is created by default. Position the
Text Box control so that it is directly adjacent to the second Label control.

11. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Copy Files".

12. Add the following code to the Click event for Command1:
13. Private Sub Command1_Click()
14. Dim SourceDir As String
15. Dim TargetDir As String
16. Dim X As Integer
17. Dim P As Integer
18.
19. SourceDir = text1.Text
20. TargetDir = text2.Text
21. CopyFile SourceDir, TargetDir, P
22. MsgBox "Number of files copied = " & Str$(P)
23. End Sub
24. Create a new subroutine called CopyFile. Add the following code to this
subroutine:
25. Sub CopyFile(SrcDir As String, TrgtDir As String, NumFiles As Integer)
26. Dim OldDir As String 'source dir name
27. Dim NewDir As String 'target dir name
28. Dim FileName As String 'source filename
29. Dim sType As String 'file type (extension)
30.
31. OldDir = SrcDir
32. If Right$(OldDir, 1) <> "\" Then
33. OldDir = OldDir & "\"
34. End If
35.
36. NewDir = TrgtDir
37. If Right$(NewDir, 1) <> "\" Then
38. NewDir = NewDir & "\"
39. End If
40.
41. NumFiles = 0 'returns # files copied
42.
43. FileName = Dir$(OldDir & "*.*")
44. While FileName <> ""
45. On Error Resume Next
46. FileCopy (OldDir & FileName), (NewDir & FileName)
47. If Err = 0 Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


48. NumFiles = NumFiles + 1
49. Else
50. Beep
51. MsgBox Error$, MB_ICONEXCLAMATION, ("Error copying file "
52. & FileName)
53. End If
54. On Error GoTo 0
55.
56. FileName = Dir$ 'get next matching file
57.
58. DoEvents 'allow processes to occur
59. Wend
60. End Sub
Run the example program by pressing F5. Type the name of the source directory (the
directory containing the files you want to copy) in the first Text Box control. Type the
name of the destination directory in the second Text Box control. Click the Copy Files
Command Button control. All files stored in the source directory are copied to the
destination directory. A message box then appears indicating how many files were
actually copied.

165: Locating CD-ROM Drives Installed


on a Computer System
December 5, 1995

Abstract
In a Microsoft® Visual Basic® application, you may need to determine what types of
disk drives are installed on a computer system. This article explains how to locate the
CD-ROM drives that are installed.

Using the GetDriveType Function


Occasionally, in a Microsoft® Visual Basic® application you may need to determine
whether a specific disk drive is a CD-ROM, removable, fixed, RAM, or network drive.
You can easily make this distinction by using the Microsoft Windows® application
programming interface (API) GetDriveType function.
The following Declare statement should be included in your project:
Private Declare Function GetDriveType Lib "kernel32" Alias "GetDriveTypeA"
(ByVal nDrive As String) As Long
To use the GetDriveType function, you need to tell it the name of the disk's root
directory. This should be in the format "c:\". After you call it, the GetDriveType
function returns a value that indicates the type of disk drive.
The value returned by the GetDriveType function may be one of the following:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Value Drive Type
0 Unknown media type
1 No such root directory exists
DRIVE_REMOVABLE Drive can be removed
DRIVE_FIXED Drive cannot be removed
DRIVE_REMOTE Network disk drive
DRIVE_CDROM CD-ROM disk drive
DRIVE_RAMDISK RAM disk drive

As you know, all disk drives are identified by an alphabetic letter, starting with the letter
A. The ASCII value for the letter A is 65. Because there are 26 possible disk drives, you
can use a For-Next loop to test each possible disk drive to determine whether it is a CD-
ROM disk drive.

Example Program
This program shows how to locate all CD-ROM disk drives installed on the computer
system.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that the Declare statement must be typed as a single line of code):
3. Private Declare Function GetDriveType Lib "kernel32" Alias "GetDriveTypeA"
4. (ByVal nDrive As String) As Long
5. Add a Command Button control to Form1. Command1 is created by default.

6. Add the following code to the Click event for Command1:


7. Private Sub Command1_Click()
8. Text1.Text = FindCDROM
9. End Sub
10. Add a Text Box control to form1. Text1 is created by default. Set its MultiLine
property to True.

11. Create a new function called FindCDROM. Add the following code to this
function:
12. Function FindCDROM() As String
13. Dim Drive As Integer
14. Const DRIVE_CDROM = 5
15. FindCDROM = "No CD_ROM Installed"
16.
17. For Drive = 65 To 90
18. If GetDriveType(Chr(Drive) & ":\") = DRIVE_CDROM Then
19. FindCDROM = "CD-ROM Drive " & Chr(Drive) & ":\"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


20. Exit For
21. End If
22. Next Drive
23. End Function
Run the example program by pressing F5. Click the Command Button control. A list of
all CD-ROM disk drives installed on the computer system appears in the Text Box
control.

166: Playing .WAV Files in Visual Basic


December 5, 1995

Abstract
This article explains how to play a waveform-audio (.WAV) file in your Microsoft®
Visual Basic® application.

Using the sndPlaySound Function


Adding sound to your Microsoft® Visual Basic® application is one of the ways you can
add more interest to your application. You can play a waveform-audio file by calling the
Microsoft Windows® application programming interface (API) sndPlaySound function.
This function's Declare statement is:
Private Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA"
(ByVal lpszSoundName As String, ByVal uFlags As Long) As Long
The first argument for the sndPlaySound function is a string containing the name of the
waveform-audio file. Alternatively, this string can contain an entry from the registry or
WIN.INI file.
The second argument for the sndPlaySound function specifies how you want the file to
be played. You can use one or a combination of the following values for this argument:
SND_ASYNC The function returns after immediately playing the file. The file is
played asynchronously.
SND_LOOP Used with SND_ASYNC, the file is played repeatedly until you
call the sndPlaySound function with the first argument set to
NULL.
SND_MEMORY The file to be played is stored in memory.
SND_NODEFAULT If the specified file cannot be found, the function returns. The
default sound file is not played.
SND_NOSTOP The function returns without playing the specified sound file if a
sound file is currently being played.
SND_SYNC The function does not return until the sound file has finished
playing.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


In the example program below, the TADA.WAV waveform-audio file is played. The
second argument to the sndPlaySound function tells the function to play the specified
sound file only, without playing the default sound.

Example Program
This program shows how to play a .WAV file in Visual Basic.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each statement must be typed as a single line of code):
3. Private Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA"
4. (ByVal lpszSoundName As String, ByVal uFlags As Long) As Long
5. Const SND_SYNC = &H0 ' play synchronously (default)
6. Const SND_NODEFAULT = &H2 ' silence not default, if sound not found
7. Add a Command Button control to Form1. Command1 is created by default.

8. Add the following code to the Click event for Command1:


9. Private Sub Command1_Click()
10. Dim X As Long
11. X = sndPlaySound("c:\windows\media\tada.wav", SND_SYNC Or SND_NODEFAULT)
12. End Sub
Run the example program by pressing F5. Click the Command Button control. The
TADA.WAV file is played.

167: Controlling the State of


Virtual Keys on the Keyboard
December 5, 1995

Abstract
This article explains how to control the state of any virtual key from within a Microsoft®
Visual Basic® application.

Retrieving and Setting the State of Virtual Keys


From within a Microsoft® Visual Basic® application, you can control the state of any
one of the 256 virtual keys on the keyboard. You do this by executing two Microsoft

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Windows® application programming interface (API) functions—GetKeyboardState and
SetKeyboardState.
In the example program below, you switch the state of both the CAPS LOCK and NUM
LOCK keys. To do this, you must first retrieve the state of these two toggle keys by
calling the GetKeyboardState function. After calling this function, the KeyboardBuffer
array that you have defined in the program contains the current state of every virtual key.

To isolate the CAPS LOCK and NUM LOCK keys, you must interrogate the bytes stored
in the KeyboardBuffer array. The constants VK_CAPITAL and VK_NUMLOCK
represent the CAPS LOCK and NUM LOCK keys, respectively. If the low-order bit in
the byte representing the toggle key is 1, then the key is on. If the low-order bit is 0, the
key is off.
You can use the Visual Basic logical AND operator to test the low-order bit for the toggle
keys, as shown here:
If KeyboardBuffer(VK_CAPITAL) And 1 Then
KeyboardBuffer(VK_CAPITAL) = 0
Else
KeyboardBuffer(VK_CAPITAL) = 1
End If
In the code fragment above, you first test the state of the CAPS LOCK key. If the CAPS
LOCK key is currently on (the low-order bit is 1), you reset the low-order bit to 0 to turn
the key off. If the CAPS LOCK key is off (the low-order bit is 0), you reset the low-order
bit to 1 to turn the key on.

When you want to reverse the state of the toggle keys (that is, turn the key on if it is not
engaged or off if it is engaged), you use the SetKeyboardState function. This function
modifies the state of any virtual key. The key you want to modify is again stored in the
KeyboardBuffer array.

Example Program
This program shows how to turn the toggle (CAPS LOCK and NUM LOCK) keys on and
off.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1:
3. Private Declare Sub GetKeyboardState Lib "user32" (lpKeyState As Any)
4. Private Declare Sub SetKeyboardState Lib "user32" (lpKeyState As Any)
5. Const VK_CAPITAL = &H14
6. Const VK_NUMLOCK = &H90
7. Add a Command Button control to Form1. Command1 is created by default.

8. Add the following code to the Click event for Command1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


9. Private Sub Command1_Click()
10. ReDim KeyboardBuffer(256) As Byte
11.
12. GetKeyboardState KeyboardBuffer(0)
13.
14. If KeyboardBuffer(VK_CAPITAL) And 1 Then
15. KeyboardBuffer(VK_CAPITAL) = 0
16. Else
17. KeyboardBuffer(VK_CAPITAL) = 1
18. End If
19.
20. If KeyboardBuffer(VK_NUMLOCK) And 1 Then
21. KeyboardBuffer(VK_NUMLOCK) = 0
22. Else
23. KeyboardBuffer(VK_NUMLOCK) = 1
24. End If
25.
26. SetKeyboardState KeyboardBuffer(0)
27. End Sub
Run the example program by pressing F5. Notice that both the CAPS LOCK and NUM
LOCK keys are off. Click the Command Button control. The CAPS LOCK and NUM
LOCK keys are both on. Each time you click the Command Button control, the state of
the CAPS LOCK and NUM LOCK keys is reversed.

168: Using the ShellExecute Function to


Print Files
December 5, 1995

Abstract
This article explains how to print a file specified by the user of your Microsoft Visual
Basic® application.

Printing a File from Within an Application


The Microsoft® Windows® application programming interface (API) ShellExecute
function can be used from within a Microsoft Visual Basic® application to print a file. In
addition, this function can be used to load an executable (.EXE) file.

To use the ShellExecute function, you must include the following Declare statement in
your project:
Private Declare Function ShellExecuteAny Lib "shell32.dll" Alias "ShellExecuteA"
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String,
ByVal lpParameters As Any, ByVal lpDirectory As Any, ByVal nShowCmd As Long)
As Long
The ShellExecute function requires six arguments, as follows:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


hWnd A long value that contains the window's handle.
LpOperation A string that specifies the operation that the ShellExecute function is to
perform. This string can be one of three values, as follows:
open Specifies that the file lpFile is to be
opened. In Microsoft Windows 95 this
file may be a Windows 95 folder.
print Specifies that the file lpFile is to be
printed.
explore Explores the folder for lpFile in
Windows 95.
LpFile A string containing the name of the file to open, print, or explore.
LpParameters Set to NULL if lpFile specifies a document file. If lpFile specifies an
executable file, then lpParameters is a pointer to a string specifying the
parameters that should be passed to the application.
LpDirectory A string specifying the default directory's name.
NShowCmd If a document file is specified in lpFile, this should be set to zero. If an
executable file is specified in lpFile, this determines how the
ShellExecute function displays the application after it is loaded. The
following values may be used:
SW_HIDE Hides the window and activates the
executable file.
SW_MAXIMIZE Maximizes the window.
SW_MINIMIZE Minimizes the window. The next top-
level window in the z-order is activated.
SW_RESTORE Activates the window even if it is hidden
or minimized.
SW_SHOW Activates the window and displays it in
its original size and at its original
position.
SW_SHOWMAXIMIZED Activates the window. The window is
displayed as maximized.
SW_SHOWMINIMIZED Activates the window. The window is
displayed as minimized.
SW_SHOWMINNOACTIVE Activates the window as minimized. The
active window retains the focus.
SW_SHOWNA Activates the window in its current state
but the active window retains the focus.
SW_SHOWNOACTIVATE Displays the window in its most recent
size and in its most recent position. The
active window retains the focus.
SW_SHOWNORMAL Displays the window in its original size
and at its original position.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


In the example program below, you retrieve the name of the file you want to print from
the Text Box control. Then you call the ShellExecute function with the lpFile argument
set to "print". Notice that for demonstration purposes, you set the nShowCmd argument to
SW_SHOWMINNOACTIVE. This argument lets you print the file to the printer without
actually having to make the program receive the focus. The actual application that prints
the document appears in a minimized window.

Example Program
This program shows how to use the ShellExecute function to print a Microsoft Word
document. The function can also print regular text files, such as those created by the
Windows 95 Notepad application.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function ShellExecuteAny Lib "shell32.dll" Alias "ShellExecuteA"
4. (ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String,
5. ByVal lpParameters As Any, ByVal lpDirectory As Any, ByVal nShowCmd As Long)
6. As Long
7. Const SW_SHOWMINNOACTIVE = 7
8. Add the following code to the Form_Load event for Form1:
9. Private Sub Form_Load()
10. Text1.Text = ""
11. End Sub
12. Add a Text Box control to Form1. Text1 is created by default.

13. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Print".

14. Add the following code to the Click event for Command1 (note that the "Ret ="
statement must be typed as a single line of code):
15. Private Sub Command1_Click()
16. Dim Ret As Long
17. Dim FileToPrint As String
18.
19. FileToPrint = Text1.Text
20. Ret = ShellExecuteAny(Me.hwnd, "print", FileToPrint, ByVal 0&, ByVal 0&,
21. SW_SHOWMINNOACTIVE)
22. End Sub
Run the example program by pressing F5. Type the name of a Microsoft Word document
or text file you want to print in the Text Box control. Click the Print command button to
run the application that originally created the document or text file. Notice that this

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


application remains loaded in the background, minimized, while it sends the file to the
printer.

169: Setting the Position and Size of the


Windows Help File
December 5, 1995

Abstract
This article explains how to set Microsoft® Windows® Help files (or the Help files of
any other application) to a specific position on the screen and to a specific size.

Using the WinHelp Function to Position and Size Help


Files
Designing a Help file for your Microsoft® Visual Basic® application is necessary if your
program is either complicated or fairly large. After you have created your Help file, you
can easily use the Microsoft Windows® application programming interface (API)
WinHelp function to display your application's Help file in the exact position and in the
exact size you want.

The WinHelp function runs the Windows Help application. This program lets you
specify the Help file you want to display to your user. The Declaration statement for the
WinHelp function is:
Declare Function WinHelp Lib "user32" Alias "WinHelpA" (ByVal hwnd As Long,
ByVal lpHelpFile As String, ByVal wCommand As Long, dwData As Any) As Long
The WinHelp function requires four arguments, as follows:
hWnd A long value containing the window's handle.
LpHelpFile A string containing the full path of the Help file to display.
Wcommand A long value that specifies the type of Help to display. This can be one of
the following values:
HELP_COMMAND Runs a Help macro or macro string. The
dwData argument is a string containing the
name of this macro.
*HELP_CONTENTS Displays the topic specified by the Contents
option in the [OPTIONS] section of the Help
file.
HELP_CONTEXT Displays the topic identified by the context
identifier defined in the [MAP] section of the
Help file. The dwData argument contains the
context identifier in a long unsigned integer.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


HELP_CONTEXTPOPUP Same as HELP_CONTEXT except that the
Help file appears in a pop-up window.
HELP_FORCEFILE Ensures that Windows displays the correct
Help file. The dwData argument must be set to
zero.
HELP_HELPONHELP Displays the WINHELP.HLP file. This file
explains how to use the Windows Help
system. The dwData argument must be set to
zero.
*HELP_INDEX Displays the index in the Help Topics dialog
box. The dwData argument must be set to
zero.
HELP_KEY Displays the topic in the keyword table that
matches the specified keyword. The dwData
argument contains the address of a keyword
string.
HELP_MULTIKEY Displays the topic in the alternative keyword
table that matches the specified keyword. The
dwData argument points to a
MULTIKEYHELP structure. This structure
specifies a keyword and a table footnote
character.
HELP_PARTIALKEY Displays the topic in the keyword table that
most closely matches the specified keyword.
The index tab appears if more than one match
is found. The index can be displayed by using
a pointer to an empty string. The dwData
argument must contain the address of a
keyword string.
HELP_QUIT Closes the Windows Help application,
providing that no other applications need it.
The dwData argument must be set to zero.
HELP_SETCONTENTS Displays the Contents topic, which is selected
when the user clicks the Contents button. The
dwData argument contains the context
identifier for the Contents topic in an unsigned
long integer.
HELP_SETINDEX Displays the specified keyword table in the
Index of the Help Topics dialog box. The
dwData argument contains the context
identifier for the Index topic in an unsigned
long integer.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


HELP_SETWINPOS Displays the Help window. The dwData
argument contains the address of a
HELPWININFO structure that specifies the
Help window's size and position.
DwData Set according to the wCommand argument.

*Note: New applications should use the HELP_FINDER value instead of this value.
This older command is provided for downward compatibility only.
In the list of options for the wCommand argument above, you can see that calling the
WinHelp function with the HELP_SETWINPOS value allows you to set the size and
position of your own Help window.

The HELP_SETWINPOS value displays a Windows Help file window, providing it is


loaded in memory and/or minimized. The exact position and size of the window is
specified in the HELPWININFO structure.
The HELPWININFO structure contains seven fields, as follows:
wStructSize A long value containing the size of the HELPWININFO structure,
specified in bytes.
X A long value containing the X-coordinate of the upper-left corner of the
window, specified in screen coordinates.
Y A long value containing the Y-coordinate of the upper-left corner of the
window, specified in screen coordinates.
Dx A long value containing the width of the window, specified in pixels.
dy A long value containing the length of the window, specified in pixels.
wMax A long value that specifies how the window is to be displayed. (See the
explanation for the nShowCmd argument in Tip #22 for these values.)
rgchMember A string containing the name of the window.

To show a Help window in a specific size, the dx and dy fields of the HELPWININFO
structure need to be defined. In the example program below, you set the values of these
two fields to 620 pixels each.
Likewise, you set the position of the Help window in the example program by setting the
X and Y fields of the HELPWININFO structure to 400 each.

Each time the program displays the Help window, the window appears at this new
position and in this new size.

Example Program
This program shows how to position the Windows Help file (or any other Help file) to a
specific location on the screen and in a specific size.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Form_Load event for Form1:


3. Private Sub Form_Click()
4. Dim Temp As Long
5. hwf.wStructSize = Len(hwf)
6. hwf.x = 400
7. hwf.y = 400
8. hwf.dx = 620
9. hwf.dy = 620
10. hwf.wMax = 1
11. Temp = WinHelp(Form1.hwnd, "c:\windows\winhelp.hlp", HELP_SETWINPOS, hwf)
12.
13. End Sub
14. From the Visual Basic Insert menu, select Module to create a new module.
Module1.Bas is created by default.

15. Add the following Constant, Type, and Declare statements to Module1.Bas
(note that the Declare statement must be typed as a single line of code):
16. Declare Function WinHelp Lib "user32" Alias "WinHelpA" (ByVal hwnd As Long,
17. ByVal lpHelpFile As String, ByVal wCommand As Long, dwData As Any) As Long
18. Type HELPWININFO
19. wStructSize As Long
20. x As Long
21. y As Long
22. dx As Long
23. dy As Long
24. wMax As Long
25. rgchMember As String * 2
26. End Type
27. Global Const HELP_SETWINPOS = &H203&
28. Global hwf As HELPWININFO
Run the example program by pressing F5. Form1 appears on the screen. Click the mouse
anywhere on the form to display the Help file for Windows. Notice that the window is
positioned in the lower-right corner of the screen and that the window's size has been set
to 620 x 620 pixels.

170: Positioning the Mouse Pointer over a


Specific Control
December 5, 1995

Abstract
This article explains how to position the mouse pointer over a specific control in a
Microsoft® Visual Basic® application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Positioning the Mouse Pointer
Sometimes you may need to position the mouse pointer over a specific control in a
Microsoft® Visual Basic® application, even though that control does not have the
current focus.

To position the mouse pointer over a specific control, you need to use the Microsoft
Windows® application programming interface (API) GetWindowRect and
SetCursorPos functions. The GetWindowRect function is used to retrieve the
coordinates of a control. The Declare statement for the GetWindowRect function is:
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect
As RECT) As Long
The GetWindowRect function requires two arguments. The first argument is the handle
of the control. The second argument is the address of a RECT structure.
After calling the GetWindowRect function, the control's coordinates are stored in the
RECT structure. The RECT structure is defined as:
Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Note that the left, top, right, and bottom positions of the control are stored in the RECT
structure. After you know the exact position of the control, you need to use the
SetCursorPos function to position the mouse pointer directly over the control. The
Declare statement for the SetCursorPos function is:
Private Declare Function SetCursorPos Lib "user32" (ByVal x As Long,
ByVal y As Long) As Long
Then, to position the mouse pointer over the control, you retrieve the coordinates of the
control's upper-left corner by using the values stored in the Left field and Top field of the
RECT structure. Next, you call the SetCursorPos function with these two values to
actually position the mouse pointer over the control.

Example Program
This program shows how to move the mouse pointer over a specific control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statements to the General Declarations section of


Form1 (note that each Declare statement must be typed as a single line of code):
3. Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect
4. As RECT) As Long
5. Private Declare Function SetCursorPos Lib "user32" (ByVal x As Long, ByVal y As
6. Long) As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


7. Add the following code to the Form_Load event for Form1:
8. Private Sub Form_Load()
9. Dim MousePos As RECT
10.
11. Call GetWindowRect(Command2.hwnd, MousePos)
12. Call SetCursorPos(MousePos.Left, MousePos.Top)
13. End Sub
14. Add a Command Button control to Form1. Command1 is created by default. Set
its Default property to False.

15. Add a second Command Button control to Form1. Command2 is created by


default. Set its Default property to True.

16. From the Visual Basic Insert menu, select Module to create a new module.
Module1.Bas is created by default.

17. Add the following Type structure to Module1.Bas:


18. Type RECT
19. Left As Long
20. Top As Long
21. Right As Long
22. Bottom As Long
23. End Type
Run the example program by pressing F5. Notice that the focus is set to the first
Command Button control, but the mouse pointer is positioned over the second
Command Button control. In short, the mouse pointer is placed over the control whose
Default property is set to True.

171: Determining RGB Color Values


December 5, 1995

Abstract
This article explains how to determine the red, green, and blue components for a specific
color in your Microsoft® Visual Basic® application.

Determining the RGB Components of a Specific Color


Using color in a Microsoft® Visual Basic® application adds visual appeal to your
program. The Visual Basic RGB function lets you tell the system what color you want to
use.
Each color used under the Microsoft Windows® operating system is actually made up
from a combination of the colors red, green, and blue. Depending on what value is
assigned to each of these components, one of many colors can be selected.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


However, you may need to determine the actual red, green, and blue values that
constitute a given color. The example program below uses the Mod operator to separate
the red, green, and blue components of the specified color. When you have these
individual values, you can then change them to suit your particular needs.

Example Program
This program shows how to separate the individual red, green, and blue components from
a given color.

1. Create a new project in Visual Basic, Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default. Set


its BackColor property to any color wanted.

3. Add the following code to the Click event for Command1 (note that the "MsgBox
=" statement must be typed as a single line of code):
4. Private Sub Command1_Click()
5. Dim C As Long
6. Dim Red As Integer
7. Dim Green As Integer
8. Dim Blue As Integer
9.
10. C = Command1.BackColor
11. Red = C Mod &H100
12. C = C \ &H100
13. Green = C Mod &H100
14. C = C \ &H100
15. Blue = C Mod &H100
16. MsgBox "Red = " & Str$(Red) & " Green: " & Str$(Green) & " Blue = "
17. & Str$(Blue)
18. End Sub
Run the example program by pressing F5. Click the Command Button control. A
message box appears indicating the individual red, green, and blue color values that
represent the button's BackColor property.

172: Extracting the Directory Name and


the Filename from the Path
December 5, 1995

Abstract
This article explains how to extract the directory name and the filename from a path
when working with files and directories in your Microsoft® Visual Basic® application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Using the Len, Mid$, and Right$ Functions
When working with files and directories in Microsoft® Visual Basic®, you may need to
isolate one or more elements from a full path. A path consists of the drive letter, directory
name, and filename. Each element of a path is separated by a backslash character (\).

When you need to extract the filename element from a complete path, you need to search
for the last backslash character in the path string. To do this, you must first calculate the
length of the path. This can be done using the Visual Basic Len function. The Len
function returns the number of characters found in the specified string.
When you know the actual length of the path string, you can check each character,
beginning with the last character in the path string, to see whether it is a backslash
character. The Visual Basic Mid$ function can be used to perform this character
comparison. When you finally locate the backslash character, you know that this signals
the beginning of the filename stored within the path. You then use the Visual Basic
Right$ function to extract the filename from the longer string.
This same technique can be used to extract the directory name from the path. In this case,
however, the comparison routine starts from the beginning of the path string.

Example Program
This program shows how to extract both the directory name and the filename from a path.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default.

3. Add a second Text Box control to Form1. Text2 is created by default.

4. Add a Command Button control to Form1. Command1 is created by default.

5. Add the following code to the Click event for Command1:


6. Private Sub Command1_Click()
7. Dim PathName As String
8.
9. PathName = "c:\eudora\wintips.exe"
10. Text1.Text = ExtractFileName(PathName)
11. Text2.Text = ExtractDirName(PathName)
12. End Sub
13. Create a new function called ExtractDirName. Add the following code to this
function:
14. Function ExtractDirName(PathName As String) As String
15. Dim X As Integer
16. For X = Len(PathName) To 1 Step -1
17. If Mid$(PathName, X, 1) = "\" Then Exit For

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


18. Next
19. ExtractDirName = Left$(PathName, X - 1)
20. End Function
21. Create a new function called ExtractFileName. Add the following code to this
function:
22. Function ExtractFileName(PathName As String) As String
23. Dim X As Integer
24. For X = Len(PathName) To 1 Step -1
25. If Mid$(PathName, X, 1) = "\" Then Exit For
26. Next
27. ExtractFileName = Right$(PathName, Len(PathName) - X)
28. End Function
Run the example program by pressing F5. Click the Command Button control. The
filename appears in the first Text Box control, and the directory name appears in the
second Text Box control.

173: Launching Applications in Visual


Basic
December 5, 1995

Abstract
This article explains how to control the way in which a launched Microsoft® Visual
Basic® application is run.

Using the CreateProcess Function to Launch


Applications
Under the Microsoft® Windows® 95 operating system, you can use the Windows
application programming interface (API) CreateProcess function to load and run any
application (or process) you want. Using this function, you have complete control over
how the launched application is run.
To use the CreateProcess function, add the following Declare statement to the General
Declarations section of your Microsoft Visual Basic® project or to a BAS module:
Declare Function CreateProcessA Lib "kernel32" (ByVal lpApplicationName As Long,
ByVal lpCommandLine As String, ByVal lpProcessAttributes As Long, ByVal
lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal
dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal
lpCurrentDirectory As Long, lpStartupInfo As STARTUPINFO,
lpProcessInformation As PROCESS_INFORMATION) As Long
As you can see, the CreateProcess function requires ten arguments, as follows:
lpApplicationName The name of the process you want to launch.
LpCommandLine The command line to be passed to the launched process.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


LpProcessAttributes Points to a SECURITY_ATTRIBUTES structure for the created
process.
LpThreadAttributes Points to a SECURITY_ATTRIBUTES structure for the
primary thread of the created process.
BInheritHandle If True, the created process inherits handles from the calling
application.
DwCreationFlags A combination of one or more creation flags for controlling the
priority class and the creation of the process.
LpEnvironment Points to an environment block for the new process. If set to
NULL, the new process uses the calling process's environment
block.
LpCurrentDirectory A string containing the drive and directory for the new process. If
NULL, the calling process's drive and directory are used.
LpStartupInfo A STARTUPINFO structure that specifies the appearance of the
main window for the new process.
LpProcessInformation A PROCESS_INFORMATION structure that receives
identification information about the new process.

The example program below launches the Windows 95 Notepad application. Note that
you specify the complete path to Notepad and launch the application as a normal process
(NORMAL_PRIORITY_CLASS).
After you call the CreateProcess function to launch the Notepad application, notice that
Notepad retains the focus. You cannot switch to another running application. This is
accomplished by executing the Windows API WaitForSingleObject function.
The WaitForSingleObject function forces the system to wait until a specific process has
finished its work. You pass the handle of the process you want to wait for and the length
of time, in milliseconds, to pause. In the example program below, the time-out value is
set to INFINITE, which means that the system will not resume running until the user has
quit Notepad.
The final step you must perform, after the user has quit Notepad, is to close the open
handle for the just-launched process. This removes all references to Notepad having been
launched.

Example Program
This program shows how to launch a Windows or MS-DOS® application from within
Microsoft Visual Basic. Control remains with the launched application until you quit that
application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Add the following code to the Click event for Command1:
4. Private Sub Command1_Click()
5. Dim AppToLaunch As String
6. AppToLaunch = "c:\windows\notepad.exe"
7. Call ExecuteAndWait(AppToLaunch)
8. End Sub
9. Create a new subroutine called ExecuteAndWait. Add the following code to this
subroutine:
10. Public Sub ExecuteAndWait(cmdline$)
11. Dim NameOfProc As PROCESS_INFORMATION
12. Dim NameStart As STARTUPINFO
13. Dim X As Long
14.
15. NameStart.cb = Len(NameStart)
16. X = CreateProcessA(0&, cmdline$, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS,
17. 0&, 0&, NameStart, NameOfProc)
18. X = WaitForSingleObject(NameOfProc.hProcess, INFINITE)
19. X = CloseHandle(NameOfProc.hProcess)
20. End Sub
21. From the Visual Basic Insert menu, select Module to add a new module to your
project. Module1.Bas is created by default.

22. Add the following code to Module1.Bas (note that each Declare statement must
be typed as a single line of code):
23. Type STARTUPINFO
24. cb As Long
25. lpReserved As String
26. lpDesktop As String
27. lpTitle As String
28. dwX As Long
29. dwY As Long
30. dwXSize As Long
31. dwYSize As Long
32. dwXCountChars As Long
33. dwYCountChars As Long
34. dwFillAttribute As Long
35. dwFlags As Long
36. wShowWindow As Integer
37. cbReserved2 As Integer
38. lpReserved2 As Long
39. hStdInput As Long
40. hStdOutput As Long
41. hStdError As Long
42. End Type
43. Type PROCESS_INFORMATION
44. hProcess As Long
45. hThread As Long
46. dwProcessID As Long
47. dwThreadID As Long
48. End Type
49. Global Const NORMAL_PRIORITY_CLASS = &H20&
50. Global Const INFINITE = -1&

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


51. Declare Function CloseHandle Lib "kernel32" (hObject As Long) As Boolean
52. Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long,
53. ByVal dwMilliseconds As Long) As Long
54. Declare Function CreateProcessA Lib "kernel32" (ByVal lpApplicationName As Long,
55. ByVal lpCommandLine As String, ByVal lpProcessAttributes As Long, ByVal
56. lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal
57. dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal
58. lpCurrentDirectory As Long, lpStartupInfo As STARTUPINFO,
59. lpProcessInformation As PROCESS_INFORMATION) As Long
Run the example program by pressing F5. Click the Command Button control. This
immediately starts the Windows Notepad application. Notice that you are unable to
switch to another running application until you quit Notepad.

174: Using the GetKeyState Function to


Determine the State of Virtual Keys
December 5, 1995

Abstract
This article explains how to retrieve the current state of any virtual key in a Microsoft®
Visual Basic® application.

Determining the State of Virtual Keys


The Microsoft® Windows® application programming interface (API) GetKeyState
function can be used in a Microsoft Visual Basic® application to retrieve the current state
of any virtual key. To use this function, include the following Declare statement in the
General Declarations section of your form:
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As
Integer
The GetKeyState function will return the state (key is up, key is down, key is toggled on
or off) of the virtual key you pass to the function.
In the example program below, you want to retrieve the state of the three toggle keys—
CAPS LOCK, NUM LOCK, and SCROLL LOCK. To do this, you call the GetKeyState
function with the virtual-key code for each individual key you want to test. For example,
to retrieve the state of the CAPS LOCK key, you execute the statement:
Key = GetKeyState(VK_CAPITAL)
The value returned by the GetKeyState function can then be tested. If the low-order bit is
1, then the toggle key is on. If the low-order bit is 0, then the toggle key is off. Therefore,
you can use the statement:
If Key And 1 Then
to test if the toggle key is on.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
This program shows how to use the GetKeyState function to determine whether a toggle
key (CAPS LOCK, NUM LOCK, SCROLL LOCK) is on or off.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long)
4. As Integer
5. Const VK_NUMLOCK = &H90
6. Const VK_SCROLL = &H91
7. Const VK_CAPITAL = &H14
8. Add a Command Button control to Form1. Command1 is created by default.

9. Add the following code to the Click event for Command1.


10. Private Sub Command1_Click()
11. Dim Key As Integer
12.
13. Key = GetKeyState(VK_NUMLOCK)
14. If Key And 1 Then
15. text1.Text = "Num Lock is On"
16. Else
17. text1.Text = "Num Lock is Off"
18. End If
19.
20. Key = GetKeyState(VK_SCROLL)
21. If Key And 1 Then
22. Text2.Text = "Scroll Lock is On"
23. Else
24. Text2.Text = "Scroll Lock is Off"
25. End If
26.
27. Key = GetKeyState(VK_CAPITAL)
28. If Key And 1 Then
29. Text3.Text = "Caps Lock is On"
30. Else
31. Text3.Text = "Caps Lock is Off"
32. End If
33. End Sub
34. Add three Text Box controls to Form1. Text1, Text2, and Text3 are created by
default.
Run the example program by pressing F5. Click the Command Button. The state of the
NUM LOCK, SCROLL LOCK, and CAPS LOCK keys appears in the corresponding
Text Box controls

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


175: Determining the Current Screen
Resolution
December 5, 1995

Abstract
This article explains how to determine the current screen resolution in a Microsoft®
Visual Basic® application.

Determining Horizontal and Vertical Screen


Resolutions
When developing a Microsoft® Visual Basic® application, it may be necessary to
determine the current screen resolution. You can do this by retrieving the
TwipsPerPixelX and TwipsPerPixelY properties of the Screen object.
To determine the horizontal resolution of the screen, you retrieve the value of the
TwipsPerPixelX property. Next, you divide the screen's current Height property by this
value.
To determine the vertical resolution of the screen, you retrieve the value of the
TwipsPerPixelY property. Next, you divide the screen's current Width property by this
value.

Example Program
This program shows how to determine the current screen resolution in a Visual Basic
application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. Dim XTwips As Long
7. Dim YTwips As Long
8. Dim XPixels As Long
9. Dim YPixels As Long
10.
11. XTwips = Screen.TwipsPerPixelX
12. YTwips = Screen.TwipsPerPixelY
13.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


14. YPixels = Screen.Height / YTwips
15. XPixels = Screen.Width / XTwips
16.
17. Text1.Text = Str$(XPixels) + " x " + Str$(YPixels)
18. End Sub
Run the example program by pressing F5. Click the Command Button control. The
current screen resolution appears in the Text Box control

176: Sending Files to the Recycle Bin in


Visual Basic 4.0
December 5, 1995

Abstract
When using the Microsoft® Windows® 95 operating system, you can delete a file from
disk by dragging the file or sending the file to the Recycle Bin. The file is not actually
removed from disk but is only marked for deletion by the system. When the Recycle Bin
is emptied, however, the file is physically removed from the disk. This article explains
how to send files to the Recycle Bin in a Microsoft Visual Basic® version 4.0
application.

Using the SHFileOperation Function to Delete Files


When you use the Microsoft® Windows® 95 operating system, any files that you delete
are stored in the Recycle Bin. The files are not physically removed from the disk, but
they appear to have been deleted. If you want, the files (or directories) that you have
moved to the Recycle Bin can be restored and thus again be made available. However, if
you want to physically remove the files stored in the Recycle Bin from your hard disk,
you must empty the Recycle Bin. After the Recycle Bin has been emptied, you cannot
recover the deleted files. The space occupied by the deleted files is also freed.
In a Microsoft Visual Basic® version 4.0 application, you can send files to the Recycle
Bin by calling the Windows application programming interface (API) SHFileOperation
function. This function lets you manipulate files by moving, copying, renaming, or
deleting them.

The SHFileOperation function requires a pointer to a SHFILEOPSTRUCT structure


that contains the name(s) of the file(s) you want to perform an operation on, as well as the
type of operation (for example, deleting a file) you want to carry out.

When deleting multiple filenames, each filename specified in the SHFILEOPSTRUCT


structure must be separated by a NULL character. The entire list of filenames must be
terminated by two consecutive NULL characters.
The fFlags field in the SHFILEOPSTRUCT structure must be set to the operation you
want to perform on the selected file(s). In this case, set this field to FO_DELETE, which

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


tells the operating system that you want to delete the file by sending it to the Recycle Bin.
In addition, because you are sending the file to the Recycle Bin, use the
FOF_ALLOWUNDO flag. This flag preserves the information required to undelete a file
should you later decide not to physically remove the file from the hard disk.

Example Program
This program shows how to send files to the Recycle Bin in Windows 95.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
the Declare statement must be typed as a single line of code):
3. Public Const FO_DELETE = &H3
4. Public Const FOF_ALLOWUNDO = &H40
5. Declare Function SHFileOperation Lib "shell32.dll" Alias "SHFileOperationA"
6. (lpFileOp As SHFILEOPSTRUCT) As Long
7. Create a new function called ShellDelete. Add the following code to this
function:
8. Public Function ShellDelete(ParamArray vntFileName() As Variant)
9.
10. Dim I As Integer
11. Dim sFileNames As String
12. Dim SHFileOp As SHFILEOPSTRUCT
13.
14. For I = LBound(vntFileName) To UBound(vntFileName)
15. sFileNames = sFileNames & vntFileName(I) & vbNullChar
16. Next
17. sFileNames = sFileNames & vbNullChar
18.
19. With SHFileOp
20. .wFunc = FO_DELETE
21. .pFrom = sFileNames
22. .fFlags = FOF_ALLOWUNDO
23. End With
24.
25. ShellDelete = SHFileOperation(SHFileOp)
26.
27. End Function
28. Add a Command Button control to Form1. Command1 is created by default.

29. Add the following code to the Click event for Command1:
30. Private Sub Command1_Click()
31. Dim FileToKill As String
32.
33. FileToKill = "c:\test*.txt"
34. ShellDelete FileToKill
35. MsgBox "File(s) deleted"
36. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


37. From the Visual Basic Insert menu, select Module to create a new module.
MODULE1.BAS is created by default.

38. Add the following TYPE structure to MODULE1.BAS:


39. Type SHFILEOPSTRUCT
40. hWnd As Long
41. wFunc As Long
42. pFrom As String
43. pTo As String
44. fFlags As Integer
45. fAborted As Boolean
46. hNameMaps As Long
47. sProgress As String
48. End Type
Run the example program by pressing F5. Click the Command Button control. A dialog
box appears, asking whether you really want to delete the selected files. (All files with
the name TEST*.TXT stored in the root directory of the hard drive will be deleted.) Click
the Yes button to confirm the delete request.

177: Adding Drag-and-Drop


Functionality to Your Application
December 5, 1995

Abstract
In many Microsoft® Windows®-based applications, you can grab an item with a mouse,
"drag" the item to another location on the screen, and "drop" the item at that location.
This article explains how to add this drag-and-drop feature to your Microsoft Visual
Basic® applications.

Dragging Files Between Two List Box Controls


Many Microsoft® Windows®-based applications allow you to move an item from one
location on the screen to another location. This is called the drag-and-drop feature. For
example, a list of files might be displayed in a List Box control. If you click a filename in
the List Box control, you can drag the item to a Command Button control, which tells
the program to print or otherwise manipulate the selected file.

You can add the drag-and-drop functionality to your Microsoft Visual Basic® application
by monitoring MouseUp and MouseDown events for a control. In the example program
below, you can drag an item from the first List Box control and drop that item on the
second List Box control.
When you initiate a drag-and-drop process, you select the item you want to drag by
pressing and holding down the left mouse button. You can then move (drag) the item to

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


another location. As soon as you release the left mouse button, the item is "dropped" on
its new location.
The MouseDown event for the source item you want to drag tells you that the user has
pressed and held the left mouse button down. In this event, you need to somehow
determine which item was selected by the user. Because you want to know which item in
the List1 List Box control was selected, you set the variable DraggedItem to the currently
selected item in the List Box control.
When the user drops the List1 item on the second List Box control, a MouseUp event is
triggered for the List Box control. The code in this routine removes the selected item
from the first List Box control and then uses the AddItem method to add this selected
item to the destination control.
The example program below shows just one of several methods you can use to add the
drag-and-drop feature to your Visual Basic applications.

Example Program
This program shows how to drag items from one List Box control and drop them on
another List Box control (note that each Private statement must be typed as a single line
of code):

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Dim DraggedItem As String
4. Add the following code to the Form_Load event for Form1:
5. Private Sub Form_Load()
6. For i = 1 To 5
7. List1.AddItem "Item #" & i
8. List2.AddItem "Entry #" & i
9. Next i
10. End Sub
11. Add the following code to the MouseUp event for Form1:
12. Private Sub Form_MouseUp(Button As Integer, Shift As Integer,
13. X As Single, Y As Single)
14. List2.Enabled = True
15. List1.Enabled = True
16. End Sub
17. Add a List Box control to Form1. List1 is created by default.

18. Add the following code to the MouseDown event for List1:
19. Private Sub List1_MouseDown(Button As Integer, Shift As Integer,
20. X As Single, Y As Single)
21. DraggedItem = List1.List(List1.ListIndex)
22. List1.Enabled = False

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


23. End Sub
24. Add the following code to the MouseUp event for List1:
25. Private Sub List1_MouseUp(Button As Integer, Shift As Integer,
26. X As Single, Y As Single)
27. List2.Enabled = True
28. List2.RemoveItem List2.ListIndex
29. List1.AddItem DraggedItem
30. End Sub
31. Add a second List Box control to Form1. List2 is created by default.

32. Add the following code to the MouseDown event for List2:
33. Private Sub List2_MouseDown(Button As Integer, Shift As Integer,
34. X As Single, Y As Single)
35. DraggedItem = List2.List(List2.ListIndex)
36. List2.Enabled = False
37. End Sub
38. Add the following code to the MouseUp event for List2:
39. Private Sub List2_MouseUp(Button As Integer, Shift As Integer,
40. X As Single, Y As Single)
41. List1.Enabled = True
42. List1.RemoveItem List1.ListIndex
43. List2.AddItem DraggedItem
44. End Sub
Run the example program by pressing F5. Each List Box control contains five items.
Click on an item in the first List Box control, and drag this item to the second List Box
control. Release the mouse button to drop the item on the List Box control. The selected
item is removed from the first List Box control and is added to the second List Box
control. The situation can also be reversed—you can drag an item from the second List
Box control to the first List Box control.

178: Enabling or Disabling Fast Task


Switching in Windows
December 5, 1995

Abstract
Within a Microsoft® Visual Basic® application, you can use the Microsoft Windows®
SystemParametersInfo to enable or disable fast task switching key combinations. This
article contains an example program that disables fast task switching.

Using the SystemParametersInfo Function


When you have several Microsoft® Windows®-based applications loaded into memory
at the same time, there are several methods you can use to switch to any one of these

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


applications. For example, when you press the ALT+TAB key combination, Windows
displays a window from which you can select the program to which you want to switch
the focus. This is called fast task switching.
From within a Microsoft Visual Basic® application, you can enable or disable the fast
task switching feature. You can do this by calling the Windows application programming
interface (API) SystemParametersInfo function, which retrieves or sets a multitude of
different settings.
To enable the fast task switching capability in Windows, you must call the
SystemParametersInfo function with its lpvParam variable set to False. To disable fast
task switching, however, you set lpvParam to True.
In the example program below, you disable fast task switching by executing the
following statement:
X = SystemParametersInfo(97, True, lpvparam, 0)
After this statement is executed, the ALT+TAB, CTRL+ESC, and CTRL+ALT+DEL key
combinations are no longer recognized by the Windows operating system.

Example Program
This program shows how to disable certain key combinations such as CTRL+ESC,
ALT+TAB, and CTRL+ALT+DEL. This, in effect, prevents the fast task switching
mechanism available under the Windows operating system.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that the Declare statement must be typed as a single line of code):
3. Private Declare Function SystemParametersInfo Lib "user32" Alias
4. "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long,
5. lpvparam As Any, ByVal fuWinIni As Long) As Long
6. Add the following code to the Form_Load event for Form1:
7. Private Sub Form_Load()
8. Dim lpvparam As Boolean
9. Dim X As Long
10.
11. X = SystemParametersInfo(97, True, lpvparam, 0)
12. End Sub
Run the example program by pressing F5. Form1 appears on the screen. Notice that you
cannot use the ALT+TAB, CTRL+ESC, or CTRL+ALT+DEL key combinations—the
Windows operating system ignores these actions.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


179: Retrieving the Computer Name
December 5, 1995

Abstract
This article explains how you can use the Microsoft® Windows® GetComputerName
function to retrieve the computer name.

Using the GetComputerName Function


When you initially install the Microsoft® Windows® operating system on your
computer, a default name is assigned to your computer. This name is initialized when you
start your computer. The actual name is stored in the registration database (registry).

You can use the Windows application programming interface (API) GetComputerName
function in your Microsoft® Visual Basic® application to retrieve the name assigned to
your computer. To use this function, include the following Declare statement in your
project:
Private Declare Function GetComputerName Lib "kernel32" Alias
"GetComputerNameA" (ByVal sBuffer As String, lSize As Long)
As Long
The GetComputerName function requires two arguments. The first argument, sBuffer, is
the buffer that will hold the computer name after the function is executed. The size of the
buffer should be large enough to hold the entire name. The second argument, lSize, must
be initialized to the size of sBuffer.

After you have executed the GetComputerName function, the lSize variable will be set
to a count of the actual number of characters stored in the sBuffer string. This count value
does not include the terminating NULL character.

Example Program
This program shows how to retrieve the name assigned to a computer.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that this Declare statement must be typed as a single line of code):
3. Private Declare Function GetComputerName Lib "kernel32" Alias
4. "GetComputerNameA" (ByVal sBuffer As String, lSize As Long) As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


5. Add a Command Button control to Form1. Command1 is created by default.

6. Add the following code to the Click event for Command1:


7. Private Sub Command1_Click()
8. Dim PCName As String
9. Dim P As Long
10.
11. P = NameOfPC(PCName)
12. text1.Text = PCName
13. End Sub
14. Add a Text Box control to Form1. Text1 is created by default.

15. Create a new function called NameOfPC. Add the following code to this
function:
16. Function NameOfPC(MachineName As String) As Long
17.
18. Dim NameSize As Long
19. Dim X As Long
20.
21. MachineName = Space$(16)
22. NameSize = Len(MachineName)
23. X = GetComputerName(MachineName, NameSize)
24. End Function
Run the example program by pressing F5. Click the Command Button control. The
name assigned to the computer appears in the Text Box control.

180: Performing Searches in a


Combo Box Control with SendMessage
December 5, 1995

Abstract
This article explains how to design your Microsoft® Visual Basic® application so that
the user can search for an item in a Combo Box control.

Searching for Items in a Combo Box Control


Microsoft® Visual Basic® Combo Box and List Box controls let you display a list of
related items. For example, you might have a Combo Box control that displays a list of
colors to the user. The user can select any color he or she wants to use by clicking that
particular item in the Combo Box control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


When your Combo Box or List Box control contains a large number of items, it may be
time-consuming to scroll through the items. An alternative solution to this problem can
be found by allowing your user to type the item he or she wants to find. Then you can
search through each entry in the control until you find the target item.
The Microsoft Windows® application programming interface (API) SendMessage
function allows you to send a specific message to a window. In the example program
below, a CB_FINDSTRING message is sent to the Combo Box control. This message
tells Windows to search the Combo Box control for the value specified in the lParam
variable.
The lParam variable must be set to the text string you want to locate in the Combo Box
control. When the CB_FINDSTRING message is sent to the Combo Box control, the
message will return the index number of the item that actually exists in the control. You
must be aware, however, that the index number returned is zero-based. Therefore, if a
value of 5 is returned, this actually corresponds to the fourth entry in the Combo Box
control.

Example Program
This program shows how to find an individual item in a Combo Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
4. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any)
5. As Long
6. Const CB_FINDSTRING = &H14C
7. Add the following code to the Form_Load event for Form1:
8. Private Sub Form_Load()
9. For X = 1 To 10
10. Combo1.AddItem "Item" & X
11. Next
12. End Sub
13. Add a Combo Box control to Form1. Combo1 is created by default.

14. Add a Command Button control to Form1. Command1 is created by default.

15. Add the following code to the Click event for Command1:
16. Private Sub Command1_Click()
17. X = SendMessage(Combo1.hwnd, CB_FINDSTRING, -1, ByVal "Item5")
18. MsgBox "Index number for this entry is: " & Str$(X)
19. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Run the example program by pressing F5. Click the Command Button control to search
the Combo Box control for the item "Item5". A message box appears that identifies the
index number (in this case, a value of 4) that corresponds to the item that was found in
the Combo Box control. A value of 4 is returned because index numbers begin at zero.

181: Arranging Child Forms in a


Cascading Fashion
December 5, 1995

Abstract
This article explains how to design your Microsoft® Visual Basic® application so that
after the user closes one child form on the screen the remaining child forms are
automatically cascaded.

Using the Arrange Method to Cascade Forms


When you design a Microsoft® Visual Basic® application in which one form contains
several child forms, the user can selectively close a child form to remove it from the
screen. However, when you close a child form in this manner, its space is still occupied
in the multiple document interface (MDI) form. You can use the Arrange method to
automatically cascade all remaining child forms each time a child form is closed or
unloaded from memory.

Example Program
This program shows how to arrange child forms in a cascading format whenever child
forms are loaded or unloaded.
1. Create a new project in Visual Basic. Form1 is created by default. Set its
MDIChild property to True. Size the form so that it is relatively small in size.

2. Add the following code to the Unload event for Form1:


3. Private Sub Form_Unload(Cancel As Integer)
4. MDIForm1.Arrange 0
5. End Sub
6. From the Visual Basic Insert menu, select MDIForm to create an MDI form.
MDIForm1 is created by default.

7. Add a menu to MDIForm1. From the Visual Basic Tools menu, select Menu
Editor. Type the Caption as "&New Form" and the Name as NewForm.

8. Add the following code to the Click event for NewForm:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


9. Private Sub NewForm_Click()
10. Static X
11. Dim Form As New Form1
12. X=X+1
13. Form.Caption = "Child" & X
14. MDIForm1.Arrange 0
15. End Sub
Run the example program by pressing F5. Click several times on the New Form menu.
Each time you click this menu entry, a new child form is created, with each form being
named sequentially as Child1, Child2, and so on. When you click the Close button, the
child form is unloaded, and the remaining child forms are arranged in a cascading
fashion, with no blank positions where the unloaded child forms used to appear.

182: Temporarily Enabling or Disabling


Tabs on the TabStrip Control
December 5, 1995

Abstract
This article explains how to selectively enable and disable specific Tab buttons in a
Microsoft® Windows® 95 application.

Enabling and Disabling Tab Buttons


The TabStrip control is one of the new controls provided in Microsoft® Windows® 95.
This control lets you organize information in an orderly manner. For example, you could
have a TabStrip control where one of the Tab buttons presents a page of information for
selecting your program's default color options. Another Tab button could present a page
where the user can select the default printer he or she wants to use. To switch between
these two pages, the user simply clicks on one of the Tab buttons.
In some situations, however, you may want to disable one or more of the Tab buttons
temporarily from within your Microsoft Visual Basic® application. The Tab button does
not have an Enabled property, but you can accomplish this functionality by maintaining
such a state in code.
You can use a global array to keep track of which Tab buttons are enabled and disabled.
Each index in the global array corresponds to one of the Tab buttons on the TabStrip
control.

In your program, you check the global array index for a specific Tab button. If it is
"enabled," then the SelectedItem property can be used to activate the code associated
with that Tab button. On the other hand, if that button has been flagged as "disabled," you
force the currently selected Tab button to remain the default Tab button—not the Tab
button the user just clicked.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Note that this solution for enabling and disabling individual Tab buttons does not prevent
the actual click event from being triggered. The code in the Tab button's Click event does
not, however, get executed if the Tab button is disabled.

Example Program
This program shows how to selectively enable and disable a specific Tab button on a
TabStrip control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a TabStrip control to Form1. TabStrip1 is created by default. Select the


TabStrip control by clicking it. Click the right mouse button once to bring up the
TabStrip control's pop-up menu. Click once on the Tabs option. Click on the
Insert tab. Type the Caption as Tab1. Create four more Tabs with the Captions set
to Tab2, Tab3, Tab4, and Tab5, respectively. Click the OK command button
when done.

3. Add the following code to the General Declarations section of Form1:


4. Dim PrevTab As Object
5. Dim TabState(1 To 5) As Integer
6. Dim FakeNext As Integer
7. Add the following code to the Form_Load event for Form1:
8. Private Sub Form_Load()
9. For i = 1 To 5 Step 2
10. TabState(i) = True
11. Next i
12. Set PrevTab = TabStrip1.Tabs(1)
13. FakeNext = False
14. End Sub
15. Add the following code to the Click event for TabStrip1:
16. Private Sub TabStrip1_Click()
17. If FakeNext Then
18. FakeNext = False
19. MsgBox "Cannot click on this TAB"
20. Else
21. For i = 1 To 5
22. If TabStrip1.Tabs(i).Selected And Not TabState(i) Then
23. FakeNext = True
24. TabStrip1.SelectedItem = PrevTab
25. Exit Sub
26. End If
27. Next i
28. Set PrevTab = TabStrip1.SelectedItem
29. MsgBox "Can click on this TAB"
30. End If
31. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Run the example program by pressing F5. The TabStrip control appears on the screen.
Notice that you can click on the Tab buttons numbered 1, 3, and 5. A message box
appears telling you that you clicked one of these three Tab buttons. However, when you
click the second and fourth Tab buttons, no click event is actually activated. A message
box appears indicating that you cannot click on the Tab2 or Tab4 button.

183: Changing the Color of the Grid


Control's Cells
December 5, 1995

Abstract
You can present information to the user of your Microsoft® Visual Basic® application in
the form of a Grid control, which displays the information in cells. This article explains
how to change the color of the cells and the color of the text within the cells.

Changing the Color of Cells and Cell Text


When developing a Microsoft® Visual Basic® application, you may need to display data
to your user by using the Grid control. The Grid control allows you to group related
information into columns and rows.

The Grid control, however, does not provide any properties or methods for changing the
color of text within individual cells or the color of the cell itself. As a workaround to this
shortcoming, you can use a Picture Box control to change the appearance of cells in a
Grid control.
In the example program below, you set the BackColor and ForeColor properties of the
Picture Box control to the desired colors. Next, you use the Print method to print the
original contents of a cell in the Grid control to the Picture Box control. The final step is
to transfer the newly created image to the Grid control's Picture property. This, in turn,
displays the cell's text in a specific color or changes the cell's color.

Example Program
This program shows how to set the color of a Grid control's cells and the text within
specific cells.
1. Create a new project in Visual Basic. Form1 is created by default.

2. From the Visual Basic Tools menu, select Custom Controls. Select the "Microsoft
Grid Control" from the list of controls to add the Grid control to your toolbox.

3. Add a Grid control to Form1. Grid1 is created by default. Set its Rows property
to 5 and its Columns property to 5.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


4. Add a Picture Box control to Form1. Picture1 is created by default. Size the
Picture Box control so that it is slightly larger than the size of one cell in the
Grid control. Set the Visible property of the Picture Box control to False.

5. Add the following code to the Form_Load event for Form1:


6. Private Sub Form_Load()
7. Dim I As Integer
8. Dim J As Integer
9.
10. For I = 1 To Grid1.Rows - 1
11. For J = 1 To Grid1.Cols - 1
12. Grid1.Row = I
13. Grid1.Col = J
14. 'Fill cell text so that "(I,J)" string is
15. 'Grid1.Text = "(" & CStr(I) & "," & CStr(J) & ")"
16. Next J
17. Next I
18.
19. End Sub
20. Add a Command Button control to Form1. Command1 is created by default.

21. Add the following code to the Click event for Command1:
22. Private Sub Command1_Click()
23. Dim I As Integer
24. Dim J As Integer
25.
26. For I = 1 To Grid1.Rows - 1
27. For J = 1 To Grid1.Cols - 1
28. Call SetGridCell(Grid1, I, J, QBColor(I - 1), QBColor(15))
29. Next J
30. Next I
31. End Sub
32. Add a second Command Button control to Form1. Command2 is created by
default.

33. Add the following code to the Click event for Command2:
34. Private Sub Command2_Click()
35. Dim I As Integer
36. Dim J As Integer
37.
38. For I = 1 To Grid1.Rows - 1
39. For J = 1 To Grid1.Cols - 1
40. Call SetGridCell(Grid1, I, J, QBColor(15), QBColor(J - 1))
41. Next J
42. Next I
43.
44. End Sub
45. Add a third Command Button control to Form1. Command3 is created by
default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


46. Add the following code to the Click event for Command3:
47. Private Sub Command3_Click()
48. Call ClearGrid(Grid1)
49. End Sub
50. Create a new subroutine called SetGridCell. Add the following code to this
subroutine:
51. Sub SetGridCell(Grd As Grid, RowNum%, ColNum%, BkClr&, FrClr&)
52. Grd.Row = RowNum%
53. Grd.Col = ColNum%
54. Picture1.BackColor = BkClr
55. Picture1.ForeColor = FrClr
56. Picture1.CurrentX = 0
57. Picture1.CurrentY = 0
58. Picture1.Print Grd.Text
59. Grd.Picture = Picture1.Image
60.
61. End Sub
Run the example program by pressing F5. Click the first Command Button control.
Each row in the Grid control appears in a different color. Click the second Command
Button control. The text in each column of the Grid control appears in a different color.
Click the third Command Button control to restore the Grid control to its original
appearance.

184: Creating a List of Directories and


Files in a List Box Control
December 5, 1995

Abstract
This article explains how you can easily recreate the structure of a disk by storing a list of
drives, directories, and/or files in a List Box control.

Creating a List of Directories and Files


The Microsoft® Windows® application programming interface (API) SendMessage
function allows you to store a list of drives, directories, and/or files in a List Box control.
In effect, this lets you easily recreate the structure of a disk. Although Microsoft Visual
Basic® provides the Common Dialog File and Drive controls, it is faster and easier to
use the SendMessage function to store a list of files in a List Box control. This technique
also gives you greater control and flexibility when manipulating the filenames stored in
the List Box control.

To use the SendMessage function, you must include the following Declare statement in
your project:
Private Declare Function SendMessageAny Lib "user32" Alias "SendMessageA"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Integer,
lParam As Any) As Long
The SendMessage function is used to send a specific message to a window. In the
example program below, the LB_DIR message is sent to the program's window. The
LB_DIR message tells Windows to add a list of the files (specified by the wParam
argument) to the List Box control.

The wParam argument for the LB_DIR message lets you specify the type of files you
want the List Box control to contain. You can set the wParam argument to include
normal, read-only, hidden, system, and archive files. In addition, you can also set the
wParam argument so that it sends drive and directory files to the List Box control.

Example Program
This program shows how to create a list of files and/or directories in a List Box control.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function AppendMenu Lib "user32" Alias "AppendMenuA"
4. (ByVal hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long,
5. ByVal lpNewItem As String) As Long
6. Const WM_USER = &H400
7. Const LB_DIR = &H18D
8. Const DIR_NORMALFILES = &H0
9. Const DIR_READONLY = &H8001
10. Const DIR_HIDDEN = &H8002
11. Const DIR_SYSTEM = &H8004
12. Const DIR_DIRECTORIES = &H8010
13. Const DIR_ARCHIVED = &H8020
14. Const DIR_DRIVES = &HC000
15. Add a Command Button control to Form1. Command1 is created by default.

16. Add the following code to the Click event for Command1:
17. Private Sub Command1_Click()
18. Call ListFiles("c:\*.*")
19. End Sub
20. Add a List Box control to Form1. List1 is created by default.

21. Create a new subroutine called ListFiles. Add the following code to this
subroutine:
22. Sub ListFiles(sFileSpec As String)
23. Dim I As Long
24. List1.Clear
25. I = SendMessageAny(List1.hWnd, LB_DIR, DIR_DRIVES, ByVal sFileSpec)
26. I = SendMessageAny(List1.hWnd, LB_DIR, DIR_DIRECTORIES, ByVal sFileSpec)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


27. I = SendMessageAny(List1.hWnd, LB_DIR, DIR_NORMALFILES, ByVal sFileSpec)
28. End Sub
Run the example program by pressing F5. Click the Command Button control. The List
Box control is filled with the names of your disk drives, directories, and files.

185: Determining the Number of


Printable Lines Per Page on the Printer
December 5, 1995

Abstract
This article explains how to determine the total number of text lines that a printer can
accommodate on a single sheet of paper.

Using the TextHeight and ScaleHeight Methods


When sending data to a printer, you often need to determine how many lines of text can
be printed on a single sheet of paper. The Microsoft® Visual Basic® TextHeight method
indicates how much space is used when text is actually sent to the printer. The
TextHeight method, in other words, tells you the vertical height of the output string.
Among other things, you can use this information to center a line of text vertically on the
printer.

The ScaleHeight property tells you the horizontal coordinates for a single printed page.
If you need to determine how many lines of text can be printed on the default printer, you
must first retrieve the TextHeight method's value for the printer. Next, you retrieve the
ScaleHeight property's value for the printer and divide this value by the height of the text
string. The result is the total number of lines per page that you can send to the printer.

Example Program
This program tells you how many lines of text can be printed on a single sheet of paper.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. Dim PrinterHeight As Integer
7. Dim NumberOfLines As Integer
8.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


9. PrinterHeight = Printer.TextHeight("Sample string")
10. NumberOfLines = Printer.ScaleHeight / PrinterHeight
11. Text1.Text = "Lines per page = " & Str$(NumberOfLines)
12. End Sub
Run the example program by pressing F5. Click the Command Button control. The
number of lines the printer can accommodate, using the currently selected font, is
displayed in the Text Box control.

186: Formatting Text in a Rich Text Box


Control in Visual Basic 4.0
February 28, 1996

Abstract
The Microsoft® Visual Basic® version 4.0 Rich Text Box control allows the user to
enter and edit text. In addition, this control provides more advanced formatting features
than the conventional TextBox control. This article explains how to display selected text
in different fonts in the Rich Text Box control.

Using the TextRTF Property of a Rich Text Box


Control
The Microsoft® Visual Basic® version 4.0 Rich Text Box control is an advanced
version of the TextBox control. Text entered in a Rich Text Box control can be
formatted in different colors, fonts and font styles, and point sizes. For example, you can
make text appear in bold or italic. You must remember that the Rich Text Box control
formatting codes apply to the currently selected text only, not to all text in the control.

In the example program below, you fill a Rich Text Box control with a list of all screen
fonts installed in the computer system. This is accomplished by using the TextRTF
property to tell the Rich Text Box control to display the text in the selected font.
Note The TextRTF property requires the Microsoft Windows® 95 or Microsoft
Windows NT® 3.51 or later operating system.

Example Program
This program shows how to display text in different fonts in a Rich Text Box control.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Rich Text Box control to Form1. Rich-Text Box1 is created by default.

3. Add a Command Button control to Form1. Command1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


4. Add the following code to the Click event for Command1:
5. Private Sub Command1_Click()
6. Dim X As Integer
7. Rich-Text Box1.Text = ""
8.
9. For X = 0 To Screen.FontCount - 1
10. DoEvents
11. With Rich-Text Box1
12. Var = .TextRTF
13. var1 = Right(Var, Len(Var) - 1)
14. .Text = Screen.Fonts(X)
15. Var = .TextRTF
16. var2 = Left(Var, Len(Var) - 3)
17. Var = var2 & var1
18. .TextRTF = Var
19. .SelStart = 0
20. .SelLength = Len(Screen.Fonts(X))
21. .SelFontName = Screen.Fonts(X)
22. .SelFontSize = 12
23. End With
24. Next X
25. End Sub
Run the example program by pressing F5. Click the Command Button control. After a
short delay, the Rich Text Box control is populated with a list of all installed screen
fonts. Each font is displayed in its 12-point size.

187: Determining Whether an


Application Was Closed from the Control
Menu
December 5, 1995

Abstract
This article explains how to prevent the user of your Microsoft® Visual Basic®
application from unintentionally quitting the application.

The Query_Unload Event


When running an application, you can select the Close command from the Control-menu
box in the upper-left corner of a window to quit the application. When running a
Microsoft® Windows® 95–based application, you can click the Close button located at
the right end of the title bar. These actions terminate the application immediately.
In a Microsoft Visual Basic® application, the Query_Unload event is triggered when the
user tries to quit an application that is running. The UnloadMode variable in the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Query_Unload event indicates how this event was triggered by containing one of the five
values in the following table.
Value Description
0 The user chose the Close command on the Control-menu box.
1 The application used the Query_Unload method itself.
2 The operating system is being shut down.
3 The application is being shut down by the Task Manager.
4 An MDI form, which closes all child forms belonging to it, is being closed.

For example, when the user chooses the Close command from the Control-menu box to
quit an application, the UnloadMode variable contains a value of zero.

Example Program
This program shows how to prevent a user from accidentally choosing the Close
command on the program's Control-menu box.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the Query_Unload event for Form1:


3. Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
4. Dim I As Integer
5. If (UnloadMode = FORM_CONTROLMENU) Then
6. I = MsgBox("Exit " & App.Title & " ?", 4, App.EXEName)
7. If I = 7 Then
8. Cancel = True
9. MsgBox "Program was not terminated"
10. End If
11. If I = 6 Then
12. Cancel = False
13. MsgBox "Program was terminated"
14. End If
15. End If
16. End Sub
Run the example program by pressing F5. Form1 appears on the screen. Either click the
File menu and click the Close command or click the Close button in the upper right-hand
corner of the form. The program displays a message box asking whether you really want
to quit the program. If you click the Yes button, the program is immediately terminated.
If you click the No button, however, the program continues to run.

188: Adding New Commands to the


Control Menu
December 5, 1995

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Abstract
This article explains how to add a new command to your Microsoft® Visual Basic®
Control menu.

Adding New Commands


When developing a Microsoft® Visual Basic® application, you may need to customize
the application's Control menu. For example, you may want to add an Always On Top
command to the Control menu that gives the user the ability to have the application
window always displayed on top of other windows.

To add a new command to the Control menu, you need to perform several steps. First,
you need to use the Microsoft Windows® application programming interface (API)
GetSystemMenu function to retrieve the handle of the Control menu. (Note that this tip
applies to Windows 95 only.) The following code is the Declare statement for this
function:
Private Declare Function GetSystemMenu Lib "user32"
(ByVal hWnd As Long, ByVal bRevert As Long) As Long
The GetSystemMenu function requires two arguments. The hWnd argument is the
handle of the window that owns the Control menu. In this case, this argument is the
handle of your application's window. The bRevert argument tells the GetSystemMenu
function which of two actions you want to perform.
If the bRevert argument is set to True, the GetSystemMenu function will reset the
Control menu back to its original state (the Windows 95 default). Other applications, as
well as your own, may have previously modified the Control menu.
If the bRevert argument is set to False, the GetSystemMenu function will return the
handle of the current Control menu. This may or may not be the original Control menu
(the Windows 95 default).

In the example program below, you want to add a new command to the Control menu.
Therefore, you run the GetSystemMenu function with the bRevert argument set to False.
Once you have the handle of the Control menu, you need to call the Windows API
AppendMenu function. The following code is the Declare statement for this function:
Private Declare Function AppendMenu Lib "user32" Alias "AppendMenuA"
(ByVal hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem
As Long, ByVal lpNewItem As String) As Long
You can use the AppendMenu function to modify an existing menu's structure. You can
also use this function to modify the appearance of a new menu command. In this case,
however, you just want the command to appear as normal (that is, a textual description).

The AppendMenu function requires the following four arguments:


hMenu A long value containing the handle of the menu you want to modify.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


wFlags A long value that consists of one or more of the following flags that define
the appearance and behavior of the new menu command:
MF_BITMAP A bitmap is used as the command. The
lpNewItem argument must contain the bitmap's
handle.
MF_CHECKED A check mark is placed next to the command.
MF_DISABLED The command is disabled but not grayed.
MF_ENABLED The command is enabled.
MF_GRAYED The command is grayed.
MF_MENUBARBREAK The command is placed on a new line. If this is
a pop-up menu, the new command is placed in
a new column and a vertical line separates the
columns.
MF_MENUBREAK The command is placed on a new line. If this is
a pop-up menu, the new command is placed in
a new column.
MF_OWNERDRAW The command is an owner-drawn command.
MF_POPUP The command is a pop-up command. Selecting
this command displays a pop-up menu. The
pop-up menu's handle must be in the
wIDNewItem argument.
MF_SEPARATOR A horizontal dividing line is drawn in a pop-up
menu only.
MF_STRING The command is a string. The lpNewItem
argument must contain the string itself.
MF_UNCHECKED A check mark is not placed next to the
command. This is the default setting.
wIDNewItem A long value containing the new menu command's identifier. If the wFlags
argument is set to MF_POPUP, this argument contains the pop-up menu's
handle.
lpNewItem A string containing the content of the new menu command according to
the wFlags argument, as follows:
MF_BITMAP A bitmap handle.
MF_OWNERDRAW A 32-bit value specifying an application's
appearance and behavior instructions for the
owner-drawn command.
MF_STRING The command's text.

In the example program below, you use the AppendMenu function to add a new
command called "NewMenu" to the Control menu. However, in order to perform some
action when a user clicks NewMenu, you need to determine when the application
receives a Click event for that new menu command.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To do this, you need to use a third-party subclassing control such as Message Blaster.
The application's window will receive a WM_SYSCOMMAND message each time the
user clicks a command on the Control menu. The subclassing control traps each
WM_SYSCOMMAND received. If the command corresponds to the new command
(identified as SC_NEWMENU), you can perform your own function. In all other cases,
Windows 95 will process the menu selection as normal.

Example Program
This program shows how to add a new command to your application's Control menu.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
each Declare statement must be typed as a single line of code):
3. Private Declare Function AppendMenu Lib "user32" Alias "AppendMenuA"
4. (ByVal hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long,
5. ByVal lpNewItem As String) As Long
6. Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Long, ByVal
7. bRevert As Long) As Long
8. Const WM_SYSCOMMAND = &H112
9. Const MF_STRING = &H0
10. Const SC_NEWMENU = 1
11. Add the following code to the Form_Load event for Form1:
12. Private Sub Form_Load()
13. Dim hw As Long
14. Dim hMenu As Long
15.
16. hw = Me.hWnd
17. hMenu = GetSystemMenu(hw, False)
18.
19. If AppendMenu(hMenu, MF_STRING, SC_NEWMENU, "&NewMenu") Then
20. MsgBlaster1.hWndTarget = hw
21. MsgBlaster1.AddMessage WM_SYSCOMMAND, POSTPROCESS
22. End If
23.
24. End Sub
25. Add a Message Blaster control to Form1. MsgBlaster1 is created by default.

26. Add the following code to the MsgBlaster1_Message event:


27. Private Sub MsgBlaster1_Message(ByVal hWnd As Long, ByVal Msg As Long, wParam As
28. Long, lParam As Long, nPassage As Integer, lReturnValue As Long)
29. If (wParam = SC_NEWMENU) Then
30. MsgBox "NewMenu menu command selected"
31. End If
32. End Sub
Run the example program by pressing F5. Form1 appears on the screen. Click the form's
Control menu. The new command, NewMenu, is shown on the Control menu. When you
click this new command, a message box appears indicating that NewMenu was selected.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


189: Determining Which Screen and
Printer Fonts Are Available
December 5, 1995

Abstract
When displaying text in a Microsoft® Visual Basic® application, you may want to
specify which fonts are assigned to the text on the screen and the text sent to the printer.
This article explains how you can retrieve a list of screen fonts and a list of printer fonts
currently installed on your computer system.

Retrieving Lists of Installed Screen and Printer Fonts


The Fonts collection provided in Microsoft® Visual Basic® is a list of the names of all
fonts installed in the Microsoft Windows® operating system. You can retrieve a specific
font by specifying an index value with a statement such as:
X$ = Screen.Fonts(2)
Note that you cannot modify the Fonts property of an object but can only ask which font
is currently being used.
The Fonts collection exists for both the Screen object and the Printer object, which both
have a FontCount property. The FontCount property indicates exactly how many fonts
for that particular object are available.
You can create a list of all available fonts by interrogating the items contained in the
Fonts collection. In the example program below, a For-Next loop retrieves each font's
name from the Fonts collection. The routine ends when the maximum number of installed
fonts (FontCount - 1) has been reached.

Example Program
This program shows how to retrieve a list of all printer and screen fonts installed in the
computer system.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a List Box control to Form1. List1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. List1.Clear
7. Dim X As Integer
8. For X = 0 To Printer.FontCount - 1
9. List1.AddItem Printer.Fonts(X)
10. Next X
11. End Sub
12. Add a second Command Button control to Form1. Command2 is created by
default.

13. Add the following code to the Click event for Command2:
14. Private Sub Command2_Click()
15. List1.Clear
16. Dim X As Integer
17. For X = 0 To Screen.FontCount - 1
18. List1.AddItem Screen.Fonts(X)
19. Next X
20. End Sub
Run the example program by pressing F5. Click the first Command Button control. A
list of all printer fonts appears in the List Box control. Click the second Command
Button control. A list of all screen fonts appears in the List Box control.

190: Adding Hot Key Access to Your


Visual Basic Application
December 5, 1995

Abstract
This article explains how to add a hot key that allows the user of a running application to
quickly switch to your Microsoft® Visual Basic® application.

Providing Task Switching by Adding a Hot Key


Under the Microsoft® Windows® 95 operating system, you can easily switch between
running applications by clicking on an application's icon on the taskbar or by pressing the
ALT+TAB key combination. When developing a Microsoft Visual Basic® application,
you may want to provide a quick method for the user to switch to the application. This
can be done by adding a hot key to your application.
Whenever the user presses the specific hot key that you have assigned to your Visual
Basic application, your application receives the focus and is maximized. The user does
not have to use ALT+TAB or the Windows 95 taskbar to activate your application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Suppose that whenever the user presses the PAUSE key, you want to activate your Visual
Basic application. Therefore, you need to monitor the computer system so that the
application is activated only when this keystroke is detected on the keyboard. This can be
accomplished by using the Windows application programming interface (API)
SendMessage function.

The SendMessage function can be used to send a specific message to a window. To use
this function, add the following Declare statement to the General Declarations section of
your form:
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long,
lParam As Long) As Long
The SendMessage function takes the following four arguments:
hWnd A long value containing the window's handle. The message is sent to this
window.
wMsg A long value containing the message you want to send to hWnd.
wParam A long value containing additional message-dependent information.
lParam A long value containing additional message-dependent information.

In the example program below, you want to set PAUSE as the hot key to your
application. Because you want the application to be activated when PAUSE is pressed,
you need to send a WM_SETHOTKEY message to the application's window—in this
case, Form1. A WM_SETHOTKEY message is used to assign a specific key as an
application's hot key. Therefore, we need to use the SendMessage function to send a
WM_HOTKEY message to the application. This message is placed at the top of the
thread's message queue, which in turn allows the PAUSE key to gain immediate attention
when it is detected.

Note that a window can have only one hot key associated with it at any one time. In
addition, child windows cannot have hot keys associated with them. Finally, if you assign
a hot key to a window that already has a hot key assigned to it, the new hot key replaces
the original one.

Example Program
This program below shows how to add hot key access to your Visual Basic applications.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
4. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long,
5. lParam As Long) As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


6. Const WM_SETHOTKEY = &H32
7. Const VK_PAUSE = &H13
8. Add the following code to the Click event for Form1:
9. Private Sub Form_Click()
10. Dim I As Long
11. I = SendMessage(Me.hwnd, WM_SETHOTKEY, VK_PAUSE, 0)
12. MsgBox "The Pause key was pressed"
13. End Sub
Run the example program by pressing F5. Each time you click the mouse on the form, the
"The Pause key was pressed" message box is displayed. Now, minimize the example
program. Control returns to the Visual Basic design environment. Press PAUSE on the
keyboard. The example program is immediately maximized and receives the focus.

191: Shelling to Other Applications


December 5, 1995

Abstract
This article explains how to run MS-DOS®-based and Microsoft Windows®-based
applications from within a Microsoft Visual Basic® application.

Running MS-DOS and Windows Applications


When you want to run an MS-DOS®-based or Microsoft® Windows®-based application,
you can use the Windows application programming interface (API) OpenProcess
function. The OpenProcess function allows you to control how the application is run.
In the example program below, you use the OpenProcess function to launch the Notepad
application. The OpenProcess function returns the handle of the newly opened process
(that is, application). When you have the process handle for Notepad, you can use the
Windows API GetExitCodeProcess function to determine whether Notepad is still
running in memory.
The GetExitCodeProcess function returns a value of STILL_ACTIVE if the opened
process is still running in memory. Knowing this, you can periodically check the status of
the Notepad application in a Do-While loop. When the example program finds that
Notepad is not running, the example program displays a message that Notepad was
indeed terminated.

Example Program
This program shows how to run an MS-DOS-based or Windows-based application in the
background. The application continues running in the background until the user
terminates it.
1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Private Declare Function OpenProcess Lib "kernel32"
4. (ByVal dwDesiredAccess As Long, ByVal bInheritHandle
5. As Long, ByVal dwProcessId As Long) As Long
6. Private Declare Function GetExitCodeProcess Lib "kernel32"
7. (ByVal hProcess As Long, lpExitCode As Long) As Long
8. Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds
9. As Long)
10. Const STILL_ACTIVE = &H103
11. Const PROCESS_QUERY_INFORMATION = &H400
12. Add a Command Button control to Form1. Command1 is created by default.

13. Add the following code to the Click event for Command1:
14. Private Sub Command1_Click()
15. Dim JobToDo As String
16. JobToDo = "c:\windows\notepad.exe"
17. Shell32Bit JobToDo
18. End Sub
19. Create a new subroutine called Shell32Bit. Add the following code to this
subroutine:
20. Sub Shell32Bit(ByVal JobToDo As String)
21. Dim hProcess As Long
22. Dim RetVal As Long
23. 'The next line launches JobToDo as icon,
24. 'captures process ID
25. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, Flase, Shell(JobToDo, 6))
26. Do
27. 'Get the status of the process
28. GetExitCodeProcess hProcess, RetVal
29. 'Sleep command recommended as well
30. 'as DoEvents
31. DoEvents: Sleep 100
32. 'Loop while the process is active
33. Loop While RetVal = STILL_ACTIVE
34. MsgBox "Notepad terminated by user"
35. End Sub
Run the example program by pressing F5. Click the Command Button control. The
example program runs the Windows Notepad application. Notice that the Notepad icon
appears in the Windows taskbar.
Notepad runs in the background until you quit it. The example program displays a
message box that indicates that Notepad was terminated

192: Selecting a New Desktop Wallpaper


December 5, 1995

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Abstract
This article explains how to select desktop wallpaper from a Microsoft® Visual Basic®
application when using the Microsoft Windows® 95 operating system.

Using the SystemParametersInfo Function


Using the Microsoft® Windows® 95 operating system, you can change the Control Panel
settings to display any wallpaper image you want. From the Start menu, click Settings
and click Control Panel. Next, double-click Desktop Themes. You can then choose a new
wallpaper from the Themes drop-down list box.

In a Microsoft Visual Basic® application, however, you can use the Windows application
programming interface (API) SystemParametersInfo function to select a new wallpaper.
This function can be used to retrieve or set many different Control Panel settings. The
following code is the Declare statement for this function:
Private Declare Function SystemParametersInfo Lib "user32" Alias
"SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam
As Long, ByVal lpvParam As String, ByVal fuWinIni As Long) As Long
Note that the SystemParametersInfo function requires four arguments. The first
argument, uAction, is the setting that you want to retrieve or set. In this case, you want to
change the desktop wallpaper, so you use the SPI_SETDESKWALLPAPER constant
The second argument, uParam, is set to zero, because the wallpaper setting does not use
this argument. For the third argument, lpvParam, you must tell the
SystemParametersInfo function the filename for the wallpaper you want to use. You
must be sure to specify the file's full path in this argument. The fourth argument,
fuWinIni, can be one of two values that tells Windows 95 how to preserve the new
settings.
If the SPIF_UPDATEINIFILE constant is specified for the fuWinIni argument, Windows
95 saves the new settings to its user profile. If the constant
SPIF_SENDWININICHANGE is specified, however, the change is broadcast to
Windows 95 after the user profile is updated.

In the example program below, you can change the wallpaper setting to None or to the
PINSTRIPE bitmap image. When you change the current wallpaper setting to None, the
desktop is presented as a black background. In all other cases, the desktop wallpaper
displays the image stored in the specified bitmap file.

Example Program
This program below shows how to change the wallpaper in Windows 95.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
the Declare statement must be typed as a single line of code):

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Private Declare Function SystemParametersInfo Lib "user32" Alias
4. "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam
5. As Long, ByVal lpvParam As String, ByVal fuWinIni As Long) As Long
6. Const SPIF_UPDATEINIFILE = &H1
7. Const SPI_SETDESKWALLPAPER = 20
8. Const SPIF_SENDWININICHANGE = &H2
9. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Remove Wallpaper".

10. Add the following code to the Click event for Command1:
11. Private Sub Command1_Click()
12. Dim X As Long
13. X = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0&, "(None)",
SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)
14. MsgBox "Wallpaper was removed"
15. End Sub
16. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Change Wallpaper".

17. Add the following code to the Click event for Command2:
18. Private Sub Command2_Click()
19. Dim FileName As String
20. Dim X As Long
21.
22. FileName = "c:\windows\pinstripe.bmp"
23.
24. X = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0&, FileName,
SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)
25. MsgBox "Wallpaper was changed"
26. End Sub
Run the example program by pressing F5. Click the Remove Wallpaper command button.
The wallpaper is removed—Windows 95 displays a black background with no graphics.
Click the Change Wallpaper command button. The wallpaper is changed to the
PINSTRIPE bitmap image

193: Launching Windows 95 Control


Panel Applets in Visual Basic
December 5, 1995

Abstract
This article explains how to launch an applet in Microsoft® Windows® 95 Control Panel
from within a Microsoft Visual Basic® application.

Using the RunDLL32 Utility to Launch Applets

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The Control Panel program in the Microsoft® Windows® 95 operating system allows
you to customize various aspects of the operating system. For example, by running the
Printers applet, you can add, remove, or select a new default printer.
A special utility included with Windows 95 allows you to execute a specific function
(that is, a Control Panel applet) from within your own Microsoft Visual Basic®
application. This utility is RunDLL32. You can use the RunDLL32 utility to execute the
Control_RunDLL function in the Shell32.DLL library. To execute a Control Panel
applet, you use a statement such as:
X = Shell("Rundll32.exe shell32.dll,Control_RunDLL main.cpl @2")
This statement uses the Shell command to execute the Printers applet in Control Panel. In
a Visual Basic application, this would give your user the ability to select a new default
printer, check a printer's status, and add or remove printer objects from the Windows 95
operating system.
When using the Shell command to launch a Control Panel applet, you must be careful to
use the exact syntax for the RunDLL32 utility. The capitalization of all components in
the statement must not be altered in any way—otherwise, an error will occur.

Each time you want to launch a Control Panel applet, your Visual Basic statement must
include the syntax used above. In other words, you need only to substitute the name of
the .CPL file for "main.cpl" used above and specify the number of the particular applet
you want to execute. Also, if that applet requires additional command-line parameters,
you would specify these as the last parameter to the statement.

Each applet contained in a .CPL file is numbered starting from zero. If you don't specify
which applet you want to execute with the @value parameter, the first applet (@0) is the
one that is launched.

The following list provides a starting point for showing how to launch the different
applets found in Control Panel.
To launch Control Panel itself:
rundll32.exe shell32.dll,Control_RunDLL
To launch the Accessibility Options applet:
• General
• rundll32.exe shell32.dll,Control_RunDLL access.cpl,,5
• Display
• rundll32.exe shell32.dll,Control_RunDLL access.cpl,,3
• Keyboard
• rundll32.exe shell32.dll,Control_RunDLL access.cpl,,1
• Mouse
• rundll32.exe shell32.dll,Control_RunDLL access.cpl,,4
• Sound

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


• rundll32.exe shell32.dll,Control_RunDLL access.cpl,,2
To launch the Regional Settings applet:
• Background
• rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0
• Appearance
• rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2
• Screen Saver
• rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1
• Settings
• rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3
To launch the Date/Time applet:
rundll32.exe shell32.dll,Control_RunDLL timedate.cpl

Example Program
This program shows how to launch the Printers applet in Control Panel from within a
Visual Basic application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

3. Add the following code to the Click event for Command1 (note that the Shell
statement must be typed exactly as shown):
4. Private Sub Command1_Click()
5. X = Shell("Rundll32.exe shell32.dll,Control_RunDLL main.cpl @2")
6. End Sub
Run the example program by pressing F5. Click the command button. The Printers applet
in Control Panel is launched.

194: Adding a Description to the Status


Bar for the Toolbar Control
December 5, 1995

Abstract
Each toolbar control in your Microsoft® Visual Basic® application can have a ToolTip,
which is a small pop-up window containing the name of the control. A ToolTip appears
whenever the mouse pointer is placed over a toolbar control. This article explains how to

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


have information that further describes a toolbar control appear in the Status Bar control
as well.

Assigning Status Bar Descriptions to Toolbar Controls


You can use the Toolbar control in Microsoft® Visual Basic® to perform functions
within an application by simply clicking on one of the control's buttons. Each button in
the Toolbar control can have a caption assigned to it or a ToolTip description that
appears when the mouse pointer is over that button. Most toolbars do not assign captions
to the buttons because these captions appear at the bottom of the button. The preferred
method is to use a ToolTip description.

To create a toolbar, you put a Toolbar control and an ImageList control on a form. Next,
the ImageList control is populated with icons or other graphic images. These images are
then assigned to the Buttons property of the Toolbar control.
You can assign ToolTip descriptions to each button on the toolbar by setting the toolbar's
ShowTips property to True and adding the text for the ToolTip description to the
ToolTip text box. When the mouse pointer is moved over a button on the toolbar, the
button's corresponding ToolTip text appears.
As an added feature, you can add a Status Bar control to the bottom of your form. A
Status Bar control contains information that is displayed to the user when certain events
occur in your application.

In the example program below, additional information is provided to the user when a
specific button is selected on the toolbar. This information appears in the Status Bar
control.

In order to provide this information in the Status Bar control, a method is needed for
determining which button the mouse pointer is over. You can calculate which button the
mouse pointer is over by monitoring the toolbar's MouseMove event.
The MouseMove event is triggered when the mouse is moved over the Toolbar control.
This event indicates the current position of the mouse on the Toolbar control in its X and
Y variables. If the current position of the mouse pointer corresponds to the position of a
toolbar button, a description can be displayed for that button in the Status Bar control.

Example Program
This program shows how to pass a description to a Status Bar control when an item is
selected on a Toolbar control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Toolbar control to Form1. Toolbar1 is created by default.

3. Add an ImageList control to Form1. ImageList1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


4. Add a Status Bar control to Form1. StatusBar1 is created by default.

5. Add the following code to the Form_Load event for Form1:


6. Private Sub Form_Load()
7. Dim imgX As ListImage
8.
9. Set imgX = ImageList1.ListImages. _
10. Add(, "open", LoadPicture("bitmaps\tlbr_w95\open.bmp")) ' 1
11.
12. Set imgX = ImageList1.ListImages. _
13. Add(, "save", LoadPicture("bitmaps\tlbr_w95\save.bmp")) ' 2
14.
15. Toolbar1.ImageList = ImageList1
16.
17. Dim btnX As Button
18. Set btnX = Toolbar1.Buttons.Add(, , , tbrSeparator)
19.
20. Set btnX = Toolbar1.Buttons.Add(, "open", , tbrDefault, "open")
21. btnX.ToolTipText = "Open File"
22. btnX.Description = btnX.ToolTipText
23.
24. Set btnX = Toolbar1.Buttons.Add(, , , tbrSeparator)
25.
26. Set btnX = Toolbar1.Buttons.Add(, "save", , tbrDefault, "save")
27. btnX.ToolTipText = "Save File"
28. btnX.Description = btnX.ToolTipText
29.
30. With Toolbar1
31. .Wrappable = True ' Buttons can wrap
32. .AllowCustomize = False
33. End With
34. End Sub
35. Add the following code to the ButtonClick event for Toolbar1:
36. Private Sub toolbar1_ButtonClick(ByVal Button As Button)
37. Select Case Button.Key
38. Case Is = "open"
39. MsgBox "Open button was clicked"
40.
41. Case Is = "save"
42. MsgBox "Save button was clicked"
43. End Select
44. End Sub
45. Add the following code to the MouseMove event for Toolbar1 (note that the
Private statement must be typed as a single line of code):
46. Private Sub Toolbar1_MouseMove(Button As Integer, Shift As Integer,
47. x As Single, y As Single)
48. Dim MyPlace As Integer
49. Dim LowPoint As Long
50. Dim HighPoint As Long
51. Static LastHit As Integer
52. MyPlace = 1
53.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


54. Do While MyPlace < Toolbar1.Buttons.Count
55. LowPoint = Toolbar1.Buttons(MyPlace).Left
56. HighPoint = Toolbar1.Buttons(MyPlace + 1).Left
57.
58. If x > LowPoint And x < HighPoint Then
59. If MyPlace <> LastHit Then
60. LastHit = MyPlace
61. StatusBar1.Panels(1).Text = "Found " & LastHit
62. Exit Do
63. End If
64. End If
65. MyPlace = MyPlace + 1
66. Loop
67.
68. If MyPlace = Toolbar1.Buttons.Count Then
69. LowPoint = Toolbar1.Buttons(MyPlace).Left
70. HighPoint = Toolbar1.Buttons(MyPlace).Left + Toolbar1.Buttons(MyPlace).Width
71. If x > LowPoint And x < HighPoint Then
72. If MyPlace <> LastHit Then
73. LastHit = MyPlace
74. StatusBar1.Panels(1).Text = "Found " & LastHit
75. End If
76. End If
77. End If
78.
79. StatusBar1.Panels(1).Text = ""
80. If LastHit = 2 Then
81. StatusBar1.Panels(1).Text = "Save"
82. End If
83. If LastHit = 4 Then
84. StatusBar1.Panels(1).Text = "Open"
85. End If
86. End Sub
Run the example program by pressing F5. A toolbar appears at the top of Form1. The
toolbar has two buttons, Save and Open. Move the mouse pointer over either one of these
buttons. A description corresponding to the specific button appears in the Status Bar
control. Double-click one of the buttons—a message box appears that indicates that you
selected that particular function from the Toolbar control.

195: Modifying a File's Date and Time


Stamp in Visual Basic 4.0
December 5, 1995

Abstract
In a Microsoft® Visual Basic® version 4.0 application, you may need to modify the date
and time stamp of a file. This article explains how this can be accomplished.

Setting a New Date and Time Stamp for a File

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To modify a file's date and time stamp in a Microsoft® Visual Basic® version 4.0
application, you must perform several steps. First, you need to call the Microsoft
Windows® messaging application programming interface (MAPI)
SystemTimeToFileTime function. This function converts a system time to a file time.
The system time you want to convert must be stored in a SYSTEMTIME structure. The
converted time is then stored in a FILETIME structure.
Next, you must use the Windows MAPI LocalFileTimeToFileTime function. This
function converts, as its name suggests, a local file time to a time that is based on the
Coordinated Universal Time (UTC). This newly converted time information can then be
used in your application by the Windows MAPI SetFileTimeWrite function.

You must retrieve a handle to the file whose date and time stamp you want to modify.
This can be done by calling the Windows MAPI CreateFile function. When you have the
file's handle, a call to the SetFileTimeWrite function actually updates the date and time
stamp of the file.

Example Program
This program shows how to modify the date and time stamp of a file.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
each Declare statement must be typed as a single line of code):
3. Private Type FILETIME
4. dwLowDateTime As Long
5. dwHighDateTime As Long
6. End Type
7.
8. Private Type SYSTEMTIME
9. wYear As Integer
10. wMonth As Integer
11. wDayOfWeek As Integer
12. wDay As Integer
13. wHour As Integer
14. wMinute As Integer
15. wSecond As Integer
16. wMilliseconds As Integer
17. End Type
18.
19. Private Const GENERIC_WRITE = &H40000000
20. Private Const OPEN_EXISTING = 3
21. Private Const FILE_SHARE_READ = &H1
22. Private Const FILE_SHARE_WRITE = &H2
23.
24. Private Declare Function SetFileTimeWrite Lib "kernel32" Alias
25. "SetFileTime" (ByVal hFile As Long, ByVal MullP As Long,
26. ByVal NullP2 As Long, lpLastWriteTime As FILETIME) As Long
27. Private Declare Function SystemTimeToFileTime Lib "kernel32"
28. (lpSystemTime As SYSTEMTIME, lpFileTime As FILETIME) As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


29. Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA"
30. (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal
31. dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal
32. dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long,
33. ByVal hTemplateFile As Long) As Long
34. Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long)
35. As Long
36. Private Declare Function LocalFileTimeToFileTime Lib "kernel32"
37. (lpLocalFileTime As FILETIME, lpFileTime As FILETIME) As Long
38. Add a Command Button control to Form1. Command1 is created by default.

39. Add the following code to the Click event for Command1:
40. Private Sub Command1_Click()
41. Dim Year As Integer, Month As Integer
42. Dim Day As Integer, Hour As Integer
43. Dim Minute As Integer, Second As Integer
44. Dim TimeStamp As Variant
45. Dim Filename As String
46. Dim X As Integer
47.
48. Year = 1996
49. Month = 1
50. Day = 1
51. Hour = 1
52. Minute = 0
53. Second = 0
54.
55. TimeStamp = DateSerial(Year, Month, Day) + TimeSerial(Hour, Minute, Second)
56. Filename = "c:\autoexec.bat"
57.
58. X = ModifyFileStamp(Filename, TimeStamp)
59. MsgBox "The time and date stamp was updated"
60. End Sub
61. Create a new function called ModifyFileStamp. Add the following code to this
function:
62. Function ModifyFileStamp(Filename As String, TimeStamp As Variant)
63. As Integer
64. Dim X As Long
65. Dim Handle As Long
66. Dim System_Time As SYSTEMTIME
67. Dim File_Time As FILETIME
68. Dim Local_Time As FILETIME
69.
70. System_Time.wYear = Year(TimeStamp)
71. System_Time.wMonth = Month(TimeStamp)
72. System_Time.wDay = Day(TimeStamp)
73. System_Time.wDayOfWeek = WeekDay(TimeStamp) - 1
74. System_Time.wHour = Hour(TimeStamp)
75. System_Time.wSecond = Second(TimeStamp)
76. System_Time.wMilliseconds = 0
77.
78. 'convert the system time to a file time
79. X = SystemTimeToFileTime(System_Time, Local_Time)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


80.
81. 'convert local file time to file time based on UTC
82. X = LocalFileTimeToFileTime(Local_Time, File_Time)
83.
84. 'open the file so we can get a file handle to the file
85. Handle = CreateFile(Filename, GENERIC_WRITE, FILE_SHARE_READ Or
86. FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, 0)
87.
88. 'now change the file time and date stamp
89. X = SetFileTimeWrite(Handle, ByVal 0&, ByVal 0&, File_Time)
90. CloseHandle Handle
91.
92. End Function
Run the example program by pressing F5. Click the Command Button control. The date
and time stamp of the AUTOEXEC.BAT file is immediately updated

196: Using the Common Dialog Control to


Invoke Context-Sensitive Help
December 5, 1995

Abstract
The Common Dialog control in Microsoft® Visual Basic® allows you to invoke Help
immediately and does not require that you use a dialog box. This article explains how to
use the Common Dialog control to access specific Microsoft Windows® Help files.

Accessing Context-Sensitive Help


Most Microsoft® Windows® users know they can get Help at any time by pressing the
F1 function key. When F1 is detected, Windows activates its WinHelp application. This
application then displays Help information for the item located under the mouse pointer.
This form of contextual user assistance is called context-sensitive Help.
You can easily add this technique to your own Microsoft Visual Basic® applications. Tip
15: Creating a List of Directories Stored on a Disk contains a full discussion of using the
Windows application programming interface (API) WinHelp function; but in this case,
you will use the Common Dialog control to access Help. It is sometimes preferable to
use the Help function of the Common Dialog control rather than using the WinHelp
function itself. The Common Dialog control allows you to invoke Help immediately and
does not require that you use a dialog box.
In the example program below, you use the Common Dialog control to call up the
Windows WinHelp application. To do this, you need only to set the Action property of
the Common Dialog control to a value of 6. This tells the control that you want to access
the Windows WinHelp application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Next, you set the HelpKey property of the Common Dialog control to the text with
which you want to invoke Help. This is the word or phrase you typed in the Text Box
control.

Finally, you set the HelpCommand property of the Common Dialog control to
HELP_KEY. This allows us to perform a context-sensitive search in the Help file.

Example Program
This program shows how to access a Windows Help file based on a specific string in a
Text Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following constants to the General Declarations section of Form1:


3. Const KEY_F1 = &H70
4. Const HELP_KEY = &H101
5. Add the following code to the Form_Load event for Form1:
6. Private Sub Form_Load()
7. KeyPreview = True
8. CommonDialog1.HelpFile = "c:\vb\vb.hlp"
9. End Sub
10. Add the following code to the Form_KeyDown event for Form1:
11. Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
12. If KeyCode = KEY_F1 Then
13. If TypeOf Me.ActiveControl Is TextBox Then
14. If ActiveControl.SelText <> "" Then
15. CommonDialog1.HelpKey = ActiveControl.SelText
16. CommonDialog1.HelpCommand = HELP_KEY
17. CommonDialog1.Action = 6
18. Else
19. MsgBox "Error - no phrase was selected to get help for"
20. End If
21. KeyCode = 0
22. Else
23. MsgBox "The Text Box must have the focus"
24. End If
25. End If
26.
27. End Sub
28. Add a Text Box control to Form1. Text1 is created by default.

29. Add a Common Dialog control to Form1. CommonDialog1 is created by default.


Run the example program by pressing F5. Type some text in the Text Box control, such
as "We need help on the CommonDialog Control right now." Highlight the phrase
"CommonDialog Control" in the Text Box control. Press F1. The program displays the
Help page for the Common Dialog control. Note that if you attempt to invoke Help
without first selecting a word or phrase in the Text Box control, an error message

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


appears. By the same token, if the Text Box control does not have the focus when you
press F1, an error is generated.

197: Preventing a Right-Click from


Displaying a Context Menu
December 5, 1995

Abstract
This article explains how to prevent the context menu from appearing when the user of
your Microsoft® Visual Basic® application uses the right mouse button to click a Text
Box control.

Using Message Blaster to Ignore Right-Clicks


A unique feature provided in the Microsoft® Windows® 95 operating system is its
ability to display a context menu for specific controls when you click with the right
mouse button (right-click) over a control. When developing a Microsoft Visual Basic®
application, however, you may not want this context menu to appear.

Each time you right-click, the system receives a WM_RBUTTONDOWN message.


Using a third-party custom control such as Message Blaster, you can tell the operating
system to ignore this WM_RBUTTONDOWN message.

In the example program below, you want to ignore all right-clicks for the Text Box
control. To accomplish this, you supply Message Blaster with the handle of the Text
Box control and the message you want to trap.
If a right-click is detected while the program is running, the Message Blaster custom
control fools the operating system into believing that no such action was received.
Message Blaster is used to send an EATMESSAGE message to the operating system.
This, in effect, prevents the context menu from appearing.

Example Program
This program shows how to prevent the context menu from appearing when you right-
click on a Text Box control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Const EATMESSAGE = 0
4. Const WM_RBUTTONDOWN = &H204
5. Add the following code to the Form_Load event for Form1:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


6. Private Sub Form_Load()
7. MsgBlaster1.hWndTarget = Text1.hWnd
8. MsgBlaster1.AddMessage WM_RBUTTONDOWN, POSTPROCESS
9. End Sub
10. Add a Text Box control to Form1. Text1 is created by default.

11. Add a Message Blaster custom control to Form1. MsgBlaster1 is created by


default.

12. Add the following code to the MsgBlaster1_Message event:


13. Private Sub MsgBlaster1_Message(ByVal hWnd As Long, ByVal Msg As Long,
14. wParam As Long, lParam As Long, nPassage As Integer, lReturnValue As Long)
15. Select Case Msg
16.
17. Case WM_RBUTTONDOWN
18. 'MsgBox "right mouse was clicked on text box"
19. nPassage = EATMESSAGE
20.
21. End Select
22. End Sub
Run the example program by pressing F5. While typing text in the Text Box control,
click the right mouse button. The context menu does not appear on the screen.

198: Retrieving File Information in Visual


Basic 4.0
December 5, 1995

Abstract
This article explains how, from within a Microsoft® Visual Basic® version 4.0
application, you can retrieve information about a specific file stored on disk. This
information may include the date and time when the file was initially created, when the
file was last accessed, and any number of other details.

Using the GetFileInformationByHandle Function


In some Microsoft® Visual Basic® version 4.0 applications you write, you may need to
examine certain file information. For example, you may need to determine when a file
was last accessed or the serial number of the volume that contains the file. This type of
information about a file can be retrieved using the Microsoft Windows® application
programming interface (API) GetFileInformationByHandle function.
To use the GetFileInformationByHandle function, you must include the following
Declare statement in your project:
Declare Function GetFileInformationByHandle Lib "kernel32" (ByVal hFile

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


As Long, lpFileInformation As BY_HANDLE_FILE_INFORMATION) As Long
As you can see, this function requires only two arguments: the handle of the file you want
to retrieve information on and the address of a BY_HANDLE_FILE_INFORMATION
structure that will hold the file's information.
There are several steps you must perform to retrieve information about a file. First, you
must use the Windows API OpenFile function to obtain the specified file's file handle.
Once you have the file handle, you can call the GetFileInformationByHandle function.
The file's information is then stored in the BY_HANDLE_FILE_INFORMATION
structure. Next, you must use the Windows API CloseHandle function to release the file
handle to the system.
In the example program below, you retrieve the date and time that the file was actually
created. You then use the Windows API FileTimeToSystemTime function to convert the
file's date and time stamp to individual values that you can use in the program. This same
procedure is used to process the file's last access and last write date and time stamp. In
addition, two other pieces of information are also retrieved about the file—the file's
attributes and the volume's serial number.

Example Program
This program shows how to retrieve information about a specific file.

1. Create a new project in Visual Basic. Form1 is created by default.

2. From the Visual Basic Insert menu, select Module to create a new module.
Module1.Bas is created by default.

3. Add the following code to Module1.Bas (note that each Declare statement must
be typed as a single line of code):
4. Declare Function OpenFile Lib "kernel32" (ByVal lpFileName As String,
5. lpReOpenBuff As OFSTRUCT, ByVal wStyle As Long) As Long
6. Declare Function GetFileInformationByHandle Lib "kernel32" (ByVal hFile
7. As Long, lpFileInformation As BY_HANDLE_FILE_INFORMATION) As Long
8. Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
9. Declare Function FileTimeToSystemTime Lib "kernel32" (lpFileTime As FileTime,
10. lpSystemTime As SYSTEMTIME) As Long
11. Public Const OFS_MAXPATHNAME = 128
12. Public Const OF_READ = &H0
13. Type OFSTRUCT
14. cBytes As Byte
15. fFixedDisk As Byte
16. nErrCode As Integer
17. Reserved1 As Integer
18. Reserved2 As Integer
19. szPathName(OFS_MAXPATHNAME) As Byte
20. End Type
21. Type SYSTEMTIME
22. wYear As Integer
23. wMonth As Integer

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


24. wDayOfWeek As Integer
25. wDay As Integer
26. wHour As Integer
27. wMinute As Integer
28. wSecond As Integer
29. wMilliseconds As Integer
30. End Type
31. Type FileTime
32. dwLowDateTime As Long
33. dwHighDateTime As Long
34. End Type
35. Type BY_HANDLE_FILE_INFORMATION
36. dwFileAttributes As Long
37. ftCreationTime As FileTime
38. ftLastAccessTime As FileTime
39. ftLastWriteTime As FileTime
40. dwVolumeSerialNumber As Long
41. nFileSizeHigh As Long
42. nFileSizeLow As Long
43. nNumberOfLinks As Long
44. nFileIndexHigh As Long
45. nFileIndexLow As Long
46. End Type
47. Add a Command Button control to Form1. Command1 is created by default.

48. Add the following code to the Click event for Command1:
49. Private Sub Command1_Click()
50. Dim Ret As Long
51. Dim FileHandle As Long
52. Dim FileInfo As BY_HANDLE_FILE_INFORMATION
53. Dim lpReOpenBuff As OFSTRUCT
54. Dim FileTime As SYSTEMTIME
55.
56. FileHandle = OpenFile("c:\autoexec.bat", lpReOpenBuff, OF_READ)
57. Ret = GetFileInformationByHandle(FileHandle, FileInfo)
58.
59. Ret = FileTimeToSystemTime(FileInfo.ftCreationTime, FileTime)
60. Print "File created on " & FileTime.wYear, FileTime.wMonth,
61. FileTime.wDay
62.
63. Ret = FileTimeToSystemTime(FileInfo.ftLastAccessTime, FileTime)
64. Print "File last accessed on: " & FileTime.wYear, FileTime.wMonth,
65. FileTime.wDay
66.
67. Ret = FileTimeToSystemTime(FileInfo.ftLastWriteTime, FileTime)
68. Print "File last written to: " & FileTime.wYear, FileTime.wMonth,
69. FileTime.wDay
70.
71. Print "Volume Serial Number is: " & FileInfo.dwVolumeSerialNumber
72. Print "File attributes are: " & FileInfo.dwFileAttributes
73.
74.
75. Ret = CloseHandle(FileHandle)
76. End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Run the example program by pressing F5. Click the Command Button control. The
program displays the file's creation date, last access date, and last write date, as well as
the disk's volume serial number and the attributes associated with the file.

199: Saving the Contents of the List Box


Control to Disk in Visual Basic 4.0
December 5, 1995

Abstract
This article explains how to save and later retrieve the contents of the Microsoft® Visual
Basic® version 4.0 List Box control to and from a file on disk.

Using Arrays with the List Box Control


The Microsoft® Visual Basic® version 4.0 List Box control lets you display a list of
items, such as employee names, to your user. In your Visual Basic application, you may
allow your user to add new items to the control. Before you quit your application,
however, you need to save the entries in the List Box control to a file on disk. Then,
when your application is run at a later time, you can retrieve the items to the List Box
control from the data file's contents.

You can save the contents of a List Box control to a sequential file on disk by first saving
each entry in the control to an array. To do this, you would run the following code
fragment:
For i = 0 To List1.ListCount - 1
A(i) = List1.List(i)
Next I
The total number of items stored in the List Box control is retrieved from the ListCount
property. Next, the array A(i) is created, where the i variable holds a count of the current
item being processed. As each item is retrieved from the List Box control, it is saved in
the array.
At this point, you use the Visual Basic Write # statement to save each item in the array to
a disk file. Similarly, when you reload the items from the disk file to the List Box
control, you use the Input # statement.

Example Program
This program shows how to save the contents of a List Box control to a file on disk. In
addition, you are shown how to retrieve the same items from the data file to the List Box
control.
1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following code to the General Declarations section of Form1:
3. Dim A(50) As String
4. Dim ItemCount As Integer
5. Add the following code to the Form_Load event for Form1:
6. Private Sub Form_Load()
7. For x = 1 To 20
8. List1.AddItem "This is item #" & x
9. Next x
10. End Sub
11. Add a List Box control to Form1. List1 is created by default.

12. Add a Command Button control to Form1. Command1 is created by default.

13. Add the following code to the Click event for Command1:
14. Private Sub Command1_Click()
15. For i = 0 To List1.ListCount - 1
16. A(i) = List1.List(i)
17. Next i
18. ItemCount = List1.ListCount
19. Open "c:\temp.txt" For Output As #1
20. For i = 0 To ItemCount - 1
21. Write #1, A(i)
22. Next i
23. Close #1
24. MsgBox "Data has been saved"
25.
26. End Sub
27. Add a second Command Button control to Form1. Command2 is created by
default.

28. Add the following code to the Click event for Command2:
29. Private Sub Command2_Click()
30. List1.Clear
31. Open "c:\temp.txt" For Input As #1
32. Do Until EOF(1)
33. Input #1, b$
34. List1.AddItem b$
35. Loop
36. Close #1
37.
38. End Sub
Run the example program by pressing F5. The program displays twenty items in the List
Box control. Click the first Command Button control. The contents of the List Box
control are saved to a file on disk. Click the second Command Button control. The
contents of the file on disk are retrieved into the List Box control.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


200: Determining the Amount of Free
Disk Space
December 5, 1995

Abstract
This article explains how to retrieve the amount of available disk space from within a
Microsoft® Visual Basic® application.

Using the GetDiskFreeSpace Function


When writing information to disk from within a Microsoft® Visual Basic® application,
you may need to determine whether there is enough space available on the disk drive
before starting to write the new data to it.
The Microsoft Windows® application programming interface (API) GetDiskFreeSpace
function allows you to calculate how much free space there is on a specified disk drive.
To use this function, include the following Declare statement in the General Declarations
section of your application:
Declare Function GetDiskFreeSpace Lib "kernel32" Alias "GetDiskFreeSpaceA"
(ByVal lpRootPathName As String, lpSectorsPerCluster As Long,
lpBytesPerSector As Long, lpNumberOfFreeClusters As Long,
lpTotalNumberOfClusters As Long) As Long
The GetDiskFreeSpace function requires five arguments, as follows:
lpRootPathName A string containing the root directory for the disk drive for
which you want to retrieve information. If NULL, the default
directory's root path is used.
LpSectorsPerCluster A long value that will contain the number of sectors per
cluster.
LpBytesPerSector A long value that will contain the number of bytes per sector.
LpNumberOfFreeClusters A long value that will contain the number of free clusters on
the disk.
LpTotalNumberOfClusters A long value that will contain the number of clusters on the
disk.

After executing the GetDiskFreeSpace function, either a value of True is returned if the
function was successful or a value of False if the function was not successful.
To calculate the total number of bytes available on the disk, you need to multiply the
number of bytes per sector by the number of sectors per cluster. Then, multiply this result
by the number of free clusters on the disk. This total gives you the number of bytes of
free space on the disk.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Example Program
This program shows how to retrieve the amount of free disk space.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Text Box control to Form1. Text1 is created by default. Set its MultiLine
property to True.

3. Add a Command Button control to Form1. Command1 is created by default.

4. Add the following code to the Click event for Command1:


5. Private Sub Command1_Click()
6. Dim X As Long
7. X = GetDiskSpace("c:\")
8. If X Then
9. sFreeSpace = Format$(CurrentDisk.FreeBytes, "###,###,##0")
10. sTotalSpace = Format$(CurrentDisk.TotalBytes, "###,###,##0")
11. sFreePcnt = Format$(CurrentDisk.FreePcnt, "Percent")
12. sUsedPcnt = Format$(CurrentDisk.UsedPcnt, "Percent")
13. End If
14. text1.Text = "Free Space: " & sFreeSpace & " Percent: "
15. & sFreePcnt & Chr(13) & Chr(10)
16. text1.Text = text1.Text & "Total Bytes free: " & sTotalSpace &
17. " Percent: " & sUsedPcnt
18. End Sub
19. Create a new function called GetDiskSpace. Add the following code to this
function (note that the "X =" line must be typed as a single line of code):
20. Function GetDiskSpace(sRootPathName As String) As Long
21. Dim X As Long
22. Dim lSectorsPerCluster As Long, lBytesPerSector As Long
23. Dim lNumberOfFreeClusters As Long, lTotalNumberOfClusters As Long
24.
25. X = GetDiskFreeSpace(sRootPathName, lSectorsPerCluster, lBytesPerSector,
26. lNumberOfFreeClusters, lTotalNumberOfClusters)
27. GetDiskSpace = X
28.
29. If X Then
30. CurrentDisk.RootPath = sRootPathName
31. CurrentDisk.FreeBytes = lBytesPerSector * lSectorsPerCluster *
32. lNumberOfFreeClusters
33. CurrentDisk.TotalBytes = lBytesPerSector * lSectorsPerCluster *
34. lTotalNumberOfClusters
35. CurrentDisk.FreePcnt = (CurrentDisk.TotalBytes - CurrentDisk.FreeBytes)
36. / CurrentDisk.TotalBytes
37. CurrentDisk.UsedPcnt = CurrentDisk.FreeBytes / CurrentDisk.TotalBytes
38. Else
39. CurrentDisk.RootPath = ""
40. CurrentDisk.FreeBytes = 0
41. CurrentDisk.TotalBytes = 0
42. CurrentDisk.FreePcnt = 0

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


43. CurrentDisk.UsedPcnt = 0
44. Exit Function
45. End If
46. End Function
47. From the Visual Basic Insert menu, select Module to create a new module.
Module1.Bas is created by default.

48. Add the following Type, Constant, and Declare statements to Module1.Bas
(note that the Declare statement must be typed as a single line of code):
49. Declare Function GetDiskFreeSpace Lib "kernel32" Alias "GetDiskFreeSpaceA"
50. (ByVal lpRootPathName As String, lpSectorsPerCluster As Long,
51. lpBytesPerSector As Long, lpNumberOfFreeClusters As Long,
52. lpTotalNumberOfClusters As Long) As Long
53. Type DISKSPACEINFO
54. RootPath As String * 3
55. FreeBytes As Long
56. TotalBytes As Long
57. FreePcnt As Single
58. UsedPcnt As Single
59. End Type
60. Global CurrentDisk As DISKSPACEINFO
Run the example program by pressing F5. Click the Command Button control. The
program displays the number and percentage of free bytes of disk space, and the total
number and percentage of bytes used.

201: Retrieving the Task List in Windows


95
December 5, 1995

Abstract
This article explains how to create a list of all currently running tasks on the Microsoft®
Windows® 95 operating system.

Determining Which Tasks Are Running


With the Microsoft® Windows® 95 operating system, you can run any number of
applications simultaneously. Occasionally, you may need to determine which tasks are
currently being run. This can be accomplished by using several Windows application
programming interface (API) functions.
To find the names of all currently executing tasks, you must first determine the handle of
the window that is currently at the top of the z-order. This, of course, would be the
window of your own Microsoft Visual Basic® application. You can use the Windows
API GetWindow function to retrieve the handle of your application's window with the
statement:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


CurrWnd = GetWindow(Form1.hwnd, GW_HWNDFIRST)
The first argument of the GetWindow function is the handle of the window that is at the
top of the z-order. In this case, this is the handle of Form1.
The second argument of the GetWindow function specifies the window you want to
retrieve the handle for. This argument can have one of the following values:
GW_CHILD Retrieve the handle for the child window.
GW_HWNDFIRST Retrieve the handle for the window at the top of the z-order.
GW_HWNDLAST Retrieve the handle for the window at the bottom of the z-order.
GW_HWNDNEXT Retrieve the handle of the window below the specified window in
the z-order.
GW_HWNDPREV Retrieve the handle of the window above the specified window in
the z-order.
GW_OWNER Retrieve the handle of the window that owns the specified window,
if any.

After you have retrieved the application's window handle, you can use the Windows API
GetParent function to retrieve this window's child window handle. Next, you call the
Windows API GetWindowText and GetWindowTextLength functions to retrieve the
text in the window's title bar and the length of this text, respectively. You can then use
the text string in your own application. For example, you can save the title bar text to a
List Box control.

All of the above steps are repeated until you have processed all running tasks. You know
that you have gone through each task when the current window is that of your own
application.

Example Program
This program shows how to create a list of all currently running tasks in Windows 95.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that each Declare statement must be typed as a single line
of code):
3. Private Declare Function GetWindow Lib "user32"
4. (ByVal hwnd As Long, ByVal wCmd As Long) As Long
5. Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
6. Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA"
(ByVal hwnd As Long) As Long
7. Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA"
8. (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
9. Const GW_HWNDFIRST = 0
10. Const GW_HWNDNEXT = 2

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


11. Add a Command Button control to Form1. Command1 is created by default.

12. Add the following code to the Click event for Command1:
13. Private Sub Command1_Click()
14. LoadTaskList
15. End Sub
16. Add a List Box control to Form1. List1 is created by default.

17. Create a new subroutine called LoadTaskList. Add the following code to this
subroutine:
18. Sub LoadTaskList()
19. Dim CurrWnd As Long
20. Dim Length As Long
21. Dim TaskName As String
22. Dim Parent As Long
23.
24. List1.Clear
25. CurrWnd = GetWindow(Form1.hwnd, GW_HWNDFIRST)
26.
27. While CurrWnd <> 0
28. Parent = GetParent(CurrWnd)
29. Length = GetWindowTextLength(CurrWnd)
30. TaskName = Space$(Length + 1)
31. Length = GetWindowText(CurrWnd, TaskName, Length + 1)
32. TaskName = Left$(TaskName, Len(TaskName) - 1)
33. If Length > 0 Then
34. If TaskName <> Me.Caption Then
35. List1.AddItem TaskName
36. End If
37. End If
38. CurrWnd = GetWindow(CurrWnd, GW_HWNDNEXT)
39. DoEvents
40. Wend
41. End Sub
Run the example program by pressing F5. Click the Command Button control. A list of
all currently running tasks on the Windows 95 operating system appears in the List Box
control.

202: Forcing a Combo Box Control to


Drop Down with the ENTER Key
February 28, 1996

Abstract
This article explains how to force a Combo Box control to drop down its list box when
the ENTER key is pressed in a Microsoft® Visual Basic® application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Monitoring the KeyPress Event
The Microsoft® Visual Basic® Combo Box control lets your user select from its list box
or optionally add a new item to the list box. A Combo Box control has three possible
styles—Dropdown Combo, Simple Combo, and Dropdown List.

The Dropdown Combo style of the Combo Box control is displayed with the arrow and
text box portion of the control slightly separated. You must click the down arrow to select
items from the Combo Box control.

The Simple Combo style of the Combo Box control displays its list box immediately.
The disadvantage of this style is that it takes up more space in your window.
The Dropdown List style of the Combo Box control is almost identical to the
Dropdown Combo style. However, this style does not allow you to modify the Text
property of this control.

Note that most applications use a Combo Box control with its Style property set to
Dropdown Combo. Instead of clicking on the arrow to cause the Combo Box control to
display its list box, it is more convenient to press the ENTER key.
Each time a key is pressed, the Combo Box control's KeyPress event is triggered
(providing that the Combo Box control has the focus, of course). By monitoring this
event, you can determine whether a user pressed ENTER. You know the user pressed
ENTER if the KeyAscii value is 13.
After you have determined that ENTER was indeed pressed, you send a
CB_SHOWDROPDOWN message to the Combo Box control by using the Microsoft
Windows® application programming interface (API) SendMessage function. If this
message is sent to the control with a parameter of True, the Combo Box control drops
down its list box. If this message is sent to the Combo Box control with a parameter of
False, however, the list box would not be dropped down.

Example Program
This program shows how to force a Combo Box control to drop down when the ENTER
key is pressed.

1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Const WM_USER = &H400
4. Const CB_SHOWDROPDOWN = &H14F
5. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"
6. (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As
7. Long) As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Add the following code to the Form_Load event for Form1:
9. Private Sub Form_Load()
10. Combo1.AddItem "Oranges"
11. Combo1.AddItem "Peaches"
12. Combo1.AddItem "Apples"
13. End Sub
14. Add a Combo Box control to Form1. Combo1 is created by default.

15. Add the following code to the KeyPress event for Combo1:
16. Private Sub Combo1_KeyPress(KeyAscii As Integer)
17. Dim I As Long
18. If KeyAscii = 13 Then
19. I = SendMessage(Combo1.hwnd, CB_SHOWDROPDOWN, True, 0&)
20. End If
21. End Sub
Run the example program by pressing F5. While the Combo Box control has the focus,
press ENTER. The Combo Box control displays its list box.

203: Searching a Disk for Files Based on


Filename and Path
February 28, 1996

Abstract
This article includes a Microsoft® Visual Basic® version 4.0 program that runs on the
Microsoft Windows® 95 operating system. You can use this program to search a disk
drive for a file or group of files. The search can be initiated from the root directory of the
disk drive or from any specific directory. The found files are listed in a List Box control
with their corresponding paths.

Using a Recursive Search Routine to Find Files on a


Disk
When you are running the Microsoft® Windows® 95 operating system, you can
selectively search a disk for a specific file in a directory by using the Visual Basic®
version 4.0 Dir function. However, when you want to find all occurrences of a specific
file in a directory or when you want to find all matching wildcard filenames on a disk,
you need to perform a recursive search routine.
The example program below lets you search a disk for a file or group of files matching
the target filename. The target filename can be any valid MS-DOS® filename, including
wildcard filenames. In addition, you can specify the directory from which you want to
start the search process.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


To find all occurrences of the target filename, a hidden List Box control is used to hold
the names of all directories in which the search is to be performed. The Dir[$]() function
is used in a Do-While loop to retrieve the names of all subdirectories under the target
directory. As each directory name is found, its name is added to the hidden List Box
control.

When you have retrieved the names of all directories stored under the target directory,
you call the List_Files procedure. The List_Files procedure then performs another
recursive search routine by using the same technique as the Get_Files procedure to find
all files that match the target filename. The full path of the found file is added to the
second List Box control. When all files and directories have been processed, the program
quits.

Example Program
This program shows how to search for a filename by specifying a directory from which to
begin the search.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Dim XFilename As String
4. Dim StrtPath As String
5. Create a new procedure called Get_Files. Add the following code to this
procedure:
6. Private Sub Get_Files(FPath As String)
7. Dim File_Name As String
8. Dim File_Path As String
9. Dim File_Read As Integer
10. Dim X As Boolean
11. Dim I As Integer
12.
13. File_Path = FPath & "\"
14. File_Name = Dir$(File_Path, vbDirectory)
15. File_Read = 1
16. X = False
17.
18. Do While File_Name <> ""
19. If File_Name <> "." And File_Name <> ".." Then
20. If GetAttr(File_Path & File_Name) = vbDirectory Then
21. StrtPath = File_Path & File_Name
22. List1.AddItem StrtPath
23. X = True
24. Get_Files StrtPath
25. End If
26. End If
27. If X = True Then
28. File_Name = Dir$(File_Path, vbDirectory)
29. For I = 2 To File_Read
30. File_Name = Dir$

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


31. Next
32. X = False
33. End If
34. File_Name = Dir$
35. File_Read = File_Read + 1
36. Loop
37. End Sub
38. Create a new procedure called List_Files. Add the following code to this
procedure:
39. Private Sub List_Files()
40. Dim XIndex As Integer
41. Dim XName As String
42. Dim X_Filename As String
43.
44. For XIndex = 0 To List1.ListCount - 1
45. XName = List1.List(XIndex) & "\" & XFilename
46. X_Filename = Dir(XName)
47. If X_Filename <> "" Then
48. List2.AddItem List1.List(XIndex) & "\" & X_Filename
49. Do While True
50. X_Filename = Dir
51. On Error GoTo exit_loop
52. If X_Filename <> "" Then
53. List2.AddItem List1.List(XIndex) & "\" & X_Filename
54. Else
55. Exit Do
56. End If
57. Loop
58. End If
59. exit_loop:
60. Next XIndex
61. End Sub
62. Add a Label control to Form1. Label1 is created by default. Set its Caption
property to "Directory".

63. Add a Text Box control to Form1. Text1 is created by default. Set its Text
property to an empty string.

64. Add a second Label control to Form1. Label2 is created by default. Set its
Caption property to "Filename".

65. Add a second Text Box control to Form1. Text2 is created by default. Set its Text
property to an empty string.

66. Add a List Box control to Form1. List1 is created by default. Set its Visible
property to False.

67. Add a second List Box control to Form1. List2 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


68. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Search".

69. Add the following code to the Click event for Command1:
70. Private Sub Command1_Click()
71. Dim XLen As Integer
72.
73. XLen = Len(Text1.Text)
74.
75. If Mid$(Text1.Text, XLen, 1) = "\" Then
76. StrtPath = Left$(Text1.Text, XLen - 1)
77. Else
78. StrtPath = Text1.Text
79. End If
80.
81. XFilename = Text2.Text
82.
83. List1.Clear
84. List2.Clear
85.
86. List1.AddItem StrtPath
87. Get_Files StrtPath
88. List_Files
89.
90. If List2.ListCount = 0 Then
91. MsgBox "Unable to find file"
92. Else
93. MsgBox "Search completed"
94. End If
95. End Sub
Run the example program by pressing F5. Suppose that you want to find all files with the
extension .TXT in the directory C:\DOCS. In the first Text Box control, type the name of
the directory from which you want to start the search. Next, type the name of the file you
want to find in this starting directory. You may use any valid MS-DOS filename, as well
as wildcard characters. Click the Search button. The program displays the results of its
search in the List Box control and displays a message box indicating that the search has
been completed.

204: Changing the Screen Resolution at


Run Time in Visual Basic 4.0
February 28, 1996

Abstract
Because Microsoft Windows 95 users may have different screen resolution settings, you
may need to set the screen resolution to a specific setting while your Microsoft® Visual

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Basic® version 4.0 application is running. This article explains how to change your
screen resolution from within Visual Basic.

Setting the Screen Resolution


Under the Microsoft® Windows® 95 operating system, you can set your screen
resolution by running the Display applet in Control Panel. In a Microsoft Visual Basic®
version 4.0 application, you can use the Windows application programming interface
(API) EnumDisplaySettings and ChangeDisplaySettings functions to change the screen
resolution while your program is running.
The EnumDisplaySettings function allows you to retrieve information about your
display's graphics modes. This information is then stored in a DEVMODE structure.
After you have interrogated the computer system with the EnumDisplaySettings
function, you use the ChangeDisplaySettings function to tell the operating system to use
a different screen resolution.
The ChangeDisplaySettings function lets you set the screen resolution to a different
graphics mode. The DEVMODE structure holds the graphics mode information to which
you want to change.
In the example program below, you first retrieve the current screen resolution
information by calling the EnumDisplaySettings function. The DEVMODE structure
contains the graphics modes information for the display type. Next, you modify the
dmPelsWidth and dmPelsHeight fields in the DEVMODE structure to reflect the new
screen resolution you want to set. Finally, you call the ChangeDisplaySettings function
to tell the operating system to set the new screen resolution as the default resolution.

Example Program
This program shows how to set the screen resolution from within a Visual Basic
application.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
each Declare statement must be typed as a single line of code):
3. Option Explicit
4. Private Declare Function EnumDisplaySettings Lib "user32" Alias
5. "EnumDisplaySettingsA" (ByVal lpszDeviceName As Long,
6. ByVal iModeNum As Long, lpDevMode As Any) As Boolean
7.
8. Private Declare Function ChangeDisplaySettings Lib "user32" Alias
9. "ChangeDisplaySettingsA" (lpDevMode As Any, ByVal dwflags As Long) As Long
10.
11. Const CCDEVICENAME = 32
12. Const CCFORMNAME = 32
13. Const DM_PELSWIDTH = &H80000

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


14. Const DM_PELSHEIGHT = &H100000
15.
16. Private Type DEVMODE
17. dmDeviceName As String * CCDEVICENAME
18. dmSpecVersion As Integer
19. dmDriverVersion As Integer
20. dmSize As Integer
21. dmDriverExtra As Integer
22.
23. dmFields As Long
24. dmOrientation As Integer
25. dmPaperSize As Integer
26. dmPaperLength As Integer
27. dmPaperWidth As Integer
28. dmScale As Integer
29. dmCopies As Integer
30. dmDefaultSource As Integer
31. dmPrintQuality As Integer
32. dmColor As Integer
33. dmDuplex As Integer
34. dmYResolution As Integer
35. dmTTOption As Integer
36. dmCollate As Integer
37.
38. dmFormName As String * CCFORMNAME
39. dmUnusedPadding As Integer
40. dmBitsPerPel As Integer
41. dmPelsWidth As Long
42. dmPelsHeight As Long
43. dmDisplayFlags As Long
44. dmDisplayFrequency As Long
45. End Type
46. Dim DevM As DEVMODE
47. Add a Command Button control to Form1. Command1 is created by default.

48. Add the following code to the Click event for Command1:
49. Private Sub Command1_Click()
50. Dim a As Boolean
51. Dim i&
52. i=0
53. Do
54. a = EnumDisplaySettings(0&, i&, DevM)
55. i=i+1
56. Loop Until (a = False)
57. End Sub
58. Add a second Command Button control to Form1. Command2 is created by
default.

59. Add the following code to the Click event for Command2:
60. Private Sub Command2_Click()
61. Dim b&
62. DevM.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


63.
64. DevM.dmPelsWidth = 800
65. DevM.dmPelsHeight = 600
66.
67. b = ChangeDisplaySettings(DevM, 0)
68. End Sub
Run the example program by pressing Click the first Command Button control. This
retrieves all the graphics modes for your display. Next, click the second Command
Button control to change the display's screen resolution to 800 x 600 graphics mode

205: Drawing a Gradient Background


Pattern on Forms in Visual Basic 4.0
February 28, 1996

Abstract
The Microsoft® Visual Basic® version 4.0 Setup program allows you to create a
standard installation program for your Visual Basic application. The Setup program, as
with other Microsoft Setup programs, draws a gradient background pattern, usually in a
blue color. This article explains how to draw gradient backgrounds in a Visual Basic
application.

Using the RealizePalette Function to Draw


Backgrounds
Most Microsoft® Windows® applications are installed by running a Setup program.
These setup programs often display a window in which appears a faded blue background.
This same effect can be reproduced in Microsoft Visual Basic® version 4.0 applications
by using the Windows application programming interface (API) RealizePalette function.

To display a window with a gradient background, you simply paint thin rectangles in
different colors on the form. This gives the form a fade-to-black effect.
The RealizePalette function lets you select a logical palette for a device context, such as
a window. This allows you to use a larger number of colors in your application without
interfering with the colors used by other forms (windows) in your program. After calling
the RealizePalette function, you need only select the colors with which you want to paint
the rectangles and the gradient background effect is created.

Example Program
This program shows how to draw a gradient background on a form/window in Visual
Basic.
1. Create a new project in Visual Basic. Form1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2. Add the following code to the General Declarations section of Form1 (note that
the Declare statement must be typed as a single line of code):
3. Private Declare Function RealizePalette Lib "gdi32"
4. (ByVal hdc As Long) As Long
5.
6. Dim RedColor(256) As Integer
7. Dim GreenColor(256) As Integer
8. Dim BlueColor(256) As Integer
9. Add the following code to the Form_Load event for Form1:
10. Private Sub Form_Load()
11. For I = 1 To 256
12. RedColor(I) = 1
13. GreenColor(I) = 1
14. BlueColor(I) = I
15. Next I
16. End Sub
17. Add a Command Button control to Form1. Command1 is created by default.

18. Add the following code to the Click event for Command1:
19. Private Sub Command1_Click()
20. dummy = RealizePalette(Form1.hdc)
21. Form1.Scale (0, 0)-(256, 1)
22. For I = 0 To 255
23. Form1.Line (I, 0)-(I + 1, 1), RGB(RedColor(I + 1), _
24. GreenColor(I + 1), BlueColor(I + 1)), BF
25. Form1.ForeColor = RGB(RedColor(I + 1), GreenColor(I + 1), _
26. BlueColor(I + 1))
27. Next I
28. End Sub
29. Add a second Command Button control to Form1. Command2 is created by
default.

30. Add the following code to the Click event for Command2:
31. Private Sub Command2_Click()
32. dummy = RealizePalette(Form1.hdc)
33. Form1.Scale (0, 0)-(1, 256)
34. For I = 0 To 255
35. Form1.Line (0, I)-(1, I + 1), RGB(RedColor(I + 1), _
36. GreenColor(I + 1), BlueColor(I + 1)), BF
37. Form1.ForeColor = RGB(RedColor(I + 1), GreenColor(I + 1), _
38. BlueColor(I + 1))
39. Next I
40. End Sub
Run the example program by pressing When you click the first Command Button
control, the program draws the black-to-blue gradient background horizontally across the
form. Clicking the second Command Button control instructs the program to draw the
gradient background vertically down the form.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


206: Populating the Outline Control with
a List of Directories and Files Stored on a
Disk
February 28, 1996

Abstract
The Microsoft® Windows® File Manager and Explorer applications use an Outline
control to display directories and files in a hierarchical list. This article explains how to
populate the Microsoft Visual Basic® version 4.0 Outline control using this same
technique.

Using the Outline Control


The Microsoft® Visual Basic® version 4.0 Outline control is an advanced List Box
control that allows you to display a hierarchical list of items to your user. The Outline
control allows you to present items with indentation levels that can be expanded (the
subordinate items become visible) or collapsed (the subordinate items become invisible).
This means that a directory can be saved to the Outline control at one level of indentation
and the files stored within that directory can be stored at a secondary level of indentation.
The example program below populates an Outline control with all directories and files
stored on the selected disk. This feat is accomplished by performing a recursive search
through the disk's file structure. The name of a file is retrieved from the disk's directory.
If this entry is a directory, it is added to the Outline control as a first-level indentation
item. However, if the entry is a file, the entry is added to the Outline control at the next
level of indentation. This action is repeated until every directory and file has been
processed.

Example Program
This program shows how to populate the Outline control with all directory and files
stored on a disk drive.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Option Explicit
4. Dim FIndent As Integer
5. Dim FIndex As Integer
6. Dim StrtPath As String
7. Add an Outline control to Form1. Outline1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


8. Add a Drive List Box control to Form1. Drive1 is created by default.

9. Add the following code to the Change event for Drive1:


10. Private Sub Drive1_Change()
11. Outline1.Clear
12. Outline1.AddItem Drive1.Drive & "\"
13. FIndent = 1
14. FIndex = 0
15. StrtPath = Drive1.Drive
16. Get_Files StrtPath
17. MsgBox "OK"
18. End Sub
19. Create a new procedure called Get_Files. Add the following code to this
procedure:
20. Private Sub Get_Files(FPath As String)
21. Dim File_Name As String
22. Dim File_Path As String
23. Dim File_Read As Integer
24. Dim X As Boolean
25. Dim I As Integer
26.
27. FIndent = FIndent + 1
28. File_Path = FPath & "\"
29. File_Name = Dir$(File_Path, vbDirectory)
30. File_Read = 1
31. X = False
32.
33. Do While File_Name <> ""
34. If File_Name <> "." And File_Name <> ".." Then
35. If GetAttr(File_Path & File_Name) <> vbDirectory Then
36. Outline1.AddItem File_Name
37. FIndex = FIndex + 1
38. Outline1.Indent(FIndex) = FIndent
39. Else
40. StrtPath = File_Path & File_Name
41. Outline1.AddItem File_Name
42. FIndex = FIndex + 1
43. Outline1.Indent(FIndex) = FIndent
44. X = True
45. Get_Files StrtPath
46. End If
47. End If
48. If X = True Then
49. File_Name = Dir$(File_Path, vbDirectory)
50. For I = 2 To File_Read
51. File_Name = Dir$
52. Next
53. X = False
54. End If
55. File_Name = Dir$
56. File_Read = File_Read + 1
57. Loop
58. FIndent = FIndent - 1

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


59. End Sub
Run the example program by pressing Select a disk drive from the Drive List Box
control by clicking the desired drive letter. The program will display a message box when
it has populated the Outline control with all directories and files found on the target disk
drive.

207: Retrieving the Short (MS-DOS)


Filename from a Long Filename in
Windows 95
February 28, 1996

Abstract
Under the Microsoft® Windows® 95 operating system, you can assign a long filename to
a file instead of being limited to an 8.3 format. This article explains how to retrieve the
short (MS-DOS®) filename assigned to a long filename from within a Microsoft Visual
Basic® version 4.0 application.

Short and Long Filenames


When you save a file to disk in MS-DOS® or the Microsoft® Windows® 3.x operating
system, you need to specify the filename in an 8.3 format (that is, the name of the file is
limited to an eight-character name followed by a period and a three-character filename
extension). However, the Microsoft Windows 95 operating system lets you assign a long
filename to any file or directory you create.
A long filename can be up to 255 characters in length and can contain individual words
describing the file you are creating. For example, a word-processing document might be
assigned a long filename (for example, Chapter 1: Working with Long Filenames).
In a Microsoft Visual Basic® version 4.0 application, you may need to manipulate a file
by its short (MS-DOS) filename. The example program below provides one method of
retrieving the short filename when you know the file's long filename.

Example Program
This program shows how to retrieve the short (MS-DOS) filename assigned to a long
filename.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add a Command Button control to Form1. Command1 is created by default.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


3. Add the following code to the Click event for Command1:
4. Private Sub Command1_Click()
5. Dim ShortName As String
6. Dim LongName As String
7. Dim I As Integer
8.
9. For I = 0 To File1.ListCount - 1
10. If File1.Selected(I) = True Then
11. LongName = "c:\" & File1.List(I)
12. End If
13. Next I
14.
15. ShortName = GetShortNameFromLongName(LongName)
16. Text1.Text = LongName
17. Text2.Text = ShortName
18.
19. End Sub
20. Add a File List Box control to Form1. File1 is created by default.

21. Add a Text Box control to Form1. Text1 is created by default.

22. Add a second Text Box control to Form1. Text2 is created by default.

23. Create a new function called GetShortNameFromLongName. Add the


following code to this function:
24. Function GetShortNameFromLongName(strLFN As String) As String
25. On Error GoTo Err_GetShortNameFromLongName
26.
27. Dim strWork As String
28. Dim strSFNWork As String
29. Dim strConvertedFN As String
30. Dim strPathPart As String
31. Dim strChar As String
32. Dim strPathWork As String
33. Dim I As Integer
34. Dim intFirstSlash As Integer
35. Dim fWork As Boolean
36.
37. intFirstSlash = InStr(strLFN, "\")
38.
39. If intFirstSlash = 0 Then
40. GetShortNameFromLongName = ""
41. Exit Function
42. End If
43.
44. strWork = Left(strLFN, intFirstSlash)
45.
46. I = intFirstSlash + 1
47.
48. fWork = True
49.
50. Do While fWork

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


51. If I > Len(strLFN) Then
52. fWork = False
53. Else
54. strPathPart = sfnGetToken(Mid(strLFN, I), "\")
55. strSFNWork = strWork & strPathPart
56. strConvertedFN = GetShortNameSection(strSFNWork)
57. If Len(strConvertedFN) = 0 Then
58. GetShortNameFromLongName = ""
59. Exit Function
60. Else
61. strWork = strWork & strConvertedFN & "\"
62. End If
63.
64. I = I + Len(strPathPart) + 1
65. End If
66.
67. Loop
68.
69. GetShortNameFromLongName = Left(strWork, Len(strWork) - 1)
70.
71. Exit_GetShortNameFromLongName:
72. Exit Function
73.
74. Err_GetShortNameFromLongName:
75. MsgBox "Error: " & Err & ", " & Error$, , "GetShortNameFromLongName_Err"
76. Resume Exit_GetShortNameFromLongName
77.
78. End Function
79. Create a new function called GetShortNameSection. Add the following code to
this function (note that each line of code must be typed as a single line of text):
80. Private Function GetShortNameSection(strLFN As String) As String
81. On Error GoTo Err_GetShortNameSection
82.
83. Const INVALID_HANDLE_VALUE = -1
84.
85. Dim lngRet As Long
86. Dim intNullPos As Integer
87. Dim WFD As WIN32_FIND_DATA
88.
89. lngRet = FindFirstFile(strLFN, WFD)
90. If lngRet = INVALID_HANDLE_VALUE Then
91. GetShortNameSection = ""
92. Else
93.
94. intNullPos = InStr(WFD.cAlternate, vbNullChar)
95.
96. If intNullPos <> 1 Then
97. GetShortNameSection = Left(WFD.cAlternate, intNullPos - 1)
98. Else
99.
100. GetShortNameSection = Left(WFD.cFileName, InStr(WFD.cFileName,
101. vbNullChar) - 1)
102. End If
103.
104. End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


105.
106. Exit_GetShortNameSection:
107. Exit Function
108.
109. Err_GetShortNameSection:
110. MsgBox "Error: " & Err & ", " & Error$, , "GetShortNameSection_Err"
111. Resume Exit_GetShortNameSection
112.
113. End Function
114. Create a new function called sfnGetToken. Add the following code to this
function (note that the Private line must be typed as a single line of code):
115. Private Function sfnGetToken(strTest As String, strDelimiter As String)
116. As String
117. Dim intPos As Integer
118.
119. intPos = InStr(strTest, strDelimiter)
120. If intPos = 0 Then
121. sfnGetToken = strTest
122. Else
123. sfnGetToken = Left(strTest, intPos - 1)
124. End If
125.
126. End Function
Run the example program by pressing Select a long filename from the File List Box
control. Click the Command Button control. The long filename appears in the first Text
Box control, and the short (MS-DOS) filename is displayed in the second Text Box
control.

208: Changing the Displayed Icon in the


About Dialog Box in Visual Basic 4.0
February 28, 1996

Abstract
In Microsoft® Visual Basic® version 4.0, the Microsoft Windows® application
programming interface (API) ShellAbout function lets you display the standard
Microsoft Windows About dialog box seen in applications such as Notepad and Explorer.
This article explains how to use an icon other than the default Microsoft Windows icon
that appears in the About dialog box of your Visual Basic 4.0 application.

Selecting Icons for the ShellAbout Function


Many Microsoft® Windows® applications, such as Notepad and Explorer, display a
standard About dialog box. This About dialog box displays information such as the name
of the application, a copyright notice, system information, or anything else the developer
wants to include in the window. Usually, the About dialog box is accessed from the
application's menu system. You can use the Microsoft Windows application

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


programming interface (API) ShellAbout function to display an About dialog box in
your Microsoft Visual Basic® version 4.0 application.
To use the ShellAbout function, you must include the following Declare statement in
your Visual Basic project:
Private Declare Function ShellAbout Lib "shell32.dll" Alias "ShellAboutA"
(ByVal hwnd As Long, ByVal szApp As String, ByVal szOtherStuff As String,
ByVal hIcon As Long) As Long
As you can see, the ShellAbout function requires four arguments, as follows:
hWnd A long value containing the window handle.
szApp A string containing the text that appears in the title bar of the About dialog
box.
szOtherStuff A string containing the text that appears after the version and copyright
information.
hIcon A long value containing the handle of an icon resource. When set to
NULL, the ShellAbout function displays the default Microsoft Windows
icon.

You can tell the ShellAbout function to display your About dialog box with an icon of
your choice rather than having the function use the Microsoft Windows icon. You can do
this by using the icon that is assigned to your Visual Basic 4.0 application, for example.
Then, when you call the ShellAbout function, you specify the handle of that icon
(Me.Icon) as the hIcon argument to the function.

Example Program
This program shows how to change the icon that appears in the About dialog box when
using the ShellAbout function to create the dialog box.
1. Create a new project in Visual Basic. Form1 is created by default. Set the Icon
property of Form1 to an icon of your choice.

2. Add the following Declare statement to the General Declarations section of


Form1 (note that this Declare statement must be typed as a single line of code):
3. Private Declare Function ShellAbout Lib "shell32.dll" Alias "ShellAboutA"
4. (ByVal hwnd As Long, ByVal szApp As String, ByVal szOtherStuff As String,
5. ByVal hIcon As Long) As Long
6. Add the following code to the Click event for Form1:
7. Private Sub Form_Click()
8. Call ShellAbout(Me.hwnd, App.Title, "My App's Details.", Me.Icon)
9. End Sub
Run the example program by pressing Click the form to display the standard About
dialog box. Notice that the icon appearing in the About dialog box is the icon you
assigned to Form1 at design time

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


209: Changing the Color of a Label
Control When the Mouse Pointer Is over
the Control in Visual Basic 4.0
February 28, 1996

Abstract
This article explains how to add visual appeal to your Microsoft® Visual Basic® version
4.0 application by changing the color of a Label control when the mouse is positioned
over that control.

Changing the ForeColor Property of a Label Control


When designing a Microsoft® Visual Basic® version 4.0 application, you can change the
color of a Label control when the mouse pointer is positioned over that control. Then,
when the mouse pointer is moved away from the control, the original ForeColor property
is restored. This technique lets you draw attention temporarily to a specific Label control
while your application is running.
Because a Label control does not have an hWnd property, you cannot use Microsoft
Windows® application programming interface (API) functions to determine whether the
mouse pointer is hovering over the control. You can, however, monitor the MouseMove
event of the Label control to determine when you should change the ForeColor property
of the Label control.

Example Program
This program shows how to change the color of a Label control when the mouse pointer
is positioned over the control.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1:


3. Dim MyFocusColor, MyNormalColor
4. Dim Lbl As Label
5. Add the following code to the Form_Load event for Form1:
6. Private Sub Form_Load()
7. MyNormalColor = QBColor(0)
8. MyFocusColor = QBColor(10)
9. End Sub
10. Add the following code to the MouseMove event for Form1 (note that the Private
statement must be typed as a single line of code):
11. Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single,

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


12. Y As Single)
13. For Each i In Me.Controls
14. If TypeOf i Is Label Then
15. If i.ForeColor <> MyNormalColor Then
16. i.ForeColor = MyNormalColor
17. End If
18. End If
19. Next i
20. End Sub
21. Add a Label control to Form1. Label1 is created by default.

22. Add the following code to the MouseMove event for Label1 (note that the
Private statement must be typed as a single line of code):
23. Private Sub Label1_MouseMove(Button As Integer, Shift As Integer, X As Single,
24. Y As Single)
25. Call ChangeColor(Label1)
26. End Sub
27. Add a second Label control to Form1. Label2 is created by default.

28. Add the following code to the MouseMove event for Label2 (note that the
Private statement must be typed as a single line of code):
29. Private Sub Label2_MouseMove(Button As Integer, Shift As Integer, X As Single,
30. Y As Single)
31. Call ChangeColor(Label2)
32. End Sub
33. Create a new subroutine called ChangeColor. Add the following code to this
subroutine:
34. Sub ChangeColor(Lbl As Label)
35. If Lbl.ForeColor <> MyFocusColor Then
36. Lbl.ForeColor = MyFocusColor
37. End If
38. End Sub
Run the example program by pressing F5. Each time you move the mouse pointer over
the Label control, the color of the Label control is changed

210: Determining Whether a Window or


Control Has a Scroll Bar
February 28, 1996

Abstract
This article explains how to determine whether a window or control has a scroll bar
associated with it when your Microsoft® Visual Basic® 4.0 application is running.

Using the GetWindowLong Function

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


When designing a Microsoft® Visual Basic® version 4.0 application, you may need to
determine at run time whether a specific control has a scroll bar. This can be
accomplished by using the Microsoft® Windows® application programming interface
(API) GetWindowLong function.
Every form (or window) in a Microsoft Windows-based application has a set of attributes
associated with it. These window style bits describe the window. For example, if a
window has a border, the WS_BORDER style bit is set. In the same manner, if a window
has a scroll bar, the WS_VSCROLL and/or WS_HSCROLL style bits are set.

You can call the GetWindowLong function to determine whether a control has a scroll
bar. The GetWindowLong function requires two arguments: the handle of the window
you want to retrieve information for, and the type of information you want to retrieve. In
this case, you tell the GetWindowLong function to retrieve the style of the window
(GWL_STYLE).
After the GetWindowLong function is called, you use the Visual Basic AND operator to
find out whether the style of the window is set to that of a vertical scroll bar (&H200000).
If this statement indicates that a scroll bar is present, the expression returns a True value.

Example Program
This program shows how to determine whether a control has a scroll bar.
1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following code to the General Declarations section of Form1 (note that
the Declare statement must be typed as a single line of code):
3. Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA"
4. (ByVal hwnd As Long, ByVal nIndex As Long) As Long
5. Const GWL_STYLE = (-16)
6. Add a File List Box control to Form1. File1 is created by default.

7. Add a Command Button control to Form1. Command1 is created by default.

8. Add the following code to the Click event for Command1:


9. Private Sub Command1_Click()
10. Dim lFlag As Long, iGotScroll As Long
11.
12. lFlag = GetWindowLong(File1.hwnd, GWL_STYLE)
13. If (&H200000 And lFlag) <> False Then
14. iGotScroll = True
15. MsgBox "File List Box has a scroll bar"
16. Else
17. iGotScroll = False
18. MsgBox "File List Box does NOT have a scroll bar"
19. End If
20.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


21. lFlag = GetWindowLong(Command1.hwnd, GWL_STYLE)
22. If (&H200000 And lFlag) <> False Then
23. iGotScroll = True
24. MsgBox "Command Button has a scroll bar"
25. Else
26. iGotScroll = False
27. MsgBox "Command Button does NOT have a scroll bar"
28. End If
29.
30. End Sub
Run the example program by pressing F5. Click the Command Button control. A
message box appears indicating that the File List Box control does have a scroll bar.
Click OK. A message box appears indicating that the Command Button control does not
have a scroll bar. Click OK.

211: Changing or Removing the Desktop


Wallpaper in Visual Basic 4.0
February 28, 1996

Abstract
Using the Microsoft® Windows® application programming interface (API)
SystemParametersInfo function, you can remove or change the default desktop
wallpaper from within your Microsoft Visual Basic® 4.0 application.

Using SystemParametersInfo to Remove/Select


Wallpaper
In a Microsoft® Visual Basic® application, the Microsoft Windows® application
programming interface (API) SystemParametersInfo function can be used to set many
different system-wide parameters. One of these parameters is the desktop wallpaper.
Normally, a user would select a new desktop wallpaper by using Control Panel.

As shown in the example program below, the SystemParametersInfo function can be


called with the SPI_SETDESKWALLPAPER value and the name of the wallpaper you
want to select. The Windows 95 operating system will then make this the new default
wallpaper.
The SPIF_SENDWININICHANGE value tells Windows 95 that it should notify all top-
level windows of the new change to the system-wide parameters.

Example Program
This program shows how to remove and select a new desktop wallpaper from within a
Visual Basic application.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Create a new project in Visual Basic. Form1 is created by default.

2. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single line
of code):
3. Private Declare Function SystemParametersInfo Lib "user32" Alias
4. "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long,
5. ByVal lpvParam As String, ByVal fuWinIni As Long) As Long
6. Const SPIF_UPDATEINIFILE = &H1
7. Const SPI_SETDESKWALLPAPER = 20
8. Const SPIF_SENDWININICHANGE = &H2
9. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Remove Wallpaper".

10. Add the following code to the Click event for Command1 (note that the "X ="
statement must be typed as a single line of code):
11. Private Sub Command1_Click()
12. Dim X As Long
13. X = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0&, "(None)",
14. SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)
15. MsgBox "Wallpaper was removed"
16. End Sub
17. Add a second Command Button control to Form1. Command2 is created by
default. Set its Caption property to "Change Wallpaper".

18. Add the following code to the Click event for Command2 (note that the "X ="
statement must be typed as a single line of code):
19. Private Sub Command2_Click()
20. Dim FileName As String
21. Dim X As Long
22.
23. FileName = "c:\windows\pinstripe.bmp"
24.
25. X = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0&, FileName,
26. SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)
27. MsgBox "Wallpaper was changed"
28. End Sub
Run the example program by pressing F5. To remove the wallpaper, click the "Remove
Wallpaper" command button. Windows 95 displays a black background with no graphics
whatsoever. Click the "Change Wallpaper" command button to change the wallpaper to
the PINSTRIPE.BMP image.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html

Das könnte Ihnen auch gefallen