Dec 222017
Updated (03/01/92) version of VB-TIPS.TXT, a file of tips for using Visual Basic. Although not necessary to use the file, VBTVWR is a utility (written in Visual Basic) designed to make the file easier to use.
File VBTIPS_1.ZIP from The Programmer’s Corner in
Category BASIC Language
Updated (03/01/92) version of VB-TIPS.TXT, a file of tips for using Visual Basic. Although not necessary to use the file, VBTVWR is a utility (written in Visual Basic) designed to make the file easier to use.
File Name File Size Zip Size Zip Type
TPCREAD.ME 199 165 deflated
VB-TIPS.TXT 194404 66965 deflated
VBT-READ.ME! 552 339 deflated

Download File VBTIPS_1.ZIP Here

Contents of the VB-TIPS.TXT file

VB Tips 03/01/92


Introductory Information:
Using This File
Using VB-Tips Viewer
| CompuServe Acronyms

General - Controls:
"&" in Captions
Aligning Text & Format$ Padding
Button Colors
| Buttons with Pictures
Modal Bugs
Scroll Bars - Automatic and Manual

General - Programming:
| Alias Use in Declares
API vs VB Routines
| API Errors
Blanking Out Text
Breakpoints Change Processing
| Code Should Go In Modules
Control ID's
DateValue Bug
| Emulating READ-DATA
Error Messages
Error Trapping
.EXE Size
Far Pointers
| Getting an Icon from an EXE
GotFocus Lost Due to MsgBox
Help Files
| Icon Caption Doesn't MOVE
Interrupting a Routine
| Julian Date Considerations
| LostFocus Timing
Saving Your Work
Slow Loading of App
Telephone Dialing
| "Too Many Files"

Arrays, Data:
Changing Elements of a Control Array
Control Array Bug
Dynamic Arrays
Huge Array Info
Huge Array Problem
Type Variables
Type Variable Number Placement

Calls - Controls:
Calling Other Form's Controls
Passing Form & Controls in CALLs
Passing Properties

Combo Boxes:
Changing Text
Detecting Change
Pseudo Combo Dropdown List Box
Slow Loading of Combo Boxes

Custom Controls & DLL's:
"Bad DLL Calling Convention"
Ending an App from a DLL
| Meter From Picture Boxes
METER1.VBX Display Update
DLL's Explained
DLL's Are Slow
ByVal When Passing Strings
Passing Strings
SLIDER Control Bug

DOS Functions:
File Copying
Getting File Date & Time
Getting File Info
Searching PATH$

Form & Control Placement, Sizing:
Controlling Form Size
Copying/Moving Controls
Covered Form?
Cursor in First Text Box
"Floating" Window (Forcing Window to Top)
Form Size and Granularity
Grouped Controls
Iconizing All Forms in a Project at Once
Instant Display of Controls
Mouse Pointer, Position
Move Disabling
| Moving Controls Cleanly
Moving Form Without Title Bar
Multiple Instances of a Form Not Supported
Norton's Desktop, Get in Front of
Control's Coords. Are Relative to Form
Overlapping Controls
Placing Forms, Controls
Resizing Forms
| Scrollable Forms
Unloading Control Arrays

File I/O, Database:
Deleting Data in Sequential File
MKI$ & CVI in VB

Fixed Width Fonts
Large Font Bug
Listing Printer Fonts
Using Different Fonts, Colors in a Box
System Font

Changing Form Name
| Detecting a Form's Getting Focus
Flashing a Form
Focus & Order of Execution
Focus on StartUp
Modal Forms, Unloading
SetFocus Won't Work?
Unloading Forms Completely
Unloading Forms From Task Manager
Unloading Forms on Windows Termination

Global Const CRLF = chr$(13) + chr$(10) Not Allowed
WINAPI - "Out of Memory"

Grid Control:
What is the Grid Control?
| Can't Enter "&"
Faster Row Adding/Deleting

Changing Icons
Icon - Get Rid Of
Transparent Background

Keyboard & Mouse:
Calling the Same Routines
INKEY$ Loop Emulation
Keyboard/Mouse Shortcuts
Mouse Pointer Style Setting
Trapping Double-Click before Click
Using "Enter" in Place of "Tab"

Label Boxes:
| An Alternative to Label Boxes
Label Box Text Background Color

List Boxes:
"AddItem #" Error
Clear a List Box
Finding an Item Added to a Sorted List Box
Finding an Item Manually
Finding an Item via API
Font Size Changes List Box Size
Forcing an Item Number to the Top
Inhibiting Screen Updating
Linking Sorted and Unsorted List Boxes (or Arrays)
"ListIndex = -1" Generates Click Event
Selecting Multiple Items
Tab Stops

Menu Bar:
AppName.Caption Problem
| Calling Help for Menu Items
| ClipBoard Status
| Disabling Menu Items
Focus Doesn't Change
Right Justify "Help"
Screen Flicker on Selection

Picture Boxes:
As Buttons
BitBlt to/from Hidden Box
"Box" Command
To Clipboard as Metafile?
Copying a Drawing to Clipboard
Copying Text in a Picture
Cutting-Pasting to a Picture Box
Dragging Text Box in Picture
Drawing - Scale
Faster Drawing
Number of Colors Supported
Saving Picture Files
ScaleHeight Negative

Aligning Text of Different Sizes
Calculating Page Length
Changing Paper Bins
| EndDoc vs NewPage
| Font Not Available on Printer
Formfeed Problems
Getting Available Font Sizes
Getting List of Installed Printers
Landscape Setting on HPLJ
Laser Printer Margins
LaserJet Driver Update
LaserJet Lost Line Fixed
LaserJet Offset
Matching Printer & Screen Fonts
Not Printing?
Number of Copies, Setting
Printer Setup
Printer Control Codes
Printer Wordwrap
Printing Forms
Printing Problems
ScaleLeft Errors
TextWidth Improvement

Screen (Also see Fonts, above):
Background for VB
Color Const Declarations
| Displaying "ASCII Graphics"
Framing Controls (the NEXT Look)
| Screen/Window Capture
Screen Scaling/Form Resizing
| Screen Scaling Tips
Screen Type
Screen Wipes
Stop Flashing During Updates
Suppress Arrow Cursor
Twips to Pixels

Adding to Control Box Menu
Control Box Exit Bug
Control Accessing Via DDE
Detecting Running Applications
| Detecting the Start-Up Directory
| Faster Loading of Apps
| File Creation Delay
File Handles - Maximum
Finding Excel
Linking to Excel
Get Program's Path & EXE Name
Getting Windows Version Number
Get System Focus
hWnd for a Control
hWnd for a Control II
.INI Files
Mode - Which You Are In
Multi-Instance App Prevention
Networks & VB
Norton Desktop vs VB
Resource Limits
| Restart Windows
Shell-to-DOS Problems
Shell-to-DOS Syntax
Shell Error Numbers
VB App Cannot Be Windows Shell
VBRUN100 Must Be on A:?
Wallpaper Changing
Why VB Apps Don't Cascade/Tile
WIN.INI Warning

Text Boxes:
CtlName Access Via CDK
Cursor (text), Position
Data Entry Masking
Data Entry Routine
Data Entry Text Limit
| Fast Loading
Flashing/Highlighting Text
| Increasing Line Limit
Line Number, Finding
Number of Lines
| Read-Only Text Box

DoEvents () in a Timer
Using the Timer

Knowledge Base:
What is Knowledge Base?

Visual Basic Books & Articles:
VB Book Reviews
VB Magazine Articles

VB Downloadable Files:
How to Get the Files
DLL's, Utilities
Database, Data Entry, Sorting
DOS Access/Functions
Font Utilities
Time/Date Applications
Custom Controls
General Tools/Reference/Info
PsL's March 1991 Visual Basic Disk



01/01/92 Update

In this update, a listing has been added of VB-related files (routines and
tools) which are available from MSBASIC on CIS or from PsL on disk. This is
very valuable information, yet it *does* take a lot of space in this file. I'm
open to readers' opinions as to whether this information is worth updating and
including each month.

This file may be freely shared with others so long as NO CHANGES ARE MADE to it.
Changes made to this file will likely result in the viewing program not working.

Official updates to VB-Tips are available from DL1 of the MSBASIC forum on
CompuServe and from the address below.

Compiled and Edited by
Nelson Ford, 71355,470
Public (software) Library
P.O.Box 35705
Houston, TX 77235-5705
800-242-4PsL 713-524-6394
(NOTE: The above numbers are NOT bbs's.)
Call or write or email for a free copy of
our monthly pd/shareware magazine/catalog.
We have many disks of routines for VB.

New entries in this update are marked with a "|" in the margin in the CONTENTS
section. Updated entries are marked with a "}" in the margin.

Updates have been posted every two to four weeks in the past. The frequency of
future updates will depend on the amount of new material added.

Names in brackets under CONTENTS are files in the MSBASIC DL's 1 or 5 (and also
available from PsL, above). There is no other information about them in this
file other than in the CONTENTS section.

This file would not have been possible without the help of those willing to
share their knowledge with others.

Main Contributors:

Jonathan Zuck
Keith Funk
Mark Novisoff
Ted Young
Dennis Harrington
the Microsoft Section Leaders
and other MSLANG members.

Using This File:

You can view this file with any file viewing utility such as List; however,
there is a program named VBT-VW on the MSBASIC forum that can make it easier
to work with this file. See VB-Tips Viewer, below.

If you are using a file viewer like List, first look up information about a
topic that you see in the CONTENTS section, you can just search for it.

As you can see below, the Category headings (eg: "General") in the CONTENTS
section are NOT repeated in the body of the file. You must search on the
actual Tip titles.

Each Tip is separated from the others by a dashed line.

Using VB-Tips Viewer:

The VB-Tips Viewer program was written for viewing this file more easily.
See the comments in the Global file of the Viewer code for more information.

To save download time, only the source code for VB-Tips was uploaded. You can
easily compile it yourself.

WARNING: If you make changes to the CONTENTS section or any of the Tips
captions, the Viewer program may not be able to access this file. Keep a

To use the Viewer, select a Category and its topics will be displayed in the
adjoining window. Select a Topic (Tip) and it will be displayed in the Tips

If you press the Clipboard button, whichever Tip the cursor is in will be
copied to the Windows Clipboard. This is the easiest way to copy a routine to
your own program.


While I have done my best to test everything that appears in this file, a couple
of typos and other glitches have appeared in previous versions of this file.
Anything that is not marked as being new (with a vertical bar in the left
margin) should be pretty stable. If you have trouble with any of the new stuff
or if you find any errors in this file or if you would like to add info to this
file, please send a message to 71355,470.

The contents of this file are not warranted in any way. I do not assume any
responsibility for the use of this materials. I am not affiliated in any way
with Microsoft Corporation.

CompuServe Acronyms:

Newcomers on the MSBASIC forum and on CompuServe may be puzzled by the
acronyms seen in some of the messages. Here are a few of the more popular
ones, to help you feel more at home:

BTW = By the way
FWI = For what it's worth
IMO = In my opinion
IMHO = In my *humble* opinion
= "Grin"
GD&R = Grinning, ducking and running (usually appended to a teasing remark)
😉 = a smiley face winking (tilt your head to the left and look at it)
PITA = Pain in the a(natomy)
PMFBI = Pardon me for butting in
PMJI = Pardon my jumping in
ROFL = Rolling on the floor, laughing
There are many more. You can usually figure them out - or just ask.


"&" in Captions:

When you put "&" in a Caption, it is a sign for VB to use the next letter as a
shortcut key. If you really want to display "&" as part of the caption, use
a pair of ampersands:

For example, Label1.Caption = "&&HFA3" will display the caption "&HFA3".

Aligning Text & Format$ Padding:

Only one VB control offers the option of text (Left, Center, Right): a Label.
Third-party controls often add this option to other controls.

You can manually align text using spaces and Format$, but with great
limitations. First problem: most fonts are not mono-spaced and thus will not
line up in columns nicely.

Second problem: Format$ does NOT pad with spaces the way PRINT USING did.
For example: List1.Additem "X" + Format$(1.2, "#####.##") results in: "X1.2"
To align numbers, use a mono-spaced font and -
Right$( Space$(nn) + Format$(___, "__________"), nn)
Right$( Space$(10) + Format$(1.2, "#######.00"), 10)
results in " 1.20"

A method of aligning proportional text is with TextWidth:

NumFmt$ = "##,###"
Numb$ = Format$ (Amount, NumFmt$)
CurrentX = CurrentX + TextWidth (NumFmt$) - TextWidth (Numb$)
Print Numb$

Button Colors:

The BackColor property has (virtually) no effect on the color of the button.
Button colors are controlled by WIN.INI. See the 6/11/91 issue of PC Mag
for details. Of course, changes you make there apply globally.

An alternative is to simulate a button with a Picture box and a bitmap.

Buttons with Pictures:

Making a picture control behave like a pushbutton is relatively easy
(J. D. Evans, Jr.):

1. Draw a bitmap.
2. Use the bitmap in a picture control with its DrawMode set to Invert.
3. On GetFocus and LostFocus, draw a 1 or 2 width line around the picture
control to highlight it (use 0,0 and ScaleWidth-ScaleHeight, black,
and B options) the way a button normally looks when it gets focus.
4. Have the click event call KeyDown and pass it a KEY_ENTER and then
make KeyDown do what you would normally do in a pushbutton click
for both KEY ENTER and KEY SPACE (to emulate keyboard).
5. On MouseDown and MouseUp do the same thing as you did in GotFocus
and LostFocus, except specify NO COLOR parameter and use BF option.
the INVERT DrawMode (or style or whatever it is) will take care
of making the picture look like a button going up and down.
Note: if you do not actually do anything in the KeyDown (e.g., call
a function or display something), then the picture may get
confused as to whether it should be up or down, but then why
have a button that actually does nothing other than go up and
down! It needs to do something to give everything a chance to
get thru the message queue in correct sequence.

This technique is courtesy of Karl Peterson (who told it to me) and results in a
very efficient graphic pushbutton that paints QUICKLY!

P.S. Use Windows Paint to make the BMP file. Alt-PrintScrn will capture the
current window to the ClipBoard and you can paste it into Paint and make a
BMP that looks just like a pushbutton or anything else!

P.P.S. When I say 'draw a line' (in 2.) I mean use the DrawLine method/function.
It will make sense when you look it up. It looks complicated but is about
15 lines of code. Actually drawing the bitmap in Paint is the thing that
takes the most time, but you can make some really neat looking buttons,
and all with vanilla VB controls.

Modal Bugs:

1. If you call the Printform method from within a modal window, the window then
loses its modality. (Jack Freudenheim)

MS has acknowledged this as a bug and offers this work-around (Don Funk):

'form that is displayed modally
Sub Command1_Click()
Const GWL_STYLE = -16
Const WS_DISABLED = &H8000000
MousePointer = 11
OldStyle& = GetWindowLong(Form1.Hwnd, GWL_STYLE)
X& = SetWindowLong(Form1.Hwnd, GWL_STYLE, OldStyle& Or WS_DISABLED)
MousePointer = 0
End Sub

2. A call to InputBox$ will also destroy a form's modality. (Bob Kubelka)

Scroll Bars - Automatic and Manual:

Vertical scroll bars appear automatically in List Boxes and Combo Boxes when
the amount of data exceeds the space available.

For an illustration about how to use the Vertical Scroll Bar control to page
through data in a file or an array and display a little at a time in a box,
see RANDGR.EXE and HUGEGR.EXE on DL1 or DL6 of the forum.

Alias Use in Declares:

With the Alias keyword added to Declares, duplicate definitions become apparent
immediately when the program is run. Otherwise, there is no indication that
DLL's have been declared in multiple places. (Steve Schmidt)

API vs VB Routines:

Given the choice, it's preferable to use VB routines rather than API calls:
1. API calls will probably be slower (goes through a p-code declaration).
2. API calls require hWnds for everything.
3. API calls will not be portable accross different OS.
- J. Zuck.

API Errors:

Errors in WINAPI.TXT can be found in article Q74526 in the Knowledge Base (GO
MSKB on CompuServe).

Blanking Out Text:

You cannot blank out text on the form or picture box by writing blank spaces
over it as you can in other BASICs. Instead, set the FontTransparent to FALSE
and you'll be able to overwrite the text that's already there with spaces. Note
that spaces aren't as wide as most characters (in proportional fonts), so you'll
have to use more spaces than you might use under a DOS Basic. (Ted Young)

Breakpoints Change Processing:

Putting a breakpoint ("Stop") in your code can change the way your application
executes. For example, when you press a key while your app is running, the
processing normally goes to KeyDown and then to KeyPress.

If you put a Stop in KeyDown or if you single-step through KeyDown, processing
will not continue on to KeyPress. The same type of thing happens in other
situations where a breakpoint has been added.

Currency Format vs Date Format:

From Sharon Dooley:

Once you format a currency variable, the DATE functions no longer work
correctly. For example:
Print Format$([email protected],"0.00")
Print Year(Now)
will display

Microsoft has confirmed this bug. The only way around it is to format some
non-currency before using the date format:
Print Format$([email protected],"0.00")
Print Year(Now)
Print Format$(0%, "###0")
Print Year(Now)
which prints four lines containing 0, 1900, 0 and 1991, respectively.

Code Should Go In Modules:

In general, code in modules is less of drag on performance than code in forms;
therefore, you should consider putting as much code as possible into modules.
(Robert Eineigl, Microsoft Product Support)

Control ID's:

CTLHWND.DLL lets you get a control's handle without having to use the clumsy
SetFocus method. But its use in your app requires you to distribute yet another
DLL, which is not desirable either.

If you wanted to do without CTLHWND.DLL but still benefit from it, you can use
it to get your control's IDs during development. Then right before making the
EXE, save the control IDs in global constants and remove CTLHWND.DLL from the
project (the IDs won't change):

DefInt A-Z
Declare Function GetDlgCtrlID Lib "User" (ByVal hWnd)
Declare Function ControlhWnd Lib "CTLHWND.DLL" (C As Control)

Sub Form_Load
ID_COMMAND1 = GetDlgCtrlID(ControlhWnd(Command1))
ID_COMMAND2 = GetDlgCtrlID(ControlhWnd(Command2))
End Sub

Right before making an EXE just replace the two Dim lines with Constants. Once
you have the control IDs you only need to call GetDlgItem(Form.hWnd, ID) to get
the hWnd. (Costas Kitsos)

DateValue Bug:

The following code is supposed to create a serial number from a given date.
DV# = DateValue("Oct 14")

The VB Reference manual says that if the year is omitted, the current year is
used, but if you translate the number back to text with
Print Format$(DV#, "mm/dd/yyyy")
you get:

If you use "10-14" instead of "Oct 14", it works. --Isaac Rabinovitch

Emulating READ-DATA:

VB does not have READ and DATA. You can emulate this function by assigning data
to a string and reading it from there:

MyData$ = "Item1, Item2, ..., ItemN,"
ReDim Array$(N)
i = 1
j = Instr(i, MyData$, ",")
Array$(x) = Mid$(MyData$, i, j-i)
i = x + 2
Loop while i < Len(MyData$)

Error Messages:

When you get an error message while programming or running inside VB, just press
F1 before pressing the Ok button and an explanation of the error message will be

Error Trapping:

The error trapping in Visual Basic is local to the Sub or Function and you
will have to write a general error handler and place a call in every level
to this error handler.

.EXE Size:

1. Long variable names increase EXE size.
2. Comments add a couple of bytes per each comment line to EXE size.
3. A Global DIM of an array adds to EXE size.
For example:
Global Sample as String*20000
will add about 20k to the size of the EXE file, but the same DIM
in a Form will not.
4. VB "accumulates" variable names and other junk that it does not let go
of when you delete and save. This can make your EXE file get larger with
each compilation. Here is how to solve the problem (repeat for each form):
a. Select "Code" on the VB menu.
b. Select "Save Text" and save.
c. Select "Load Text" and click the Replace button, then load what you
just saved.
To get the maximum size reduction, exit and restart VB before step c.

Far Pointers:

Here's a question regarding direct calls to the Windows API from a Visual Basic
procedure. In trying to set up a call to the API entry "Polygon", I discovered
that one of the arguments is a far pointer to an array of structures. I've
searched the VB documentation, but can't find a method that will return the
run-time address of a variable (or an array element) as a far pointer.

Is there a VB technique for passing a far pointer as an argument -- if so, how?
Also, how would such an argument be specified in the corresponding DECLARE
statement? Many thanks to anyone who can supply this information.

(Fm: Mark Novisoff (TA) 73047,3706)

If the structures themselves don't require pointers (which is the case with
Polygon), then it's a piece of cake. Use TYPE...END TYPE to declare a model
structure, and then DIM an array of the structures.

In your Global module Declare statement, use the "As" syntax:

Type Points
' Define the structure
X As Integer
Y As Integer
End Type
Declare Function Polygon Lib "Gdi" (hDC%, MyStruc As Points, nCount%)

In your code:

ReDim MyStrucArray(0 To 10) As Points
' Set the variables in the array here
Result = Polygon(hDC%, MyStrucArray(0), nCount%)

Note that this results in passing a far pointer to the zeroth element of the
array. Because the length of the structure is known to the DLL routine, it
will figure out where the rest of the elements are.

Getting an Icon from an EXE:

To extract an icon from an EXE file
1. Get the handle to the EXE using FindWindow (assuming its up and running)
2. Plug the handle into GetWindowWord with the constant GWW_HINSTANCE to get
an hInstance.
3. Feed the hInstance into LoadIcon to get an HICON (handle).
4. Now assuming you have a DC from a picture control or form already, then
use that in DrawIcon.

Dim hInstance, handle, HICON As Integer
handle = FindWindow("TheAPP'sClass", "TheApp'sWindowname")
hInstance = GetWindowWord(handle, GWW_HINSTANCE)
HICON = LoadIcon(hInstance, n&)
n& = n& + 1
Loop Until HICON <> 0
Print "HICON= "; HICON
If DrawIcon(picture1.hDC, 10, 10, HICON) Then Print "OK"
Also note that DrawIcon is also in User not GDI.
(Robert Eineigl, MS)

GotFocus Lost Due to MsgBox:

Putting a MsgBox in a Control's LostFocus keeps the GotFocus in the next
Control from being executed:

Sub Text1_LostFocus ()
MsgBox = "Losing Focus"

Sub Text2_GotFocus ()
Text2.Text = "Got Focus"

This appears simply to be a bug in VB.

Help Files:

1. You can create Help files more easily and cheaply than with the SDK: use
the help compiler (HC.EXE) that comes with Turbo Pascal for Windows and
Borland C++.

2. A shareware program named Xantippe is a good front-end for making help

3. Iconwrks, which comes with VB, contains a Sub called Get_Help. This shows
how to call the Win API function, WinHelp, to invoke Windows' Help system.

Icon Caption Doesn't MOVE:

If you use the Move method on your form while it's minimized, the icon will
move as you would expect it to, but the caption doesn't move with it!!!
I used Spy to determine that the caption is a separate Window (with a style
name of "#32767"). (Mark Novisoff)

As long as you don't have the .Move code in the _Resize event, you can force
the caption to catch up by simply setting WindowState = 1. That will force a
kind of Refresh, bringing the caption into place. (Jonathan Zuck)

Interrupting a Routine:

I have code attached to a control button called "Start". When you click on it
it increments a counter to the screen with a FOR loop. I want this to continue
until I click on a control button called "Stop". VB seems to keep control
until the loop ends and it does not see the click on the "Stop" button. How
can I get around this?

Use a DoEvents (see your manuals for details):

While DoEvents and CounterOn
Counter = Counter + 1

Where CounterOn is a global variable you set to FALSE from your Stop button.

Julian Date Considerations:

RE: Finding out what day of the week July 4th, 1776 was.

You need to be a little careful about dates when going back that far. Although
the Gregorian Calendar was invented in 1583, it was not universally and
immediately accepted. The British didn't adopt it until the second half of the
18th century and the U.S. after that (and not all states at the same time).

In addition, the new year was moved from Mar 25 to Jan 1. Geo Washingtons
birthday was Feb 11, 1731 O.S. (old style), but was changed to Feb 22, 1732
N.S. (new style). China didn't change until 1912 and Turkey until 1917.

The original accumulated error in 1583 was estimated to be 10 days, but this
expanded to 11 and then 12 days the longer the country waited to adopt the
standard. To change from old style julian dates to modern gregorian dates, add
10 days to dates oct 5, 1582 through feb 28, 1700; 11 days to dates through
feb28, 1800; 12 days through feb28, 1900; and 13 days through feb28, 2100. If
you want a program to back it out the other way, you'll need to subtract these
figures to arrive at the true O.S. date value. (Bruce Fulton)

LostFocus Timing:

If one control has focus and you execute another control by clicking on it,
control-1's LostFocus gets executed before control-2's Click event, but if you
use an access key to execute the second control, then control-1's LostFocus gets
executed AFTER control-2's Click event.


From Mark Novisoff, Microhelp: We found a *terrible* problem with End just
recently (concerns custom controls).

Unloading all forms in your apps has the same effect as End and custom controls
get the required messages correctly.

Jonathan Zuck adds: I found this problem also when working on a DLL for my
column. The problem is that End does a FreeLibrary *before* it destroys the
application windows (including custom controls). This is particularly a problem
when subclassing. What you need to do is in your WEP, clean up *everything*
rather than relying on getting the WM_DESTROY message, or whatever, to the
particular window. I think you will find similar problems if the application is
ended by choosing "End" from the Run menu of the development environment.

Saving Your Work:

Several users have reported losing their work in different ways. If you always
save before test-running your code, you can prevent most losses. If you get
any kind of warnings or other indications that something might be wrong with
your system, try to get into DOS (or use File Manager) and copy your project
files to a backup directory. Many people have had their project files
"trashed" when the system went screwy.

There is a utility on the DLs here for copying all the files in a Project -
very handy to have, although a better idea, generally, is to keep each project
in its own subdirectory, then you can just copy the entire subdirectory to
another one.

Slow Loading of App:

If your app takes a long time to load, try the following procedure to keep your
users from getting antsy:

Create a "Banner" form with only a small label which says "Loading Program" and
a Timer Control (Enabled). Make this form Borderless, no Max, Min, or Control
Box. Put a Screen.MousePointer = 11 as the only command in the Load Event.
Select this as the start-up form. This form will load fast.

Under the Timer event, disable the timer, Load MainForm (with Visible = False),
and MainForm.Visible = True. The Load Event of MainForm should Unload Banner.

The result is that you get a fast mousepointer 11 and a "please wait" message
which lasts until the main screen gets loaded. (Al E Meadows)

Telephone Dialing:

OPEN "COM1" For Output As #1
Print #1, "ATD 000-0000"

Here is a more complete routine from Mark Novisoff.

' General section
Dim PortIsOpen As Integer

Sub Form_Click () ' Tells me to dial the phone
'get PhoneNumber$ from the form, however you have the user input it.
If Not PortIsOpen Then
PortIsOpen = -1
Open "COM1" For Output As #1
End If
Print #1, "ATDT" + PhoneNumber$ + Chr$(13)
End Sub

Sub Form_DblClick () ' Tells me to hang up
If PortIsOpen Then
Print #1, "ATH" + Chr$(13)
Close #1
PortIsOpen = 0
End If
End Sub

"Too Many Files":

If you get the message "Too many files", try the following:

Declare Function SetHandleCount Lib "Kernel" (ByVal wnumber)

j = SetHandleCount(32)

The default number of file handles is 20 (actually 15 because the system uses
5). Entering 32 will bump your total number of handles up quite a bit.
(Todd W. Bristol)


The following is a quirk of all versions of MS Basic, not just VB:

Any non-zero number will return a "true" result with the syntax: "If x..."
but only "-1" will work with the syntax: "If NOT x Then ... Else...".


x = 1
if x then print "x is True"
if not x then print "x is False" else print "x is True"
x = -1
if x then print "x is True"
if not x then print "x is False" else print "x is True"

Run this and you get:

x is True
x is False
x is True
x is True

Here's a typical example of how you can get caught by this:

If Not InStr("13579", n$)

When i is 5, InStr("5", Str$(i)) will have a value of 3, which as we have just
seen, will not trigger a "Not" test correctly. Instead, you must say

If InStr("13579", n$) > 0

Changing Elements of a Control Array:

Q: Is there a way to simultaneously change a property in a control array,
affecting all the elements simultaneously? For example, if I had an array that
had several text labels and I wanted to change all their forecolors to blue.

A: No. You have to use a For-Next loop.

Control Array Bug:

If you create a control and add code to one of its events and then decide to
make it into a control array, VB does not add the "Index As Integer" argument
to the control's argument list. When you attempt to run the program, VB gives
the error, "Incorrect number of event procedure arguments". (Keith Funk)

Dynamic Arrays:

In order to use a dynamic array, you must REDIM it in a module or form:

Type SomeType
X As Integer
End Type
Global ArrayName() As SomeType

Module or Form:
Redim ArrayName(x) As SomeType ' x can be a number or a variable

Huge Array Info:

Most VB controls (eg: List & Text boxes) and arrays limit you to 64k of data.
Microsoft gave beta testers a Huge Array DLL, but did not include it in the
finished VB package.

The DLL seems to work all right most of the time (see below), but it is
difficult to access and slow to load. See HUGEGR.EXE and RANDGR.EXE comparing
the use of a Huge Array to a Random Access data file. Performance is about the
same, but using a Random Access file means the program starts faster and data
is more secure.

Huge Array Problem:

(1) If you DIM a Huge Array (a public domain DLL) to a small number of elements
and later REDIM it to a large number, you will get an Out Of Memory error from
the DLL, even when there is sufficient memory. You can REDIM to a smaller
number of elements with no error.

Using a 16-byte TYPE variable, I DIMmed the array to 10000 elements and then
ReDIMmed it to 23864 elements. This worked fine. However, I could not ReDIM
this same array to more than 23864 elements without getting a (-1) Out of
Memory error from HUGEARR.DLL, even though if I initially DIM the array to

30000 elements it works fine. - Randy Berry

(2) You cannot redim a Huge Array past a 64k boundary. Example, if you Dim an
array to 100K in size, you can redim it to 90k or 110k, but not to 50k nor 150k.
(Steve Schmidt)

Type Variables:

Unlike QuickBASIC, you cannot use arrays within a TYPE...END TYPE declaration,
such as:
Type SomeType
x As Double
y(100) As SomeOtherType
End Type

You can work around it by putting a variable length STRING in your TYPE and
then storing the information in the string. Not the easiest way to do things,
but in some ways it's more flexible since you don't have to know before hand
how large it should be.

Type Variable Number Placement:

Be sure when using arrays with Type variables to put the variable number
AFTER the type name and BEFORE the element name.

TypeName(i).string1 = "xyz" NOT TypeName.string1(i) = "xyz"

Calling Other Form's Controls:

You can invoke a control from another form via the Value property. For
example, in Form2, you can say "Form1.Command1.Value = -1" and this will
invoke Command1_Click on Form1.

Normally, if you have a "(general)" routine (ie: not attached to a control)
that you want to access from more than one Form, you should put the code in a
Module (see the VB manuals). However, it *is* possible to put the code in one
Form and call it from others by the method in the previous paragraph. Instead
of putting the routine in "(general)", assign it to a button and set the
button's Visible property to False. Now you can call it as above.

Passing Form & Controls in CALLs:

Often overlooked in the manual is how to pass a control in a CALL. The syntax is

SUB SomeSub (Lst as Control)
Lst.AddItem "abc"

In the calling routine, you would say Call SomeSub(List1), for example.

You cannot call a Sub that is in another Form. It must be in a module, if not
in the calling Form. There is a kludge for getting around this (it involves
the principle described in Calling Other Form's Controls, above), but why
bother kludging when you can just move the routine into a Module?

To pass text from a control, you must use ByVal.
Example: Sub SomeSub (s as string)
Call SomeSub(Text1.Text)

Passing Properties:

If you have a Sub defined like "Sub Something(a%)", you can pass a control's
property to it by putting it inside parentheses (in addition to any required

X = Something((CtlName.ForeColor))

If the Sub is defined using "ByVal", such as "Sub Something (ByVal a%)", then
the extra parentheses are not required:

X = Something(CtlName.ForeColor)

If you need to change the value of a Property in a Sub, you must use the
following syntax:

Sub ToggleBoolean (Check As Control)
Check.Enabled = Not Check.Enabled
End Sub

Sub Command1_Click ()
ToggleBoolean Check1
End Sub

Changing Text:

Text is read-only, but you can change it by using "Combo1.ListIndex = 3".
This would select and display the 4th item in the list.


BackColor does not apply to the list portion of the combo box, only the 'edit'

Detecting Change:

In a Combo Box, you only get a CHANGE event if the user actually types inside
the edit control portion of the control, not if they select an item from the

To detect that the user has selected an item from the list, you must use
either the Click or DblClick events. Using the arrow keys has the same effect
as .Click. Reportedly there is no keyboard equivalent of DblClick, although
you can simulate it by setting the default to your "OK" button.

To determine if a Click actually changed what was in the edit box, you will
have to set up a variable with the initial data and compare it after the

Pseudo Combo Dropdown List Box:

To implement a pseudo Combo DropDown List Box like the one used in the VB
Help/Index/Search Window: As you type text in a Text Box, VB hilights the
first entry in the List Box that matches what you have typed. This can be
implemented in VB by using a Text Box, a List Box and the API message

Slow Loading of Combo Boxes:

Combo Boxes are much slower to load data into than List Boxes. Some people
prefer to make their own "Combo Boxes" by combining a Text Box and a List Box.

The API CB_ADDSTRING loads a Combo Box 5-10 times FASTER than using the VB
AddItem method. - Bill Reilly

"Bad DLL Calling Convention":

If you get the above error message with a Declare like the following:

Declare Function bitblt Lib "Gdi" (ByVal destHdc, ByVal X, ByVal Y,
ByVal w, ByVal h, ByVal srcHdc, ByVal srcX,
ByVal srcY, ByVal Rop As Long)

it is because you need "%" of "as integer" after some of the arguments.
Alternatively, you can use DefInt A-Z, but when using Declares, be sure to pay
attention to the type of each variable.

Ending an App from a DLL:

If you have a system menu with the CLOSE option, then you could make the
following call from within your dll:

X& = SendMessage(Handle, WM_SYSCOMMAND, SC_CLOSE, NULL)

Const NULL = 0&
Const SC_CLOSE = &HF060

Don Funk

Meter From Picture Boxes:

To make a Meter control from Picture boxes, follow these steps:

1. Draw a Picture box and adjust it to the size you want.
2. With the Picture box selected, press Ctrl+Ins to copy it to Clipboard.
3. With the Picture box still selected, press Shift+Ins to create a second
box within the first one.
4. When asked if this is an array, say No, and it will be named Picture2.
5. Set Picture2's BorderStyle to 0 in the attributes menu.
6. Set Picture2's BackColor to a contrasting color.
7. Declare StepValue and MaxValue as global variables in Global.bas.
8. Enter the following code in your app:
Sub Form_Load ()
Picture2.Width = 1
MaxValue = (some number)
StepValue = Picture1.Width / MaxValue
End Sub
To increase/decrease the gauge:
Picture2.Width = Picture2.Width +/- StepValue
(By Jussi Mattila)


METER1.VBX is a graphic meter control on DL1 or DL6. It allows specification of
max items and number of items complete, and it computes percent complete and
displays graphical Percent Complete bar.

If the number of max items are greater than 100, than you get inconsistent and
crazy results (sometimes). I worked around by always using 100 as max items, and
did the scaling myself.

METER1.VBX Display Update:

To get the meter display to reflect changes, add the following in your loop:


Here's my DLL written in TPW. I had no documentation besides the Windows
Programmer's Reference so maybe someone here can tell me if I'm on the right
track. Some questions come to mind such as: how large should I make the voice
queue? Is it necessary to open and close the sound device every time I want to
set a note?

library SoundDLL;

uses WinTypes, WinProcs;

procedure PlayNote(Note, nLength, Cdots: Integer);export; begin
StopSound; end;

exports PlayNote index 1;

begin end.

The declaration in VB (general):

Declare Sub PlayNote Lib "e:\tpw\output\soundll.dll" (ByVal note%, ByVal
Length%, ByVal Cdots%)

(Mark N.):
The size of the voice queue is one of those numbers that you simply "Pull out
of thin air". It depends on what you're going to do. For example, in VBTools,
we set the queue to 8K because we include several large pieces of music.

OTOH, if you're going to play single notes on an occasional basis, then 1K
should be plenty.

It is not necessary to open and close the sound device every time. In fact,
if you close it while there are notes in the queue, they'll be lost!
I suggest that you do what we've done in VBTools:

1. Open the sound device when the user first calls your proc.
2. If the device is open, then close it when your DLL unloads.
3. Give the user a method to close it so that sound can be generated by
other apps.

DLL's Explained:

A DLL is a dynamic link library which can be loaded and used by numerous
programs at the same time. This saves memory since if it is done the "old"
way by linking in the libraries at compile/link time, every program that
uses the code would have to have the same code inside the "EXE". An example
is the PRINT statement in QBX. Every program that you have written (except
the BRUN code) has the same routine that performs the Print routine. In
Windows, you can remove this code from your EXE and put it into a DLL. That
way when you have several VB programs that use the Print routine, there is
only one copy of this routine in memeory.
There are several DLL's used by Windows. USER, GENERAL, KERNEL, and GDI
are the 4 main DLL's used by Windows. They have in "their" code functions
used by all programs in the Windows environment. Things such as positioning
the window, painting the background, and moving the cursor are a few
examples of functions used by all the programs in the environment.
There is an excellent book called MS Windows Programmer's Reference.
This manual lists all(well 99% of them) functions that can be called from
VB. Not all functions can be called from VB, and not all of them would you
want to call, but it does list all of them, so you can take your pick. It's
a pretty hefty manual but quite easy to read. You can get it from MS by
calling 1-800-426-9400 and asking for kit 1-55615-413-5, called "MS Windows
Programmer's Reference Manual and Online Resource". It's is 49.95 but well
worth it. It's about 800-1000 pages.

DLL's Are Slow:

If you call a C routine in a DLL that assigns strings it will be slower than a
direct assignment in Visual Basic. Even if the assignment routine is just as
efficient as VB's, you are adding the overhead of extra calls to get the same
job done. If you wrote the DLL code in pure .asm instead of calling another
routine it would probably be closer to VB's time.

ByVal When Passing Strings:

I have a question regarding the use of 'C' style DLL return structures.
So far I've figured out that strings can be used to return values by declaring
them ByVal if the item returned is an array (i.e. char bozo[20]). What I can't
figure out is how to get a string which is returned as char far *.

What does ByVal do when passing strings? My understanding is that this means
that the contents of the var. should not be modifiable, but this does not seem
to be the case when passed to a DLL.

When passing non-strings to DLLs, does omitting the ByVal mean that it can be
updated by the call?? For example, should parameter declared short far * be
passed with or without ByVal?

Fm: Jonathan Zuck (UFI) 76702,1605

Passing a string ByVal tells VB to append a NULL to the end of it and pass an
LPSTR to the string. It *is* therefore able to be modified by the DLL as long
as the DLL is capable of looking for the terminating character. These changes
will get reflected in the VB string.

This leads me to you next question. The way to deal with functions that return
a pointer to a string is to use the LSTRCPY API function to which you would
pass your VB string (padded with enough spaces) ByVal and the LPSTR you got
back. There is a function in QuickPak/VB that will simply convert an LPSTR to
a VB string if you have that package.

Yes, if you want the DLL to be able to change a numeric parameter, you should
*not* pass it ByVal. Also make certain that the DLL is expecting a pointer and
not the value. However, if it plans to change the variable, it would be
expecting this anyway.

TPW Framework for DLL's:

From Rick Sands:
Here is the basic framework for a Turbo Pascal DLL:

Uses WinTypes, WinProcs;
Function FileExist(Filename:pChar):Integer; EXPORT; { <<-- NOTE }
var Fp:File;
assign(Fp, Filename);
{$I-} reset(Fp,1); {$I+} { Open file as Binary }
if IOResult = 0 then { Did it open? }
FileExist := -1; { VB True }
Close(Fp) { Don't forget to close }
FileExist := 0 { VB False }
EXPORT FileExist Index 1; { <<-- NOTE }

In VB,
Declare Function FileExist Lib "PAS.DLL" (byval Filename as String) as Integer

Passing Strings:

From John Kesler:

Thanks to Jonathan Zuck and Mark Novisoff for providing the suggestions used
here to demonstrate a call to the Paradox Engine function pxTblCreate%. This
workaround addresses the problem that VB cannot directly pass an array of
strings to a DLL written for C.

As you can see below, the technique is to append null values to elements in
string arrays (so they "look" like zStrings) and then build an arrays of Long
Ints that point to the address of the strings. As I understand it, the DLL
gets a pointer to the first string element and finds the pointers to the other
elements one after the other.

Declare Function pxTblCreate% Lib "pxengwin.dll" (ByVal tblname As String,
ByVal nflds As Integer,
fnames As Long,
ftypes As Long)

'In your form or module.
'create arrays to hold field names and types
Static vbnames(2) As String
Static vbtypes(2) As String

'append null value to each string element
vbnames(1) = "First" + Chr$(0)
vbnames(2) = "Second" + Chr$(0)
vbtypes(1) = "A1" + Chr$(0)
vbtypes(2) = "A2" + Chr$(0)

'create arrays to hold the segment selector & offset for each element
Static names(2) As Long
Static types(2) As Long

'MicroHelp function SSEGADD& returns the segment selector & offset of
'each string element
For x% = 1 To 2
fnames(x%) = ssegadd&(vbnames(x%))
ftypes(x%) = ssegadd&(vbtypes(x%))
Next x%

'call the pxTblCreate% function with the first element of each array
retval% = pxtblcreate%("Test", 2, names(1), types(1))

From Jonathan Zuck:

Great job! Remember that these addresses are subject to change given any
further string assignments. Make certain to recalc their SSEGADDs each time
before you use them.

Also, the Windows API includes a function that can be used as a SSEGADD
function if you don't have one of the commercial DLLs. The drawback is that it
capitalizes the strings but that ain't a problem in this context.

Declare Function SSegAdd& Lib "Kernel" Alias "AnsiUpper" (byval StrVar$)

SLIDER Control Bug:

SLIDER is a free custom control. In the SLIDER custom control example, there is
an error which causes the control to either repeat an erroneous value or show no
slider knob.

The MODEL flag MODEl_fLoadMsg has been omitted which suppresses the VBM_CREATED
message the control is waiting for to reset it's knob=value to zero. Also the
MODEL_fArrows flag NOT MODEL_fGetArrows flag must be set. (Manfred Waldmeyer)

File Copying:

Open "FileIn" for Binary as #1
whole = Lof(1) \ 32000 'numer of whole 32768 byte chunks
part = Lof(1) MOD 32000 'remaining bytes at end of file
buffer$ = string$(32000,0)
start& = 1
Open "FileOut" for Binary as #2
for x=1 to whole 'this for-next loop will copy 32,000
get #1, start&, buffer$ 'byte chunks at a time. If there is
Put #2, start&, buffer$ 'less than 32,000 bytes in the file,
start&= start& + 32000 'whole = 0 and the loop is bypassed.
next x
buffer$ = string$(part, 0) 'this part of the routine will copy
get #1, start&, buffer$ 'the remaining bytes at the end of the
put #2, start&, buffer$ 'file.

Instead of GET try using the Api function lread : provide it with a handle
after a GloballAlloc and a GlobalLOck and loop to read chunks of 32767 bytes
This is twice as fast as using GETs.

Getting File Date & Time:

Dennis Harrington, with Costas Kitsos help, came up with the following routine
for getting the date and time of an existing file.

DefInt A-Z
cBytes As String * 1
fFixedDisk As String * 1
nErrCode As Integer
r1 As Integer
r2 As Integer
szPathName As String * 128
End Type
Declare Function OpenFile Lib "Kernel" (ByVal lpFileName as String,
lpReOpenBuff As OFSTRUCT,
ByVal wStyle)

Form1 General:
DefInt A-Z
Dim FileMonth, FileDay, FileYear
Dim FileHour, FileMinute, FileSecond

Sub TestMe(Fi$)
x = OpenFile(Fi$, a, &H4000)
FileMonth = ((a.r1 And &H7FFF) \ 32) And &HF
FileDay = a.r1 And &H1F
FileYear = (a.r1 And &H7FFF) \ 512 + 80
FileHour = (a.r2 And &H7FFF) \ 2048
If a.r2 < 0 Then FileHour% = (FileHour% + 16) - 12 'for PM times
FileMinute = ((a.r2 And &H7FFF) \ 32) And &H3F
FileSecond = (a.r2 And &H1F) * 2
End Sub

Sub Form_Click:
Call TestMe("c:\config.sys")
Print FileMonth "/" FileDay "/" FileYear
Print FileHour ":" FileMinute ":" FileSecond
End Sub

Getting File Info:

VB does not let you get detail about files, such as file date, size, attribute.
(Except through API calls. See above.)
Look for VBDOS on DL1 or DL6. Third-party libraries offer these functions too.
Look for demos of QuickPak for Windows and VB-Muscle.

Searching PATH$:

For an example of how to search the directories in the DOS Path for a file,
see the ICONWRKS.BAS module's "Help_File_In_Path". (ICONWRKS is one of the
sample files that comes with VB.)

Controlling Form Size:

Set MaxButton to False so the user can't maximize the form.

Dim OldWidth As Single '-- width before resizing.
Dim OldHeight As Single '-- height before resizing.
Const MinWidth = 6000!, MaxWidth = 9000! '-- change these values to...
Const MinHeight = 3000!, MaxHeight = 6000! '-- the ones you want.

Sub Form_Load ()
OldWidth = Width
OldHeight = Height
End Sub

Sub Form_Resize ()
If WindowState <> 1 then '-- allows user to minimize window.
If Width < MinWidth Or Width > MaxWidth Then
Width = OldWidth
OldWidth = Width
End If
If Height < MinHeight Or Height > MaxHeight Then
Height = OldHeight
OldHeight = Height
End If
End If
End Sub

Copying/Moving Controls:

You can copy or move one or more controls from one Form, Frame or Picture box
to another using the Clipboard:

1. Click on the source Form, Frame or Picture box.
2. Hold the Ctrl key and, one by one, click on all the controls you want
to copy.
3. Press Shift-Del (to move) or Ctrl-Insert (to copy).
4. Click on the destination Form, Frame, or Picture box.
5. Press Shift-Ins.

Covered Form?:

Q: I would like to know if there is a way to tell if a window is completely or
partially covered by another window in Visual Basic. Even if the covering
window is not in the same application.

A: No - nothing straightforward and/or foolproof, anyway.

Cursor in First Text Box:

Q: When my program starts, how do I get it to put the cursor into the first
text box so that the user can start entering data in it immediately?

A: In the design mode, set the boxes TabIndex (in the Properties list) to 0.
During runtime, you can say something like: Text1.SetFocus

"Floating" Window (Forcing Window to Top):

(Also see "Overlapping Controls", below.)
Sometimes you may want a small window to show above the current window, but VB
does not offer this feature. Here is how to force it (courtesy of Ted Young):

Declare Sub SetWindowPos Lib "User" (ByVal hWnd As Integer,
ByVal hWndInsertAfter as Integer,
ByVal X as Integer,
(put this all on --> ByVal Y as Integer,
one line) ByVal cx as Integer,
ByVal cy as Integer,
ByVal wFlags as Integer)
Declare Function GetWindow Lib "User" (ByVal hWnd as Integer,
ByVal wCmd as Integer) As Integer
' Set WindowPos Flags:
Global Const SWP_Nosize = &H1
Global Const SWP_NoMove = &H2
Global Const SWP_NoActivate = &H10
Global Const SWP_ShowWindow = &H40

Form1, Load:
Form2.Show ' this is the "Floating" window
End Sub

Form1, Timer1 (set Interval to 50 in Properties)
Sub Timer1_Timer
If GetWindow(Form2.hwnd,0) <> Form2.hWnd Then
wFlags = SWP_Nomove or Swp_Nosize or Swp_ShowWindow or Swp_NoActivate
SetWindowPos Form2.hWnd, 0, 0, 0, 0, 0, wFlags
End If
End Sub

Form Size and Granularity:

Check the "Granularity" under the Desktop settings in the Control Panel. If
this number is anything but zero, you'll get the effect of all windows only
being able to be sized and placed by increments of 16 pixels multiplied by the
Granularity number. Set it to zero and this should fix things.

Grouped Controls:

If you want to group a set of controls within another control, such as a
Frame, you cannot do so by double-clicking the control and moving it into the
Frame. You must single-click the control and then click-and-drag INSIDE the
frame (or other control) to draw the added control.

Iconizing All Forms in a Project at Once:

You can all the visible windows in your program minimize to one icon when a
"main" window is minimized, and then all be restored when that icon is double

Put some code in the main form's Resize event that tests the WindowState to
see if it's been minimized (WindowState=1), then change the Visible property
of all your other forms (this would make only the Main form an icon, if you
want to iconize the other forms instead of hiding them, you'd change the
WindowState of the other forms). There's an example of this in the VB Lang Ref
pg 331 (WindowState).

Instant Display of Controls:

To make controls appear more quickly, try placing all the controls in a
Picture box with no border and the same background as the form. Leave the
picture box invisible until all the controls are drawn, then make it visible.
Instant display of all the controls. (Dennis L. Harrington)

Mouse Pointer, Position:

Sometimes it nice to be able to place the user's mouse cursor for him. One
example is when the user has to click on a button to continue, you can put the
cursor on the button for him.

Declare Sub SetCursorPos Lib "User" (ByVal x%, ByVal y%)
example: Call SetCursorPos(100,200)

The above code will do a rudimentary job of positioning the cursor.
Here is some more information about positioning:

Using the Windows API call SetCursorPos, you can move the mouse to any
location on the screen. Note that the x & y coordinates are *screen*
coordinates. You'll need to translate the coordinates in your VB
program to screen coordinates. One way is to get the current coordinates of
the mouse using GetCursorPos, and then moving the mouse in relation to those
coordinates. Note that GetCursorPos returns the coordinates in a Type
structure (see below). You can also use the API functions ClientToScreen
and ScreenToClient to convert the coordinates from the Client (your Form) to
the Screen, and back. Experiment with the functions to see which suits your
purpose, and holler if you have any questions. Also, I'd recommend
downloading WINAPI.ZIP which has all the Declares necessary for calling the
API functions.

Declare Sub SetCursorPos Lib "User" (byval x as integer, byval y as integer)
Declare Sub GetCursorPos Lib "User" (lpPoint as PointAPI)
Declare Sub ClientToScreen Lib "User" (ByVal hWnd As Integer,
lpPoint As PointAPI)
Declare Sub ScreenToClient Lib "User" (ByVal hWnd As Integer,
lpPoint As PointAPI)
Type PointAPI
x As Integer
y As Integer
End Type

Note: Type the declares on one line, not two as shown above.

Move Disabling:

You can keep a user from moving a form by setting BorderStyle in the Form's
Properties list to "0 - None".

Moving Controls Cleanly:

When resizing a form and moving the controls around on it, the screen can look
very messy. Hiding the form causes a lot of flickering.

Instead of hiding the form, render invisible the controls being moved until they
are all moved then make them visible. More code but more esthetic too! (Robert
Eineigl, Microsoft Product Support)

Moving Form Without Title Bar:

A user normally moves a form by clicking and dragging on the title bar. If you
have a form without a title bar, you have to make other arrangements if you
want to allow the user to move the form.

One simple method is to trap a mouse click on the form and then bring the
title bar back, along with a box to click on when moving is done, at which
point you hide the title bar again.

Multiple Instances of a Form Not Supported:

Many people have expressed the desire to set up a form and use multiple
instances of it during run-time to display different data sets. This is known
as "MDI" and VB does not support it.

The only alternative at this time is to make as many duplicate forms as you
think you might need.

Here is another alternative from the MS sysops:

Make a separate EXE file with the form that you want to use multiple times.
Then display it by shelling to it, which you can do multiple times. You can
pass data to it via DDE or a text file.

Norton's Desktop, Get in Front of:

If you are using Norton Desktop for Windows and you have a startup form that
loads your main form, the main form may be displayed *behind* the
Windows/Norton groups of icons when you SHOW it.

In your SUB MainForm_Load, put the SHOW statement *before* you UNLOAD
StartupForm, rather than after. (Bob Craig)

Control's Coords. Are Relative to Form:

Note that when you specify coordinates for the placement of controls within a
Form, the coordinates start with 0,0 at the top, left corner of the Form; they
are NOT screen coordinates. (Bob Craig)

Overlapping Controls:

(Also see "Floating" Window, above.)

VB does not directly support overlapping controls. (Controls placed inside
Frames and Picture boxes are not considered overlapping controls.) Overlapping
controls were considered not possible in VB at all until Keith Funk came up
with the following: (for a complete demo program, get CLPSIB)

1. Put the following in the form's General Declarations. You can also put it in
Global.Bas if you change the Declare's, Const's and Dim's to Global's.

Declare Function GetFocus Lib "User" ()
Declare Function ControlhWnd Lib "ctlhwnd.dll" (Ctl As Control) As Integer
Declare Sub SetWindowPos Lib "User" (ByVal hWnd As Integer,
ByVal hWndInsertAfter As Integer,
ByVal X As Integer,
ByVal Y As Integer,
ByVal cx As Integer,
ByVal cy As Integer,
ByVal wFlags As Integer)
Declare Function GetWindow Lib "User" (ByVal hWnd As Integer,
ByVal wCmd As Integer) As Integer
Declare Function GetNextWindow Lib "User" (ByVal hWnd As Integer,
ByVal wFlag As Integer) As Integer
Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer,
ByVal nIndex As Integer) As Long
Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer,
ByVal nIndex As Integer,
ByVal dwNewLong As Long) As Long
Const WS_CLIPSIBLINGS = &H4000000
Const SWP_NOSIZE = &H1
Const SWP_NOMOVE = &H2
Const GW_CHILD = 5
Const GWL_STYLE = (-16)
Dim hWndChild As Integer
Dim Style As Long

2. Create the following subroutine:

Sub SetClipSiblingBit (ByVal hWndParent As Integer)
hWndChild = GetWindow(hWndParent, GW_CHILD)
Style = GetWindowLong(hWndChild, GWL_STYLE)
Style = SetWindowLong(hWndChild, GWL_STYLE, Style)
If GetWindow(hWndChild, GW_CHILD) <> 0 Then
Call SetClipSiblingBit(hWndChild)
End If
hWndChild = GetNextWindow(hWndChild, GW_HWNDNEXT)
Loop Until hWndChild = 0
End Sub

3. Put the following in Form_Load

Call SetClipSiblingBit(Form1.hWnd)
'get hWnd for the controls:
G = GetFocus()
t = GetFocus()
Call SetWindowPos(G, t, 0, 0, 0, 0, (SWP_NOSIZE Or SWP_NOMOVE))

The example above makes a Text box "float" on top of a Grid control.

Placing Forms, Controls:

Sub InitializeWindow
Height = Screen.Height * .75
Width = Screen.Width * .75
Top = (Screen.Height - Height) / 2
Left = (Screen.Width - Width) / 2 '
*** Then place and size your controls.
End Sub

If you want to make the form a specific size, then Scale it in inches,
centimeters, points, or twips (a twip is 1/20th of a point), and don't bother
making references to Screen.Height or .Width.

My application has a background form and pop-up forms. Sometimes, other
windows, such as Program Manager, and even icons, get stuck in the middle
between my background and the popups. How can I force some order in this mess?

In each form's GetFocus, you could use the Floating Window routine above to
force each of the forms in your program to the top.

Resizing Forms:

See "Screen Scaling/Form Resizing" under "Screen"

Scrollable Forms:

If you need to make a form larger than the screen, you can do so by making it
scrollable. Simply put everything in a Picture box and Move it up and down
and/or left and right using the Picture1.Top and Picture1.Left properties.

Unloading Control Arrays:

I am using an array of labels on a drawing to provide visual access to CAD data
for a PCB board viewing app. With 50 to 100 labels loaded, I find that exiting
my app and restarting it is faster than looping thru and unloading each element
before viewing a new drawing. Is there any way to dump and entire control
array at once?

There's no way (besides looping) to delete (unload) all the elements of a
control array. However, if you've got 50 or 100 labels you're using too many
labels which can *severely* affect performance and availability of system
resources for other Windows program. Have you tried simply using the Print
statement instead of labels?

Instead of doing loading/unloading and/or restarting your app, have you
considered simply setting the .Caption property of all the labels to a null
string. You might also want to set .Visible=False, depending on if you need all
the labels for the new drawing.



There are the DLL-Declarations for Btrieve. The requestor must be loaded
before Windows:

Declare Function wbtrvinit Lib "wbtrcall.dll" (ByVal init$) As Integer
Declare Sub wbtrvstop Lib "wbtrcall.dll" ()
Declare Function btrcall Lib "wbtrcall.dll" (ByVal a%, ByVal b$, ByVal c$,
d%, ByVal e$, ByVal f%, ByVal g%) As Integer

FIELD and VARPTR are definitely not necessary. You'll need to set up a
user-defined type to work with Btrieve (instead of FIELD). The call syntax for
Win Btrieve takes care of pointing to the record (instead of VARPTR). Just be
sure to pass any variable-length strings (i.e, A$) BYVAL.

Owners of versions of Btrieve no longer supported, including the old
single-user DOS version, can upgrade to Btrieve for Windows for $125. If you
have a still-supported version, such as Btrieve/N, you cannot upgrade.
To upgrade, call 800-RED-WORD.

If you are using Btrv-Win with a network, the Btrv requestor must be loaded
before Windows, but for single-user set-ups, you do not need anything but the
Btrieve DLL.

Deleting Data in Sequential File:

Here's an idea for deleting text in the middle of a sequential file that might
work. Haven't tried it, but it seems like it would work and wouldn't be
difficult to implement. First, you'll have to save file pointers to the
beginning of each message (e.g., in a Long Int array during message load using
the SEEK function) in MsgPtr&(). Now, if you want to delete message 50 and
there are a total of 200, you'd do the following:

MsgToDelete = 50
TotMsgs = 200
BlockSize& = 32768&'-- make this larger or smaller to find optimum size
Temp$ = Space$(BlockSize&)
Open "MESSAGE.MSG" For Binary As #1

SizeOfFile& = LOF(1)
SizeToMove& = SizeOfFile& - MsgPtr&(MsgToDelete + 1)
NumBlocks = SizeToMove \ BlockSize&
LeftOver = SizeToMove& - Num32KBlocks * BlockSize&
FromLoc& = MsgPtr&(MsgToDelete + 1)
ToLoc& = MsgPtr&(MsgToDelete)

For i = 1 To NumBlocks
Seek #1, FromLoc& + BlockSize& * (i - 1)
Get #1, Temp$
Seek #1, ToLoc& + BlockSize& * (i - 1)
Put #1, Temp$
Temp$ = Space$(LeftOver)
Seek #1, FromLoc& + BlockSize& * NumBlocks
Get #1, Temp$
Seek #1, ToLoc& + BlockSize& * NumBlocks
Put #1, Temp$
'-- Now adjust the MsgPtr& array
TotMsgs = TotMsgs - 1
Adjust = MsgPtr&(MsgToDelete + 1) - MsgPtr&(MsgToDelete)
For i = MsgToDelete To TotMsgs
MsgPtr&(i) = MsgPtr&(i + 1) - Adjust

MKI$ & CVI in VB:

function MKI$ (Work%)
MKI$ = Chr$ (Work% MOD 256) + Chr$ (Work% \ 256)
end function

function CVI% (Work$)
CVI% = (ASC (Left$ (Work$, 1)) * 256) + ASC (Right$ (Work$, 1))
end function


ObjectTrieve/VB seems to be the first data manager available to offer Binary
Large Objects (BLOBS). ObjectTrieve is an ISAM file manager based on the
X/OPEN standard. You may want to consider calling Coramandel at 1 800 535-3267
or 718-793-7963. Ask for Narayan Laksham

Fixed Width Fonts:

Courier, System and Terminal are fixed width fonts.

Large Font Bug:

If you use a large font and then backspace over it, traces of the text will be
left behind. This is a bug in Windows 3.00, by adding a Refresh to the text
box (in the _KeyUp event) you can correct the problem.

Listing Printer Fonts:

Two problems with listing fonts are that (1) you can get duplicate font names
in the list and (2) it is difficult to determine available font sizes.

George Campbell offers the following tips:

The answer to the first problem is to check the current list of fonts before
adding the next one. It's a bit slow, but it works. Use a For...Next loop with
the value List.Fontcount -1 to go through the list box and compare the font
names with the one you're about to add.

The second problem is more difficult. What I do is to simply use an On Error
Resume Next line when specifying font sizes. That way, if the font isn't
available, the size reverts to the last size selected or to a default size in
the case of fixed-size fonts. I suppose you could do that check in a For...Next
loop for the sizes you want to test, exiting the loop if an error occurs.

Dennis Harrington offers the following tip about duplicate font names:

It's documented somewhere in the MSKB that this is a bug is win 3.0. So until
win 3.1, we just have to live with it. There's also a little quicker way to
parse out duplicates when adding them to a list box. First, concetate all the
fonts names into a simple string:

test$ = "" 'initiallize a null string
numFonts = Screen.FontCount 'Use "Printer" for prt fonts
For x = 0 to numFonts 'make string of all font names
test$ = test$ + Screen.Fonts(x)
Next x

For x = 0 to numFonts
v = InStr(1, test$, Screen.Fonts(x)) 'find 1st occurance of font
z = InStr(v+1, test$, Screen.Fonts(x)) 'look for a 2nd occurance
If z = 0 then 'if there are no more occur-
ListBox.AddItem Screen.Fonts(x) 'rances of the font, add it
End if 'to the list box.
Next x

Instead of reading the entire list box for each new font (Very slow!), you only
need to make 2 very fast InStr calls for each font.

Using Different Fonts, Colors in a Box:

The only way to have different colors and fonts in a box is with the .Print
method in a Picture Box or on the Form itself. No other VB boxes allow
different colors and fonts.

System Font:

ALERT: In a List box using the System font, 9 pitch is non-proportional, 9.75
is proportional.

Changing Form Name:

To change the name of a form (not the form's FILE name), select the form and
pick FormName in the Properties list. This is often overlooked, for some

Detecting a Form's Getting Focus:

The following will detect when a form gets focus:

Sub Main
hMyTask = GetCurrentTask()
hOldTask = hMyTask
Ok = DoEvents()
hTask = GetWindowTask (GetActiveWindow)
Loop Until hTask <> hOldTask
If hTask = hMyTask then
End If
hOldTask = hTask

Flashing a Form:

Declare Function FlashWindow% Lib "user" (ByVal hWnd%, ByVal bInvert%)

z& = BackColor
For i = 1 to 12
FlashColor% = Int(7 * Rnd + 1)
BackColor = QBColor(FlashColor%)
FlashIt% = FlashWindows(hWnd, 1)
For j = 1 to 20: Next
RestoreIt% = FlashWindows(hWnd, 0)
BackColor = z&
'from Mike Mezaros' "Executive Decision Maker"

Focus & Order of Execution:

Some times VB will delay doing something while it executes more code. For
example, if you write a lot of stuff to a List box in a tight loop, the List
box display on the screen will not be shown updated until the code comes to a
"resting point" (ie: waiting for user input).

Focus on StartUp:

If you try to display a form on startup that has your logo, copyright notice,
etc, you may find that the form displays, but the detail on it does not. You
have to force VB to wait until the detail is displayed. One way to do that is
do define a Global variable and when you are finished with the Title box, set
the variable ("Continue", in the example below) to true (-1).

x = DoEvents() 'allow control to the system
Loop Until Continue = -1

Another way is to "Loop Until Len(Label1.Caption) > 0" (You can omit the >0.)
This assumes you have a Label1 box, of course.

Another problem is trying to get some other form to load and to transfer focus
to it during start-up, and then continuing the start-up. What happens is that
you can do a Form2.Show, for example, but after the Form2_Load code (if any)
is run, focus will immediately return to the original form. Users don't get a
chance to interact with Form2. If Form2 is an installation form, for example,
the installation will never get done.

The solution is to use Show Modal (eg: Form2.Show 1), with the possible
drawback being that you have to Unload the form to return to the calling form.


GotFocus will not be executed for a Form that has a control on it.

Modal Forms, Unloading:

You can perform a Form.Hide in the modal form and then unload it when you
return back to the calling form.

SetFocus Won't Work?:

You cannot do a SetFocus until a form is visible. Use Show instead.

Unloading Forms Completely:

If you have some reason to suspect that your VB apps aren't exiting as
completely and cleanly as they are supposed to, you may have a hidden form
lurking around.

Make it a point in the main form's Unload proc to unload all other forms
associated with the app:

Sub Form_Unload(Cancel As Integer)
Unload Form2
Unload Form3 ' etc.
End Sub

Unloading Forms From Task Manager:

Form_Unload will not be called if you exit a VB app from the Task Manager.
MS is aware of this problem.

Unloading Forms on Windows Termination:

If Windows is about to terminate, it will invoke your Form_Unload procedure. In
that proc, you can do whatever you want, including telling Windows to *cancel*
the shutdown!

To cancel the Form_Unload, use: Cancel=True.


DEFINT A-Z in your Global file does NOT have a GLOBAL effect. You must do a
DEFINT A-Z in the General_Declarations of each form.

Global Const CRLF = chr$(13) + chr$(10) Not Allowed:

Several people have complained about the above command not being allowed. (You
cannot use CHR$ in a Global Const.) This is not a bug, but a limitation of the
language. Use the following instead:

Global CRLF$

Sub Form_Load() ' Or Sub Main, if you prefer
CRLF$ = CHR$(13) + CHR$(10)
End Sub

WINAPI - "Out of Memory":

WINAPI contains all the Declares for the API for VB. However, you can NOT
simply load this file into Global.Bas or you will get an out of memory error.
(It is too large, for one thing.)

Instead, load it into Write or some other editor or word processor with
sufficient capacity and cut-paste just the Declares that you need into Global.

A cross-reference file for WINAPI is now available: APIXREF.HLP. To access
this file, while in VB press F1 for Help, then File-Open from the menu to load
the file.

What is the Grid Control?:

The Grid Control is like a List Box divided into columns. It was given to beta
testers of VB, but not included in the final product; it is not buggy, but it
has some serious limitations, which is why it may have been left out.

First of all, the GRID uses the local heap for memory and that memory is
*very* limited. The amount of data a Grid will hold varies depending, it
appears, on the number of columns you specify. It may hold less than 14k or up
to 35k.

Because of the memory limitations and because it is so slow, if you are using
a large array of data, it is doubtlessly preferable to keep the data in an
array (or even on disk) and display a small chunk at a time in the Grid.

The file RANDGR.EXE on DL6 shows how to manually scroll data through a Grid.

Can't Enter "&":

You cannot put a single "&" as text in a cell of a Grid. You have to put "&&".

Faster Row Adding/Deleting:

Fm: Jim Dolson

Inserting a blank line (or deleting a line) in a GRID has always been slow. If
you have a Grid with 300 rows, and you wanted to insert a new line at row 10,
you'd have to move 290 rows down, one at a time.

A MUCH faster way is to define the block of rows to move using .SelStartRow and
.SelEndRow properties, .Clip it, then select the new block (which begins one
row down from the previous block) and .Clip it again! It reduced the time
required to do an insert by 90%.

InsertRow% = Grid.Row 'insert a new row at the current row.
LastRow% = Grid.Rows 'last row in the grid.

Grid.Rows = LastRow% + 1 'since we are inserting a new row, our Grid
'is one row larger than before.

Grid.SelStartRow = InsertRow% 'select the block that we are going to
Grid.SelEndRow = LastRow% 'move down.
Grid.SelStartCol = 0 'in this example, we've got 9 columns
Grid.SelEndCol = 8 'in the grid.

Stuff$ = Grid.Clip 'put the contents of the selected rows
'and columns into Stuff$

Grid.SelStartRow = InsertRow% + 1 'define a new block which starts one
Grid.SelEndRow = LastRow% 'row down from the previous block.

Grid.Clip = Stuff$ 'and stuff the old block into the new one.

Changing Icons:

You can select from a set of icons, using Form1.Icon = ... . You can also draw
on the icon exactly as if it were a normal-sized form by setting
Form1.Icon = LoadPicture()
Then you can draw on it with normal drawing statements (Line, Circle, etc).

Icon - Get Rid Of:

1. Click on the form
2. Select the Icon property from the properties bar.
3. Click on the middle portion of the properties bar, where (icon) shows.
4. Press the Del key

Transparent Background:

If you create an icon with Icon Works (comes with VB), the area behind the
icon is colored in. To make this area transparent to allow the wallpaper to
show through, use a different program such as the shareware program Icon Magic
which is available on CompuServe and from Public (software) Library.

Calling the Same Routines:

The easy way to get both the Mouse Click and the Enter key to do the same
thing is to write a third sub that you call from the other two:

Sub CommonHandler
' Do your thing here
End Sub
Sub Ctlname_Click()
End Sub
Sub Text1_KeyPress(....)
If KeyAscii = 13 Then
KeyAscii = 0
End If
End Sub

INKEY$ Loop Emulation:

In other Basics, you can say:

[do stuff]

VB does not support INKEY$, so here's how to emulate the concept of looping
until a key is pressed:

DefInt A-Z
Declare Function PeekMessage Lib "User" (ByVal Msg$, ByVal hWnd, ByVal Min,
ByVal Max, ByVal Flag)
Const WM_KEYFIRST = &H100 Const WM_KEYLAST = &H108

Function NoKeys ()
Result = PeekMessage(Msg$, 0, WM_KEYFIRST, WM_KEYLAST, 1)
NoKeys = Result -1
End Function

SomeSub ():
While NoKeys ()
[do stuff]
End Sub

This function does *NOT* yield to other Win apps like DoEvents does, if you
want the same functionality then the last parameter passed to PeekMessage
should be zero (0). -=- Jonathan Zuck

Keyboard/Mouse Shortcuts:

Fm: Chris Sacksteder

1. To show a control's first event procedure, double-click on the control.
2. Use tab to cycle through controls.
3. Ctrl+up and Ctrl+down can be used to quickly cycle through procedures.
4. Ctrl+right and Ctrl+left move the cursor by word.
5. Ctrl+Y cuts the current line to the clipboard.
6. Ctrl+N opens a blank line before the current line.
7. double-click on a word to mark the whole word.
8. select a procedure name (double click on it) and press Shift+F2 to jump
to that procedure.
9. select several lines and press Tab to indent all of them; Shift+Tab will
undent them
10. Click-drag down the top of a code box to open a second code box.
11. use the keyboard to jump to the first letter of any list (controls,
procedures, properties)
12. Get help on any keyword by placing the carret anywhere in the word and
pressing F1 (in the Immediate window you have to select the entire
keyword before pressing F1).
13. If you get an error during runtime, press F1 to get more information.

Mouse Pointer Style Setting:

Each form has it's own mouse pointer. If you want to keep the pointer as an
hourglass, for example, while different forms are loaded, you need to place a
"_.MousePointer = 11" statement in each Form_Load.

Alternatively, you can use the Screen.MousePointer object/property to set the
hour glass for all forms in the Project. (It does not affect the MousePointer
for other Windows applications that you may be running at the same time.)

Trapping Double-Click before Click:

If you have code assigned to both the Click event AND the Double-Click,
when you double-click, VB will execute the Click code on the first click of
the double and the D-C code on the second click.

Here is a routine for letting you have both (Nelson Ford):

Defint A-Z
Dim DblClk, Reject

Sub List1_Click:
If Reject then Exit Sub Else Reject = -1
For I = 1 to 500: y=DoEvents(): Next
If DblClk Then Exit Sub
Print "Single Click"
Reject = 0
End Sub

Sub List1_DblClick ()
If DblClk Then DblClk = 0: Exit Sub
Print "Double Click"
Reject = 0
End Sub

Using "Enter" in Place of "Tab":

Sub Text1_KeyPress(KeyAscii As Integer)
If KeyAscii = 13 Then
KeyAscii = 0 ' Avoid the beep
SendKeys "{Tab}" ' Simulate pressing the Tab key
End If
End Sub

An Alternative to Label Boxes:

You can save system resources by printing directly on a form instead of using
a Label control:

LabelText$ = "Some text"
e = FormName.TextWidth(LabelText$)
FormName.CurrentX = TextControl.Left - e - (the amount desired for spacing)
FormName.CurrentY = TextControl.Top (+ or - the desired spacing)
FormName.Print LabelText$;
(Dennis L. Harrington)

Label Box Text Background Color:

With some light backgrounds for a label box, the background behind each
individual letter in the box is a different (darker) color. This is a
"feature" and nothing can be done about it.

"AddItem #" Error:

You will get an error that may be hard to figure out if you do a
List1.Additem x$, n
and "n" is a value greater than ListCount.


The List Box should handle strings up to around 1K in length with a total size
around 64K. Your mileage may vary, as Bill Reilly discovered: Combo boxes and
List boxes will only accept up to 5440 lines. Try this:

For i = 1 to 10000
List1.AddItem "x"

It will bomb out at i = 5440

Clear a List Box:

The following routine will quickly clear a list box:

Declare Function SendMessage% Lib "user" (ByVal hWnd%,_
ByVal wMsg%,_
ByVal wParam%,_
ByVal lParam&)
Declare Function GetFocus% Lib "user" ()
Declare Function PutFocus% Lib "user" Alias "SetFocus" (ByVal hWnd%)

Form Declarations:
Const WM_USER = &H400

Sub ClearListBox (Ctrl As Control)
hWndOld% = GetFocus()
x = SendMessage(GetFocus(), LB_RESETCONTENT, 0, 0)
Suc% = PutFocus(hWndOld%)
End Sub

Finding an Item Added to a Sorted List Box:

When you AddItem to a Sorted List Box, the lines displayed in the List Box do
not automatically change so that the new item is displayed. You can search for
the new item manually (see next Tip) or instead of using List.AddItem, use the

SendMessage(hWnd, LB_ADDSTRING, wParam, lParam).

This adds the item to the list and returns the ListIndex of the newly added
item. Here is the complete routine (from Bob Scherer):

In the Global module, declare the following constants & functions:

Global Const WM_USER = &H400
Global Const LB_ADDSTRING = (WM_USER + 1)
Declare Function GetFocus Lib "user" () As Integer
Declare Function SendMessage Lib "User" (ByVal hWnd As Integer,
ByVal wMsg As Integer,
(all this on one line)--> ByVal wParam As Integer,
ByVal lParam As Any) As Long

In the procedure where you want to do the "AddItem" use this code:

LbhWnd = GetFocus()
RetVal& = SendMessage(LbhWnd, Lb_AddString, WParam, MyInput$)
List1.ListIndex = RetVal&

Finding an Item Manually:
(Also see the preceding and following Tips.)

The following routine uses a binary search to find an item in a sorted List

Object: (general) Proc: FindItem
Sub FindItem (Lst as Control, a$)
U = Lst.ListCount
L = 0
If U < L Then
MsgBox "Not Found"
Lst.ListIndex = I 'set .ListIndex to nearest match
Exit Sub
End If
I = (L + U) / 2
If a$ = Lst.List(I) Then
Lst.ListIndex = I 'Found. Set ".ListIndex" accordingly
Exit Sub
ElseIf a$ > Lst.List(I) Then
L = I + 1
U = I - 1
End If
(Nelson Ford)

Finding an Item via API:

The following routine will search a List box, sorted or unsorted, for the
first occurrence of a specified string. (Don Funk, MS)

Declare Function SendMessage& Lib "user" (ByVal hWnd%, ByVal wMsg%,
ByVal wParam%,
ByVal lParam As String)
Declare Function Getfocus% Lib "user" ()
Dim ListHwnd As Integer

Sub Form_Click ()
List1.AddItem "John"
List1.AddItem "George"
List1.AddItem "Sue"
End Sub

Sub Text1_Change ()
Const LB_SELECTSTRING = &H400 + 13
Static X As Long
wParam = 1
lParam$ = Text1.Text + Chr$(0)
X = SendMessage(ListHwnd, LB_SELECTSTRING, wParam, lParam$)
Print X
End Sub

Sub Form_Load ()
ListHwnd = Getfocus()
End Sub

Font Size Changes List Box Size:

If you change font sizes during runtime, VB will change the size of the List Box
to adjust it so that partial lines are not shown. This causes an unwanted
change in your display, and VB does not let you disable this feature.

The ratio between 8.25 print size and 9.75 is around 1.2 and if you set the
number of lines with this ratio in mind, the number of lines displayed can
change without changing the size of the List Box.

For example, 6, 11, or 22 lines of 8.25 text will convert to 5, 9, or 18 lines
at 9.75. (Keith Funk)

Forcing an Item Number to the Top:

To force a particular item number in a List Box to be at the top of the box,
enter the following two lines:

List1.ListIndex = List1.ListCount - 1
List1.ListIndex = ItemNumberWanted

If you do not enter the first line, the line you want will be in the List Box,
but not necessarily at the top of it.

Inhibiting Screen Updating:

If you are adding a whole bunch of items to a list box all at once like
assigning items from an array from within a loop, you might want to use the API
WM_SETREDRAW message to prevent the list box from being continually updated on
the screen as each item is added. Not only does this stop the flickering, but it
makes adding the items much faster.

A simpler method is just set the List box's Visible property to 0 and reset it
to -1 when done, although this will cause the List box to disappear completely.

Linking Sorted and Unsorted List Boxes (or Arrays):

You may need to link data in an array or an unsorted list box to data in a
sorted List box; however, when an element is added to a sorted List box or if
you manually sort an unsorted List box, the array or related List box will no
longer be in sync with the sorted List box..

There are several ways to keep the two in sync:

One way is to use the routines under "Finding an Item...", above, and then
adjust the unsorted array or List box accordingly. The file VBSORT on DL6
illustrates how to sort a List box and an array at the same time.

Another way is to add a pointer at the end of each item in the sorted List
box. By tabbing over far enough, the pointer will not show up in the List box.
This method is not ideal if the contents of the List box will be approaching
the 64k space limit, since adding a pointer takes up space in the List box.

A third way is to append a 4-byte value to each item in a List Box by using
the LB_SETITEMDATA message and you can retrieve the value using the

The tag is created when you add an item to the List Box. The value of the tag
can be retrieved at anytime. This approach requires a good deal of familiarity
with API calls.

"ListIndex = -1" Generates Click Event:

If you use "List1.ListIndex = -1" to unselect a highlighted item in a List
box, a (probably unwanted) click event will be generated.

To negate the unwanted click action, add the following line to List1_Click:
If List1.ListIndex = -1 Then Exit Sub
(Selden McCabe)

Selecting Multiple Items:

The VB List Box does not directly support selecting multiple items.

MULTIPK is a sample project that shows how to create a VB List box that DOES
allows selecting multiple items. Look for it in the MSBASIC forum or in PsL.

Tab Stops:

I have downloaded App Note Q71067 on "How to Set Tab Stops Within a List Box".

The sample code in the note works fine, but if I change the lengths of the
quoted elements in the list, the example falls apart. It appears that there are
pre-existing tab stops *between* the stops I create! Does anyone know if this
is, indeed, true? And if so, how do I clear the preexisting tab stops before I
load in my own.

The LB_SETTABSTOPS does not use character units, but a Dialog unit based on
the currect system font width and height. This means you may have to adjust
the tabstop values to a larger size depending on the fontname and fontsize.

Try increasing the values and use a mono-spaced font to get an idea of how it
works, it is a pain!

AppName.Caption Problem:

Page 118 of the Programmer's Guide shows how to dynamically adding items to a
menu. About 3/4ths of the way down the page, it shows a routine with the last
line being: AppName(LMenu).Caption = FileSpec$

If you follow this example, you will get another separator bar. To get the
FileSpec$ to show, you must add the following line:

AppName(LMenu).Separator = 0

Calling Help for Menu Items:

It would be nice to be able to call the Windows Help Engine from a menu where,
if a user has a menu selection highlighted and hits F1 or Sh-F1 then help will
come up for that menu item.

Here's how to detect those keypresses:

1. Global - declarations
Global HelpTopic%
' Also, set up some constants to use for help topics here. It will
' make your program much more readable

2. In each menu item's Click proc:
Sub Ctlname_Click
' You can also sprinkle statements like this in appropriate
' areas of your code if you like.
HelpTopic% = One_of_the_CONST_items
End Sub

3. In each control's keydown proc:
Sub CtlName_Keydown(.....)
CheckHelp Shift, KeyCode
End Sub

4. In a module (or the form, if it's a single form program)
Sub CheckHelp(Shift%, KeyDown%)
If Shift% = DesiredShiftStatus and KeyDown% = HelpKey Then
' Invoke WINHELP here, passing HelpTopic%
End If
End Sub
(Mark Novisoff, MicroHelp)

ClipBoard Status:

If you have a menu set like the following:


and you want to gray out Paste and Copy when the clipboard is empty, then
check the clipboard's contents as soon as "Edit" is clicked and
modify the enabled property of the Paste and "Copy selections accordingly.

Disabling Menu Items:

There's an example in the VB manual that shows how to enable and disable menu
items as a menu is opened. Let's say I have a "File" Menu with CtlName
FileMenu, and "Open" and "Save" items on that menu with CtlNames of FileOpen
and FileSave respectively, and I want the "Save" Menu item to be Enabled or
disabled depending upon whether a file is currently open.

As an extension of the example in the manual, I might intercept the
FileMenu_Click event, then set the FileSave.Enabled property. This method is
acceptable, unless there is an accelerator key associated with the FileSave
menu item. In that case, the FileSave.Enabled property may not have be set
correctly when the user presses the shortcut key, unless the FileMenu has been
opened in the mean time. (Steve Schmidt)

Focus Doesn't Change:

If you choose a menu item that executes another procedure, the control that
had focus before the menu selection never loses it; that is: LostFocus for
that control is not executed before the menu's procedure is executed.

You can manually trigger it if you know which control had focus by entering,
for example, Call Text1_LostFocus. You can determine which control had focus
by setting up a set of flags to keep track of each control. For example:

Global Command1Focus

Command1Focus = -1

Right Justify "Help":

In Windows 3.0 menu bars, "Help" is right-justified. Here's how to do it in your

Using the Menu Design Window, name the final top-level menu item caption &HHelp.
After saving and making an EXE, use a hex editor to change the "&" to the BS
character (HEX 08), and the first "H" to an "&". (Chris Calkins)

The following is a routine to do it from within VB (Ted Young):

DefInt A-Z
Declare Function GetMenu Lib "User" (ByVal hWnd)
Declare Function GetSubMenu Lib "User" (ByVal hMenu, ByVal nPos,)
Declare Function DrawMenuBar Lib "User" (ByVal hWnd)
'-- get the menu handle for the form
hMenu = GetMenu(Form1.hWnd)
'-- get the handle to the submenu: nPos is the item number starting at 0
' e.g., if &Help is the 3rd main menu item, nPos = 2
hSubMenu = GetSubMenu(hMenu, nPos)
'-- create the menu string (you can use GetMenuString if you don't
' want to create the menu string from scratch, but merely change
' what was previously there -- see the Win Ref manual)
Menu$ = Chr$(8) + "&Help"
'-- nPos is the same as above in hSubMenu
nResult = ModifyMenu(hMenu, nPos, MF_BYPOSITION, hSubMenu, Menu$)
'-- redraw the menu bar
DrawMenuBar Form1.hWnd

Screen Flicker on Selection:

I have a VB app whose main window has a variable menu. I make some of the
entries visible or invisible depending upon circumstances. The problem I am
encountering is the amount of screen flicker when this menu change occurs.
Dropping one item and adding another on the menu bar causes a lot of flicker on
the whole screen, and takes display time to boot!

You can set the .Visible property of the "main" item to 0, update the menu,
then set .Visible back to -1.

As Buttons:

You can use a Picture Box to create a button with whatever graphics you want
on it. However, the Click event code will only be executed only if the mouse
cursor is still over the control when it is released. This could cause


BitBlt works faster with AutoRedraw OFF. The problem is that SavePicture
Picture1.Image only works with AutoRedraw ON. A solution is to leave
AutoRedraw Off while you do your drawing and then use the following code to
BitBlt the non-persistent image that you just created to the persistent image.
Note that AutoRedraw is OFF when this code executes.

ScrnhDC = Picture1.hDC 'Handle to non-persistent image.
Picture1.AutoRedraw = True
PersistenthDC = Picture1.hDC 'Handle to persistent image.
'BitBlt from ScrnhDC to the PersistenthDC.
Picture1.AutoRedraw = False

In addition, I sometimes follow this with Picture1.Picture = Picture1.Image.

This gives me three different levels of 'protection'. Picture1.Cls with
AutoRedraw OFF erases the non-persistent image only. Picture1.Cls with
AutoRedraw ON erases both the persistent and non-persistent images, but
preserves the Picture Property image. And Picture1.Picture = LoadPicture() zaps
all three images.

This works great for graphics created by VB graphics commands and the API
PolyLine Function. - KeithF

BitBlt to/from Hidden Box:

If you want to BitBlt from/to a non-visible picture control, make sure the
AutoRedraw property is set to TRUE.

"Box" Command:

There's no explicit BOX command, but it's part of the LINE syntax. Check out the
manual for the syntax (pg 172).

To Clipboard as Metafile?:

Q: How I copy the contents of the picture box to the clipboard as a metafile?
Just using the metafile parameter doesn't work.

A: The graphic methods create BMPs, not Metafiles.

Copying a Drawing to Clipboard:

The following will work, IF Picture1.AutoRedraw is True. (J. Zuck)

Sub Picture1_Click ()
Picture1.Circle (1000, 1000), 500
ClipBoard.SetData (Picture1.Image) 'Picture1.Picture won't work.
End Sub

Sub Picture2_Click ()
Picture2.Picture = ClipBoard.GetData() 'Picture2.Image is illegal.
End Sub

Copying Text in a Picture:

If you use the Print command ("Picture1.Print...") to print text in a Picture
box, that text will NOT be copied if you use a command like "Picture2.picture
= Picture1.picture". You have to re-print the text in the second box.

Cutting-Pasting to a Picture Box:

Fm: Don Funk (SL) 76701,155

The code that follows demonstrates how to place the pawn from the CHESSS.BMP
(that comes with Windows) into a picture control.

1. Create the following controls using the Properties Bar:

CntlName Height Left Top Width
Form1 4920 7305
Picture1 2295 360 4440 2535
Picture2 2415 4080 1560 2535
Command1 855 3600 240 1695

Form1.Picture = Picture("c:\win\chess.bmp")
Picture1.Visible = FALSE
Picture1.AutoRedraw = TRUE
Picture1.scalemode = PIXEL
Picture2.scalemode = PIXEL

2. Enter the following code:
(Get the Declare Function BitBlt Lib from "Saving Picture Files" further
down in the VB-TIPS file.)

Sub Command1_Click ()
'* Handle for the destination bitmap.
hDestDC% = Picture1.Hdc
'* X% & Y% coordinates of the invisible bitmap
X% = 0: Y% = 0
hSrcDC% = Form1.Hdc
'* Specified X1 & Y1 coordates of the bitmap to clip.
XSrc% = 80
YSrc% = 25
'* Specify the dimensions of the bitmap to copy.
nWidth% = 110
nHeight% = 110
'* Specify source copy{SRCCOPY}
dwRop& = &HCC0020
'* Copy section of the bitmap to the hidden picture control.
S% = BitBlt%(hDestDC%, X%, Y%, nWidth%, nHeight%,
hSrcDC%, XSrc%, YSrc%, dwRop&)
'* Copy to the clipboard.
clipboard.SetData Picture1.Image, 2
'* Copy to the Picture2 control.
Picture2.Picture = clipboard.GetData(2)
End Sub

Dragging Text Box in Picture:

The following is code by Don Funk for dragging a Text box in a Picture box:

Declare Function SetParent% Lib "user" (ByVal H%, ByVal H%)
Const DRAG_START = 1
Const DRAG_STOP = 2

Sub Picture1_DragDrop (Source As Control, X As Single, Y As Single)
Print X, Y
Child% = GetFocus()
NewParent% = GetFocus()
X = X - Text1.Width / 2: Y = Y - Text1.Height / 2
H% = SetParent(Child%, NewParent%)
W% = DoEvents()
Text1.Drag DRAG_STOP
Source.Move X, Y
End Sub

Sub Form_DragDrop (Source As Control, X As Single, Y As Single)
Print X, Y
TextHwnd = GetFocus()
H% = SetParent(TextHwnd, Form1.Hwnd)
X = X - Text1.Width / 2: Y = Y - Text1.Height / 2
Text1.Drag DRAG_STOP
Source.Move X, Y
End Sub

Sub Command1_Click ()
End Sub

Drawing - Scale:

You can change the scale for pictures from twips to virtually anything you
want (100 allows you to do easy percent drawing), but you still have to draw
by using pixel measurements. I found this out by trying to set DrawWidth to
draw a line covering the middle 20% of a small picture.

Faster Drawing:

You can speed up drawing by having a hidden (.Visible=FALSE) picture box which
has AUTODRAW=TRUE. You echo your output to this box but the autodraw does not
slow things down because it is hidden. Then, in the paint method of your
visible picture box, blast the image from the invisible box to the visible
box. The whole thing is *very* fast and preserves your work. - J. Zuck

If the drawing is slow and complex, then bilblt is the best way.

You won't need a hidden picture control to bitblt to as you already have two
additional images available that are part of any picture control. They are the
persistent image (that AutoRedraw uses) and the Picture Property.

Leave AutoRedraw OFF. Draw on the 'screen' image of the picture control
(non-persistent image) then bitblt the screen image to the persistent image.
This is a way of turning AutoRedraw ON after you have drawn the image. It
makes for fast drawing and a way to protect what you have drawn.

You can get the hDC of the persistent image by turning AutoRedraw ON, using
hDC and then turning AutoRedraw OFF.

You also have available another level of protection. You can copy the persistent
image to the picture property (ctl.picture = ctl.image). The picture property
doesn't get erased even if you use CLS with AutoRedraw ON. - Keith Funk

Number of Colors Supported:

VB only supports 16-color graphics, even if your system supports 256-colors.
This is well-documented in the manuals, but a lot of people ask about it.

Saving Picture Files:

It seems that SavePicture saves everything when the property is a Picture on a
Form, but it saves only the original loaded file when the property is a
Picture on a PictureBox, and it saves only the new drawn lines when the
property is an Image in a PictureBox.

The following should fix it:

'BMP or WMF loaded into Picture1
'Redlines added with Line, Pset, etc., then
Form1.Picture = Picture1.Image
PictureSave Form1.Image, "TEST1.BMP"

This works with WMFs but they cannot be changed and saved as WMFs, they get
saved as BMPs, which may or may not be ok with you.

Another example was posted:

Sub SavePicture_Test ()
Picture1.Picture = LoadPicture("chess.bmp")
Form1.Picture1.Line (1, 1)-(1000, 1000)
SavePicture Picture1.Image, "test.bmp" }"Image" not "picture"!
Form1.Picture1.Picture = LoadPicture("test.bmp") End Sub

More input on the subject:

1. Set AutoRedraw to True, and use the .Image property in the SavePicture

2. If you don't want to use the AutoRedraw property (since it is slower when
it's True than when it's False), use the following code before your SavePicture

hScreenDC = Picture1.hDC 'get the device context handle of the "screen"
Picture1.AutoRedraw = -1 'set it to true, so the hDC points to .Image
Picture1.AutoRedraw = 0 'set it back to normal
SavePicture Picture1.Image,"test.bmp" 'save the image with all changes

Note: type the "bitblt" statement all on one line, and don't forget to include
the following in your Global.Bas code form:

'type the following all on one line (this is taken from WINAPI.TXT)
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

Global Const SRCCOPY = &HCC0020

ScaleHeight Negative:

If you set ScaleHeight negative to get positive Y up on a form or picture, the
height of a label must be changed in code with a NEGATIVE height. Since the
object height is explained as being measured positively from the top edge
down, this is inconsistent.

If you set the height with a positive number your program MIGHT run with
garbage on the screen or crash with an error message. - Richard S. Johnson

Aligning Text of Different Sizes:

By default, windows aligns text characters based on the top of each
character's cell. If you use several fonts, this can result in some funny
looking typography because their base lines don't lineup, even if they're the
same point size.

By using the API call SetTextAlign to reset the text alignment to TA_BASELINE
which aligns characters on their typographical baselines, you can mix
different fonts and different sizes and they line up correctly.

If you do this, remember that with base aligned text, CurrentY is the text
baseline and not the top of the character cell. Printing the first line with
CurrentY = 0 will only print the descenders of text characters on that line.
(Dennis L. Harrington)

Calculating Page Length:

If you use GetDeviceCaps to determine the printable page size, you know the
actual printable page length. It's then easy to adjust your printing to any
length paper.

Compute the number of verticle twips on the page and subtract whatever you want
for top and bottom margins and 1 line of text. Then just print from some form of
loop and increment the CurrentY yourself by the number of verticle twips
required for each line.

Each time you increment the CurrentY, test the new value against the maximum
computed above. If the new CurrentY is greater than the maximum, you've filled
the page and it's time to start a new page. This is especially handy with laser
printers may be use either letter or legal size paper. (Dennis L. Harrington)

Changing Paper Bins:

The following is a routine to change paper bins between pages on a laser printer
(Dennis L. Harrington):

In Global Module:
DefInt A-Z
Type BinInfo
BinNumber as Integer
nBins as Integer
res as string*8
End Type
Declare Function Escape Lib "GDI" (ByVal hDc%, ByVal nEscape%,
ByVal nCount%, Indata as Any, OutData as Any) as Integer

Code for changing the paper bin:
Dim BinIn as BinInfo
BinIn.BinNumber = {desired bin number} or &H8000
'NOTE: You must enter the appropriate bin number for the printer in use.
'On a NEC LC890 for example, the top bin is 1 and the bottom bin is 3.
'Using "or &H8000" makes the change immediate for the next page.
'Omit "or &H8000" and the change does not occur till the next print job.
e% = Escape(printer.hDC, GETSETPAPERBINS, 12, BinIn, 0&)

Code for reading current paper bin status
Dim BinOut as BinInfo
e% = Escape(Printer.hDc, GETSETPAPERBINS, 12, 0&, BinOut)
Current_Bin_Num% = BinOut.BinNumber
Num_Available_Bins% = BinOut.nBins

The value for BinNumber is a printer-dependent value. For example, on an NEC
LC890 in postscript mode, BinNumber = 1 uses the upper bin and BinNumber = 3
LaserJet IID, BinNumber = 0 uses the upper bin and BinNumber = 1 uses the lower
bin. Thus, you need to figure out which BinNumber relates to which tray for the
printer and use those numbers.

EndDoc vs NewPage:

Use NewPage between pages (if required) and EndDoc at the end of the print. This
will keep another app from printing in the middle of a long printout. There is
NO need to put NewPage just before the EndDoc.

Put DoEvents in your print routine. This will let other apps print, but they
still won't break up your document. The Windows print buffer and print manager
are smart enough to know to keep documents together. (Jack Presley)

Font Not Available on Printer:

If your VB app uses a font that is not available on a user's printer, the next
closest font (based on size) will be used instead. (Robert Eineigl, MS)

Formfeed Problems:

I am writing a label printing routine in VB, and in order to align the labels,
I want to be able to print only one test label. The problem I am seeing is
that the label won't print until I either close the program completely, or
send a Printer.NewPage or Printer.EndDoc. However, both of these force a page
feed (11 inch type) which wastes the next 10 labels. How can I close the Print
Manager file to force a printout of only 3 lines?


Getting Available Font Sizes:

How does one get a list of the available sizes for a font that has been

1. You only deal with stroke or PostScript fonts. Then any size is available
for all practical purposes.

2. For "raster" fonts such as LJ non-PS font cartridges, you have to use the
API EnumFonts function to get a list of every font in every available size for
the printer device context. The bad news is that EnumFonts is one of those API
calls which cannot be used directly from VB. Rather than returning meaningful
data, it passes the data back to a user defined function for processing, and
there's no way to create this user defined function in VB. Definitely custom
DLL time. Any DLL guru's listening? This would be nice to have.

3. I think you could super-kludge getting available raster font sizes using a
loop to specify every font size in a range of sizes and use an error handler to
bypass the invalid property error generated for every specified size that does
not exist. (Dennis Harrington)

Getting List of Installed Printers:

buf$ = String$(2048, 0)
BufSize% = len(buf$)
y% = GetProfileString("devices", ByVal 0&, "Error", buf$, BufSize%)

You'll need to parse through buf$ to find all of the printers. Chr$(0) will
delimit each printer entry.

Landscape Setting on HPLJ:

The following code lets you change a LaserJet to landscape mode.
(From Dennis L. Harrington)

Orientation as Long
Pad as string * 16
End Type
Declare Function PrtOrient% Lib "GDI" Alias "Escape" (ByVal hDc%,
ByVal nEsc%,
ByVal nLen%,
lpData as ORIENT,
lpOut as Any)
Global prtO as Orient

Sub Form_Click
prtO.Orientation = n 'n=1 for portrait or 2 for landscape
x% = PrtOrient%(Printer.hDC, 30, len(prtO), prtO, 0&)
Printer.EndDoc '- required to make the change take effect immediately.
'EndDoc usually generates a FormFeed, which would be unwanted here, but in
'this case, coming right after the PrtOrinet line, it does not. Thanks to
'John Kesler for pointing this out.

Printer.Print "whatever text you want to print goes here"
Printer.EndDoc 'This EndDoc does do a FF.
End Sub

Laser Printer Margins:

The amount of margin (unprintable area) varies with different laser printers
and cartridges. One solution is to assume a larger than .25" margin. (George

Another solution is to use the Printer.ScaleWidth and Printer.ScaleHeight
properties instead of Printer.Width & Printer.Height for calculating printable
area. (Bryon Scott)

But be careful with these two properties, especially if you're setting
Printer.CurrentX and CurrentY. If you change these figures, you revert to the
User setting for ScaleMode, and that can throw your printing off. It's OK,
however, to read these properties. (George Campbell)

Sample code (Don Funk):
Dim PageNo As String
Header$ = "Yess"
Printer.CurrentX = 0
Printer.Print Date$;
Printer.CurrentX = (Printer.ScaleWidth - Printer.TextWidth(Header$))
Printer.CurrentX = Printer.CurrentX / 2
Printer.Print Header$;
PageNo = "Page: " + Format$(12, "###")
Printer.CurrentX = Printer.ScaleWidth - Printer.TextWidth(PageNo)
Printer.Print PageNo
Printer.Print " "

LaserJet Driver Update:

The latest version of the LJ III printer driver is 3.86. There were a bunch of
bugs fixed in this version. It is available over in the HPPER forum LIBS on

LaserJet Lost Line Fixed:

Using the File menu Print command to print source code will truncate one line of
code per page of output when printing to a Hewlett-Packard (HP) LaserJet series
III printer using the HPPCL5A.DRV printer driver. This is a problem with the
Hewlett-Packard LaserJet series III printer driver version 3.42 for Windows.

Microsoft has confirmed this to be a problem with the HPPCL5A printer driver
version 3.42.

This problem was corrected by the HP III driver version 30.3.85 included with
Microsoft Word for Windows version 2.0.

LaserJet Offset:

VB uses the whole page for figuring offsets, but the HP LaserJet driver, or
the printer itself, uses the printable area, which is about .25 less on each
side of the paper. You must allow for this when trying to place printing
precisely on the page with a LaserJet printer.

Matching Printer & Screen Fonts:

Use an On Error Resume Next line before sending the printer font name stuff to
the screen. If you try to display a printer font for which there's no screen
equivalent, you'll get an Invalid Property Value error. If, however, you use
the On Error line to ignore the error, the Windows GDI will attempt it's very
best to display your font. It actually does a pretty good job, although it
won't be an accurate representation.

Same thing applies when setting FontSize, etc. for the printer. If you bypass
the setting with the On Error line, the printer will go ahead and print, but in
the font's default size. It's sloppy programming, but it works. Just be sure
to reset your error handling after doing this, or your program will crash
without a doubt, and you won't be able to spot your other errors.

With ATM, if a font family doesn't have a separate BOLD, ITALIC, or BOLD
ITALIC member, you can still print bold, italic and bold italic characters. I
believe it's the Windows GDI which takes care of that job, but I'm not
certain. The quality won't be as good as if you actually had the outlines
available, but it's not bad either. You should see Cooper Black in this fake
Boldfacing. (George Campbell)

Not Printing?:

If your print routines never make it to the printer, make sure you have done
the following:

1. Make sure you send the Printer.EndDoc property at the end of the print job.
2. If you are in a long print job, add "x = DoEvents ()" to allow the Windows
Print Manager to get control.
3. If you're using a PostScript printer, the Printer.EndDoc isn't enough.
You need two lines:
Without that, you'll put your PS printer into a lock. Don't put
anything between the two commands, or you'll get a blank page.

Number of Copies, Setting:

The following routine lets you set the number of copies to be printed:

Declare Function Escape% Lib "GDI" (ByVal hDC%,ByVal nEscape%,
ByVal nCount%, lplnData As Any,
lpOutData As Any)
Global Const SETCOPYCOUNT = 17

Sub Command1_Click ()
Printer.Print ""
NumCopies% = 3 'an example to print 3 copies
ActualCopies% = 3
X% = Escape%(Printer.hDC, SETCOPYCOUNT, Len(i%), NumCopies%, ActualCopies%)
Printer.Print Text1.Text
End Sub

Printer Setup:

Declare Function LoadLibrary Lib "kernel" (Byval LibName$) As Integer
Declare Function FreeLibrary Lib "kernel" (hLib%) As Integer
Declare Function DeviceMode Lib "HPPCL.DRV" (hWnd%, hLib%, Dev$, Port$)

SUB Command1_Click ()
hLib% = LoadLib% ("HPPCL.DRV")
Result% = DeviceMode (hWnd, hLib%,"HP / PCL LaserJet","LPT1:")
Result% = FreeLibrary (hLib%)

Declare Function LoadLibrary Lib "KERNEL" (ByVal lpLibFileName$) As Integer
Declare Sub FreeLibrary Lib "KERNEL" (ByVal hLibModule%)

Then change PSetupMNU_Click to read:

Sub PSetupMNU_Click ()
RetStr$ = String$(256, 0)
RetStrSize% = Len(RetStr$)
x% = GetProfileString("windows", "device", "", RetStr$, RetStrSize%)

i% = InStr(RetStr$, ",")
If i% > 0 Then
a$ = Left$(RetStr$, i% - 1)
b$ = Right$(RetStr$, Len(RetStr$) - i%)
j% = InStr(b$, ",")
If j% > 0 Then
DriverName$ = Left$(b$, j% - 1)
PortName$ = Right$(b$, Len(b$) - j%)
End If
End If

If Len(DriverName$) > 0 And Len(PortName$) > 0 Then
LibHandle% = LoadLibrary("PSETUP.DLL")
If LibHandle% >= 32 Then
r% = DoPrinterSetup(Form1.hwnd, DriverName$, PortName$)
FreeLibrary LibHandle%
If Not r% Then MsgBox "Can't run Printer Setup", 64, "Printer Setup"
End If
MsgBox "No default printer selected", 64, "Printer Setup"
End If
End Sub

Printer Control Codes:

The normal Print command in VB will not properly send printer control codes
through to the printer. Here is how to get around VB:

Open "LPT1" For Binary As #1 'note: no colon on LPT1
Put #1,,
Close #1

Printer Wordwrap:

The following shows how to print text to a printer/form using the line
wrapping built into a Text box. By calling the API calls, you can printout
individual lines of text to the form/printer. You only have to add the
CurrentX/CurrentY to control the location of the print out. This example will
need one Text box and one Command button. Make sure that you set the
CurrentX/Y to some location on the form that isn't being covered up by the
Text box or Command control.

Declare Function GetFocus% Lib "user" ()
Declare Function SendMessage% Lib "user" (ByVal hWnd%, ByVal wMsg%,
ByVal wParam%, ByVal lParam As Any)

Sub Command1_Click ()
Dim i As Long
CurrentY = 1000
NumberOfLines = GetLineCount(Text1)
For i = 0 To NumberOfLines
Form1.CurrentX = 1000
Print LineOftext$(Text1, i)
Next i
End Sub

Function LineOftext$ (TextBox As Control, LineNumber As Long)
'* This function copies a line of text specified by LineNumber
'* from the edit control. The first line starts at zero.
Const EM_GETLINE = &H414
Buffer$ = Space$(MAX_CHAR_PER_LINE)
API_Error% = SendMessage(GetFocus(), EM_GETLINE, LineNumber, Buffer$)
LineOftext$ = Buffer$
End Function

Function GetLineCount& (TextBox As Control)
'* This function will return the number of lines in the edit
'* control.
Pos% = SendMessage(GetFocus(), EM_GETLINECOUNT, 0&, 0&)
aGetLineCount.Caption = "GetLineCount = " + Str$(Pos%)
fGetLineCount = Pos%
End Function

Printing Forms:

In VB's File menu, there is a Print option for printing forms and code. On an
HPLJ, the top of forms will be cut off because VB/Windows tries to print in
the top-most part of the page, which is, of course, unusable on an HPLJ.

MS acknowledges the problem but has no solution other than printing to disk
and then using another program to print the disk file. Also see LaserJet Lost
Line Fixed, above.

Printing Problems:

Fm: George Campbell

1. The StretchBlt and BitBlt tools don't work on Postscript printers.

2. Also, the same tools fail to work with ICO or WMF files. The images appear
on the screen, but will not print.

ScaleLeft Errors:

You have to be careful with ScaleLeft and the other similar properties. If you
set them at runtime, your ScaleMode reverts to the User setting. What this means
is that setting any of those properties destroys your original ScaleMode

The documentation on this subject is a disaster. I spent almost an entire day
fooling with those properties, only to give up trying to use them for setting
margins on the Printer object.

Probably the best option is to treat ScaleLeft, etc. as read-only at runtime.
(George Campbell)

TextWidth Improvement:

The following function beats the TextWidth method all to heck. It returns the
width to 1/1000th of a point with complete accuracy, is device independent,
accepts very long strings, doesn't initialize the Print Manager, and doesn't
send a header to the printer. (Walt Wooton)

Declare Function GetTextExtent Lib "GDI" (ByVal hDC%,
ByVal lpString$,
ByVal nCount%) As Long

Function WidthOfText (text$) As Single
r% = GetDeviceCaps%(Printer.hDC, 52)
s% = Printer.FontSize
Printer.FontSize = 1000
Maxchars& = Int(65535 / (r% / 72) / 1000)
length% = Len(text$)
j% = Abs(Int(length% / Maxchars&))
For k% = 0 To j%
textpart$ = Mid$(text$, 1 + (Maxchars& * k%), Maxchars&)
i& = GetTextExtent(Printer.hDC, textpart$, Len(textpart$))
a& = 65535 And i&
b& = b& + a&
Next k%
Printer.FontSize = s%
WidthOfText = CInt(b& * (72 / r%)) * .001 * s%
End Function

Background for VB:

Many people have complained about VB not having a background and that seeing
the Program Mangaer and/or wallpaper in the background is distracting.

One work-around would be to compile a simple VB app with a form that fills the
screen up to the space at the top where the VB menu bar appears. Have this app
shell to VB to get it going.

An alternative to having a full-screen form would be to have an app that
minimizes the Program Manager and uses the routine in this file to load a
blank wallpaper file (a BMP which you can easily create with Paintbrush) and
then shell to VB.

Color Const Declarations:

The following are VB Global Const definitions for the 48 default colors on the
palette. The constant definitions correspond to the color palette when viewing
the palette from top-to-bottom and left-to-right. (Steve Schmidt)

Global Const Black0 = &HFFFFFF
Global Const Black2 = &HC0C0C0
Global Const Black3 = &H808080
Global Const Black1 = &HE0E0E0
Global Const Black4 = &H404040
Global Const Black5 = &H0&
Global Const Red0 = &HC0C0FF
Global Const Red1 = &H8080FF
Global Const Red2 = &HFF&
Global Const Orange0 = &HC0E0FF
Global Const Orange1 = &H80C0FF
Global Const Orange2 = &H80FF&
Global Const Yellow0 = &HE0FFFF
Global Const Yellow1 = &H80FFFF
Global Const Yellow2 = &HFFFF&
Global Const Green0 = &HC0FFC0
Global Const Green1 = &H80FF80
Global Const Green2 = &HFF00&
Global Const Cyan0 = &HFFFFC0
Global Const Cyan1 = &HFFFF80
Global Const Cyan2 = &HFFFF00
Global Const Blue0 = &HFFC0C0
Global Const Blue1 = &HFF8080
Global Const Blue2 = &HFF0000
Global Const Magenta0 = &HFFC0FF
Global Const Magenta1 = &HFF80FF
Global Const Magenta2 = &HFF00FF
Global Const Red3 = &HC0&
Global Const Red4 = &H80&
Global Const Red5 = &H40&
Global Const Orange3 = &H40C0&
Global Const Orange4 = &H4080&
Global Const Orange5 = &H404080
Global Const Yellow3 = &HC0C0&
Global Const Yellow4 = &H8080&
Global Const Yellow5 = &H4040&
Global Const Green3 = &HC000&
Global Const Green4 = &H8000&
Global Const Green5 = &H4000&
Global Const Cyan3 = &HC0C000
Global Const Cyan4 = &H808000
Global Const Cyan5 = &H404000
Global Const Blue3 = &HC00000
Global Const Blue4 = &H800000
Global Const Blue5 = &H400000
Global Const Magenta3 = &HC000C0
Global Const Magenta4 = &H800080
Global Const Magenta5 = &H400040

Displaying "ASCII Graphics":

In February 1992's Inside Visual Basic, we printed some code that let's you
output text using the OEM character set (aka extended ASCII characters):
(subliminally: subscribe, subscribe)

DefInt A-Z
Declare Function GetStockObject Lib "GDI" (ByVal nIndex)
Declare Function SelectObject Lib "GDI" (ByVal hDC, ByVal hObject)

SaveScaleMode = Form1.ScaleMode
Form1.ScaleMode = 3 'Set to Pixel

nIndex = 10 'OEM-dependent fixed font
hObject = GetStockObject(nIndex)

SavedObject = SelectObject(Form1.hDC, hObject)
For i = 0 to 4
For j = 1 to 51
Print Chr$(j*51+i);
Form1.ScaleMode = SavedScaleMode

I put these instructions into Form1's Click event procedure for testing. If you
want to use them elsewhere, you'll need the hDC% for that item.
(Blake Ragsdell, Inside Visual Basic)

Framing Controls (the NEXT Look):

The following routine adds shading around your controls that gives the look of
depth to them. Put this routine in a Module and you can call it from any Form.

Note that this routine puts the shadow on the top and left edges of the
"recessed box". This is done to match the lighting pattern on the VB buttons.
Also, the bottom and right edges are drawn first so that the dark edges will
be drawn over the light edges at the corners to create a natural shadow

DefInt A-Z
Sub Frame (C As Control)
Offset = 20 'try experimenting with this value and the next
Screen.ActiveControl.DrawWidth = 3
Screen.ActiveControl.forecolor = &HE0E0E0 'you may have to try different color combo's
Screen.ActiveControl.Line (C.Left, C.Top + C.Height + Offset)
-(C.Left + C.Width, C.Top + C.Height + Offset)
Screen.ActiveControl.Line (C.Left + C.Width + Offset, C.Top)
-(C.Left +C.Width +Offset, C.Top +C.Height +Offset)
Screen.ActiveControl.forecolor = &H808080
Screen.ActiveControl.Line (C.Left - Offset * 1.5, C.Top - Offset * 1.5)
-(C.Left +C.Width +Offset *1.5, C.Top -Offset *1.5)
Screen.ActiveControl.Line (C.Left - Offset * 1.5, C.Top - Offset * 1.5)
-(C.Left - Offset * 1.5, C.Top + C.Height + Offset)
End Sub

(In Form1, for example:)
Sub Form_Paint
Call Frame(Label1)
Call Frame(Text1)
Call Frame(List1)
Call Frame(Drive1)
Call Frame(Dir1)
Call Frame(File1)
End Sub

Bonus tip: If you have a row of buttons, put them inside a Picture box. (You
must draw the button inside the box. You cannot double-click or drag the
button into the Picture box.)

Set the Picture box's BackColor to the same as the Forms and the Border to
none. Now use the routine above to put a frame around the Picture box to make
it look like the buttons are in a recessed panel.

Screen/Window Capture:

PrtSc captures the whole Windows 3 screen to the clipboard. Alt-PrtSc captures
the contents of the active window to the clipboard.

For better screen grey scales, load the monochrome display driver first.
(Jonathan Zuck)

The shareware program Paint Shop Pro is an easy way to save captured screens
to disk in a wide variety of graphics formats. From PsL, order disk #3388.

Screen Scaling/Form Resizing:

You're on your own in terms of rescaling your forms and controls for different
resolution screens. Your best bet is to either design exclusively for 640x480
screens (though there are people with 640x350 EGA screens), or to have your
program use the Screen.ScaleWidth/ScaleHeight values and adjust your forms
appropriately by using percentages of the screen for sizing instead of an
absolute number of Twips.

All of the VB scale modes use that wonderful and totally baffling item, the
"logical inch." A logical inch is defined as an inch as written on paper and not
on the screen. Thus, all VB scale modes are relative to how big it prints and
not the video resolution of the screen. As such, twips are not "Physical" units
of measure with video output, but relative units. This is why forms change
relative size as you change video resolution.

As an example, a line of text which is 1" long when PRINTED is 72 points or 1440
twips. In std vga mode, the screen width is 7680 twips while in 1024x768 hi-res,
the screen width is 12,288 twips (Assuming you use the same system fonts). In
VGA, this 1" line of text takes 19% of the screen width to display and in 1024,
it only takes 12% of the screen width. Thus, this line of text will physically
change relative size between video resolutions while still being 1" long when

More Input:

The physical size of the monitor isn't what's important. It's the actual
pixel resolution of the monitor that determines how a form or control displays
on the monitor. I've got a 5D at work and a 4D at home. When in the same
resolution, a form occupies the same relative area of the screen on both. It's
when I change resolution from 1024 to 640 that big changes occur.
As for twips, they are perfectly consistent -- but on the printer and not
the screen. Any object 1440 twips wide will print exactly 1" wide. How wide
1440 twips is on the screen changes with video resolution.


Think in percentages, rather than in terms of absolute measurements. No matter
what your screen resolution, you can simply make each control's position and
size a percentage of Screen.Width and Height.

Form1.Left = Screen.Width * .25
Form1.Top = Screen.Height * .1
Form1.Width = Screen.Width * .5
Form1.Height = Screen.Height *.5

That's the basic structure of the thing. Font scaling and other items can also
use the same concept, and your app will look the same no matter what
resolution your user chooses. (George Campbell)

Screen Scaling Tips:

Here are two pointers if you want to be able to Resize your forms:

One: Group your controls onto frames or pictures. It is vastly simpler to hide,
show and move things around if they're in a common control like a frame or
picture box.

When I resize a form, I have routines to move the two or three 'container'
controls; I then call separate subs to resize/reposition the CONTENTS of each of
those controls if required. It sounds like more code, but it actually ends up
taking less code and being faster. You do pay a slight memory cost with the
added control, but the speed increase is worth it.

(2) To avoid recursion, use a flag in the Resize routine:

Sub Form_Resize
If AlreadySizing Then Exit Sub
AlreadySizing = -1
'Code to move controls, etc. goes here.
AlreadySizing = 0
Exit Sub

Set the AlreadySizing flag anytime you want to make adjustments to the form size
without calling the control-moving routines. (Barry Seymour)

Screen Type:

z = Screen.Height
If z = 6000 Then
ElseIf z = 7000 Then
Type$ = "EGA"
ElseIf z > 7000 Then
Type$ = "VGA or Better"
End if

There's another way to do this, calling GetDeviceCaps to find out the vertical
resolution; but the above method is a lot easier... BTW, if you want to know
if it is exactly VGA, not "or better" (i.e., better than 640x480), the number
for that 7200 if memory serves...

Screen Wipes:

You can "draw a curtain" over the screen as follows: (Don Funk)
CONST RESOLUTION = 768 '1024 X 768 in this example
For i = 1 to RESOLUTION
LINE (i,1)-(i,1024)
Next i

Stop Flashing During Updates:

Set an object's Visible property to 0 during updates to speed up the process
and to stop the screen from flashing caused by each update being written to
the screen. (Set it back to -1 when the update is done, of course.)

Suppress Arrow Cursor:

The problem with creating a custom mouse pointer is that VB constantly resets
it to the arrow shape. The following routine overcomes that problem. (Gregg

DefInt A-Z
Declare Function ShowCursor Lib "User" (ByVal bShow)
Declare Function SetCursor Lib "User" (ByVal hCursor)
Declare Function LoadCursor Lib "User" (ByVal hInstance,
ByVal lpCursorName As Any)
Declare Function GetClassWord Lib "User" (ByVal hWnd, ByVal nIndex)
Declare Function SetClassWord Lib "User" (ByVal hWnd, ByVal nIndex,
ByVal nNewWord)
Global Const IDC_CROSS = 32515&
Global Const IDC_WAIT = 32514&
Global Const IDC_ICON = 32641&
Global Const GCW_HCURSOR = (-12)
Global Const IDC_ARROW = 32512&

Sub Text1_LostFocus ()
hCursor = LoadCursor(0&, IDC_ICON)
h1 = SetCursor(hCursor)
h2 = ShowCursor(1)
oldcur = GetClassWord(Form1.hWnd, GCW_HCURSOR)
newcur = SetClassWord(Form1.hWnd, GCW_HCURSOR, hCursor)
End Sub

Sub Text2_LostFocus ()
hCursor1 = LoadCursor(0&, IDC_ARROW)
h2 = SetCursor(hCursor)
h2 = ShowCursor(1)
oldcur1 = GetClassWord(Form1.hWnd, GCW_HCURSOR)
newcur1 = SetClassWord(Form1.hWnd, GCW_HCURSOR, hCursor1)
End Sub

The limitation of this approach is that when you set the class cursor you are
changing the cursors for every window in that class. Thus changing the cursor
for one text box promptly changes it for every text box. (Daniel Appleman)

Twips to Pixels:

Here is a routine for converting twips to pixel.

Form1 Declarations:
Const PIXEL = 3
Const TWIP = 1

Sub Form_Click ()
Print "Form Left = "; Twips_To_Pixel(Left)
End Sub

Function Twips_To_Pixel& (ByVal N&)
ScaleMode = TWIP
Scale_Twip = ScaleWidth
ScaleMode = PIXEL
Scale_Pixel = ScaleWidth
Twips_To_Pixel = Scale_Pixel / Scale_Twip * Left
End Function

Adding to Control Box Menu:

You can add items to the Control Box menu, but you can't attach any code to
it, so it is really useless and not advised. - Don Funk, MS

Control Box Exit Bug:

If you exit a compiled VB app by using Exit from the form's Control Box menu,
the program may not close down properly and may not free up system resources.
(Bob Meyer)

One of the resources not freed up is VBRUN100.DLL, which Windows does not remove
from memory. If you run a VB app from drive A: with VBRUN on A: as well, then
use the Control Box exit option to quit rather than the app's exit routine, then
the next VB app you run will insist on VBRUN being in drive A:. (Keith Funk)

Control Accessing Via DDE:

A control on a frame cannot be accessed via DDE. I had tested this with three
applications, WinWord, Superbase and Objectvision. The latter two produce an
immediate, null result to a DDE request; WinWord seems to recognize the
control is there and keeps trying to get the data until it eventually times
out. (George Mair)

Detecting Running Applications:

1. Shelled App Still Running?
See "Shelling-To-Dos Problems". The GetModuleUsage can also be used
for Windows apps, per Jonathan Zuck.

2. To determine if an application is already running, ask Windows to find its
title caption. Use this code:

DefInt A-Z
Declare Function FindWindow Lib "user" (ByVal CName As Any,
[all on one line]---> ByVal Caption As Any)

Function Loaded (Caption$)
Loaded = FindWindow(0&, Caption$) 'use call
If Loaded > 0 Then Loaded = -1 'if caption found
End Function

If NOT Loaded("Program Manager") Then
dummy = Shell("PROGMAN.EXE",1)
End If

3. Is application already running?

Declare Function GetModuleHandle% Lib "Kernel" (ByVal lpProgramName$)
Declare Function GetModuleUsage% Lib "Kernel" (ByVal hProgram%)

Sub Form_Load ()
hw% = GetModuleHandle("project.EXE")
If GetModuleUsage(hw%) > 1 Then
MsgBox "This program is already loaded!", 16
End If
End Sub

Detecting the Start-Up Directory:

DefInt A-Z
Declare Function GetWindowsDirectory Lib "kernel" (ByVal P$, ByVal S)
Declare Function GetModuleHandle Lib "kernel" (ByVal FileName$)
Declare Function GetModuleFileName Lib "kernel" (ByVal hModule,
ByVal FilName$, ByVal nSize)
Dim hMod, path As String

hMod = GetModuleHandle("MyApp.EXE")
path = String$(145, Chr$(0))
path = Left$(path, GetModuleFileName(hMod, path, Len(path)))
VbPos = InStr(path, "\MyApp.EXE")
If VbPos = 0 Then
Dim WinPath As String
WinPath = String$(145, Chr$(0))
Response = MsgBox("MyApp.EXE is Missing or has been renamed", 48, "Will
use default path")
Label1.Caption = Left$(WinPath, GetWindowsDirectory(WinPath,
Len(WinPath))) + "\MyApp"
Label1.Caption = Left$(path, (VbPos% - 1))
End If
(Arthur Edstrom)

Faster Loading of Apps:

VB apps will load faster if you load VBRUN100.DLL during Windows startup.

You don't even need a form. You can write a program that has nothing but a Sub
Main. In that, you will want to call the LoadLibrary API function to load
VBRUN100.DLL. This increases the lock count on the DLL so that even when you
exit from your app, the DLL remains loaded. This way, your EXE does not need to
remain in memory.

DefInt A-Z
Declare Sub LoadLibrary Lib "Kernel" (ByVal LibName$)

Sub Main ()
LoadLibrary "VBRUN100.DLL"
End Sub

That's all there is to it. (Jonathan Zuck)

File Creation Delay:

If you create a file, eg: for temporary use, and immediately try to use it, it
may not show in the directory.

This problem is due to Windows' handling of file buffers. A timer that checked
for the file's availability using an error trap is one way to overcome this
problem. -Robert Eineigl

File Handles - Maximum:

Windows Allows a maximum of 255 file handles system wide. It does not matter
what you have specified in the Config.sys "FILES=".

The "SetHandleCount" SDK function is the correct way to increase an app's
default number of 20.

Windows allocates a default of 20 to any task, or app spawned by it (Windows
3.0 is a single task with multiple app's). A task (generally a DOS task) is
time sliced in 386 enhanced mode according to the parameters set for forground
and background execution.

To increase the "TASK" default number set the "PerVMFiles= " parameter in your
SYSTEM.INI file. Be aware that the 255 available handles are shared by all
tasks and apps. When you grab a set of handles, they are removed from the
available pool. It would be a good idea to set the handle count back to the
default after you are through with them.

Remember, the return value from the SetHandleCount function is the number
actually allocated. Also, Windows itself and Visual Basic may have 4 or 5
files open (like win.ini or GLOBAL.BAS). (Tony Altwies)

Finding Excel:

The parameters that acknowledge Excel's presence are:

classname$ = "XLMAIN" + chr$(0)
mywinname$ = "Microsoft Excel"+chr$(0)


The parameters that will work for finding a specific excel window are still
not settled, but something like:

classname$ = "EXCEL5" + chr$(0)
mywinname$ = "DDE1.XLS" + chr$(0)

Linking to Excel:

Sharon Dooley 70740,2330

This fragment shows what I've done to send stuff to Excel, let the user play
around, and then come back to my program.

DefInt A-Z
Declare Function GetActiveWindow Lib "User" ()
Declare Function ShowWindow Lib "user" (ByVal hWnd, ByVal nCmdShow)
Declare Function PutFocus Lib "user" Alias "SetFocus" (ByVal hWnd)
Declare Function IsWindow Lib "User" (ByVal hWnd)

ThisApp = StartApp(ToExcel.ExcelBox, "Excel", CurrPath +
"expenses.xls", "excel c:\timelog\expenses.xls", 2, hActive)
BuildLink ToExcel.ExcelBox, ThisApp, CurrPath + "expenses.xls"
'.... here I send my data to Excel '
DestroyLink ToExcel.ExcelBox ' transfer control to Excel and wait till
' the user exits
Dummy = ShowWindow(hActive, SW_SHOWNORMAL) ' and restore its size
Dummy = PutFocus(hActive) ' give it the system focus
Do While IsWindow(hActive)
x% = DoEvents()
Loop ' when user exits Excel, control returns here

Function StartApp (Link As Control, AppName As String, Topic as String,
CommandLine As String, Style As Integer,
Hwnd As Integer) As String
On Error GoTo StartUp
Link.LinkMode = NONE
Link.LinkTopic = AppName + "|" + Topic
Link.LinkMode = COLD
Exit Function
If Err = DDE_NO_APP Then
T% = Shell(CommandLine, Style)
StartApp = "Excel" + LTrim$(Str$(T%))
Hwnd = GetActiveWindow()
MsgBox "UnKnown Error " + Str$(Err) + " in StartApp", \
MB_ICONSTOP, "Timelog Error"
Status = VBPXExit()
End If
End Function

Get Program's Path & EXE Name:

The following routine will get the full path and filename for your program.

DefInt A-Z
Declare Function GetClassWord Lib "user" (ByVal HWnd, ByVal nIndex)
Declare Function GetModuleFileName Lib "kernel" (ByVal HWnd, ByVal Fi as String,
ByVal FiLen)
Sub Form_Click ()
Const GCW_HMODULE = (-16)
Filename$ = Space$(127)
hModule = GetClassWord(Form1.hWnd, GCW_HMODULE) 'substitute the form name
FLength = GetModuleFileName(hModule, Filename$, 127)
Print Left$(Filename$, FLength)
End Sub

Getting Windows Version Number:

The GetVersion API call will report "3.00" for both versions 3.00 and 3.00a
since the minor version is still 00 for both. There is no way to detect the
"a". Windows 3.10 should report in hex "A03".

Get System Focus:

VB provides no way of detecting when your app has been given the system focus.
There are a number of Windows messages that the form does not translate into
events for you.

For example, you have no way to prevent an iconized program form being opened
but a C programmer does. Also, you have no way of knowing if a form was moved
but again, this is a message sent to a form. (Jonathan Zuck)

Here is a routine for detecting when an app gets/loses system focus (Don Funk):

DefInt A-Z
Declare Function GetActiveWindow Lib "User" ()
Global Focus

Sub Timer1_Timer ()
If Focus Then
If GetActiveWindow() <> Form1.hWND Then
Print "Lost Focus"
Focus = 0
End If
If GetActiveWindow() = Form1.hWND Then
Print "Got Focus"
Focus = -1
End If
End If
End Sub

Sub Form_Load ()
Focus = -1
Timer1.Interval = 10
End Sub

hWnd for a Control:

All you have to do is use the Windows API GetFocus call after setting the
focus to the desired control:

General Section
Declare Function GetFocus% Lib "User" ()

In your code
ButtonHwnd = GetFocus()

hWnd for a Control II:

Fm: Costas Kitsos 73667,1755

Here's yet another way of getting a Control's hWnd without having to use that
ugly SetFocus method . It will also get hWnds for Labels. If you use
ClipSib to create overlapping controls, this will not work.

Declare Function ChildWindowFromPoint Lib "User" (ByVal hWnd As Integer,
ByVal dwPoint As Long) As Integer

Function GetHwndCtl (FrmhWnd As Integer, Ctl As Control) As Integer
OldMode = Scalemode
Scalemode = 3
GetHwndCtl = ChildWindowFromPoint(FrmhWnd, ( * &H10000) + Ctl.left)
Scalemode = OldMode
End Function

.INI Files:

The calls to read/write private ini file are:

GetPrivateProfileInt - returns int value
WORD GetPrivateProfileInt( LPSTR lpApplicationName,
LPSTR lpKeyName,
int nDefault,
LPSTR lpFileName )

GetPrivateProfileString - returns string

The calls to read/write the WIN.INI file are almost the same -- just remove
the Private.

x = GetPrivateProfileInt(APP_NAME, KEY_FrogSpit, DefaultNum%, TEST.INI)

If the TEST.INI file has a key "FrogSpit", then "x" will be either 0 or -1. If
the file doesn't yet have a "FrogSpit" key, then the answer will be DefaultNum%.

As for the result, 0 if value correspponding to keyname is not int or if int is
negative. Otherwise, returns the value after keyname. Check SDK Windows Prog Ref
pp. 4-198-199

Strings passed back from a PrivateProfile and stripped of trailing spaces by
RTrim$ still have a null character (it tests out to ANSI 000, BTW) stuck on
the end.

Mode - Which You Are In:

In Windows, click on Help and then "About Program Manager". Box will tell you
what mode you're running in.

Multi-Instance App Prevention:

1. Straightforward method:

DefInt A-Z
Declare Function GetActiveWindow Lib "user" ()
Declare Function IsWindow Lib "user" (ByVal hWnd)
Declare Function FindWindow Lib "user" (ByVal CName As Any,
ByVal Caption As Any)

Wnd = Shell("printman.exe", 1) 'run other app
Wnd = GetActiveWindow() 'immediatly do this

'See if printman is still running:
While IsWindow(Wnd) 'API call to determine is Wnd
dummy = DoEvents() ' is valid; then use doevents()
Wend ' to run other tasks

'To determine if an application is already running ask windows to find its
'title caption. Use this code...

Function Loaded (Caption$)
Loaded = FindWindow(0&, Caption$) 'use call
If Loaded > False Then Loaded = True 'if caption found
End Function

If NOT Loaded("Program Manager") Then dummy = Shell("PROGMAN.EXE",1)

2. This example is more specific to someone's application:

Dim PassString$ ' declare a local string to hold user's password

Declare Function GetActiveWindow Lib "User" () As Integer
Declare Function ShowWindow Lib "user" (ByVal hWnd As Integer,
ByVal nCmdShow As Integer) As Integer
Declare Function SetFocusAPI Lib "user" Alias "SetFocus"
(ByVal hWnd As Integer) As Integer

Form_Load() ' this executes when the PassWord form loads
On Error GoTo NoLink ' setup our no-link error handler
SystemLink.LinkMode = 1 ' try to establish a hot link with a server
On Error GoTo 0 ' disable error trapping


AppActivate "SalHist" ' activate the "other" SalHist
hActive = GetActiveWindow() ' pickup it's hWnd handle
Dummy = SetFocusAPI(hActive) ' give it the system focus
Dummy = ShowWindow(hActive, SW_SHOWNORMAL) ' and restore its size
End ' finally, terminate this app!

On Error GoTo 0 ' disable our temporary error trapping
SystemLink.LinkMode = 0 ' abandon our local DDE connection attempt
LinkMode = 1 ' and establish ourselves as a DDE server...

CenterForm PassWord ' (My standard.lib form-centering routine)
PassString$ = ""
PassWord.Show 1 ' present the PassWord form for user data
' entry. The "1" makes it "modal" (stickier)
End Sub ' end of the Form_Load subroutine.

Networks & VB:

VBRUN100.DLL and VB apps (EXE's) should be placed on each machine's hard disk
to avoid significant performance degradation.

If you have VB on a network, you may find that it does not write its temporary
files to the place you would like them. The solution is to set an environment
variable to specify where the files should be written. Example:


Norton Desktop vs VB:

Several Norton Desktop users have reported problems (freezeups) occurring after
long sessions in Visual Basic.

Resource Limits:

You cannot just keep adding boxes and buttons at will. The limit on the number
of controls on a form is 255, but you'll likely have resource limitation
problems long before that.

An example of resource conservation in Windows in general is to get programs
out of Program Manager that you do not regularly use.

To conserve resources in a VB app, try using a Grid box instead of a bunch of
Text or List boxes to display a lot of information. You can also use Print to
put captions at specific points on a Form instead of using Label boxes.

Restart Windows:

Declare Function ExitWindows% Lib "User" (ByVal dwReserved&, ByVal wReturnCode%)
x% = ExitWindows(66, 66)
(Paul Smith)


In the Global module:
'type the declaration on one line
Declare Function Sendmessage Lib "user" (ByVal Hwnd%,
ByVal Msg%,
ByVal wParam%,
ByVal lparam As Any) As Long

Global Const wm_WinIniChange = &H1A

Then, for example:
Sub Command1_Click
x& = Sendmessage(&HFFFF, wm_WinIniChange, 0, ByVal "")
End Sub

Shell-to-DOS Problems:

When you Shell to DOS to execute a command or a DOS application, Windows tends
to want to iconize the Shell and continue with the rest of the program.

In the following example, you would get an error in trying to open MYFILE
because the COPY command has not been executed yet.

x = Shell(Environ$("COMSPEC") + " /c COPY A:MYFILE C:", 3)
Open "MYFILE" For Input as #1

In the next example, you would get a sharing violation and/or a string of DOS
icons across the screen:

For i = 1 to 10
x = Shell(Environ$("COMSPEC") + " /c dir c:\ >> c:\testshel", 3)

The following code has been suggested as a fix. It does not work for me in the
Standard Mode, but others have said it works for them.

Declare Function GetModuleUsage Lib "Kernel" (ByVal hModule%) As Integer

Sub Form_Load ()
For i% = 1 To 10
s% = Shell(Environ$("COMSPEC") + " /c dir c:\ >> c:\testshel", 1)
While GetModuleUsage(s%)
x% = DoEvents()
End Sub


Some machines will crash after shelling to DOS from Windows. It appears to
happen only when a math chip is in the system and Windows is in the Standard
mode -- and then not on ALL such systems.

At first we thought this was a VB problem, but Keith Funk, Costas Kitsos, Ted
Young and others have continued to dig into the problem and discovered that it
can be duplicated (on SOME systems) with any program that uses the math chip.
If you have a math chip, try the following:

1. Start Windows in Standard mode (ie: /S).
2. Run the Windows Calculator and minimize it to an icon.
3. Open and close a DOS window several times.

After three or four times, if your system is one of the unfortunate ones, you
should get an UAE. In Visual Basic, the problem manifests itself in this way:

In Form_Load, put the following lines and run it:
For i = 1 to 5
x = (Environ$("COMSPEC") + " /c dir c:\", 1)
y = DoEvents()
You might get a spurious "Illegal function call" on the DoEvents line rather
than a UAE, but if you keep going, you will get the UAE.

NOTE: Microsoft has acknowledged that this is a bug in Windows. It appears
that all we can do is wait for Windows 3.1. Again -- this problem is limited
to some machines with math chips that run Windows in Standard mode (normally,
just 286's).

Shell-to-DOS Syntax:

To shell to DOS, you could use the syntax:
x = ("c:\ /c ...
but this might not give you the right location of Instead, use
x = (Environ$("COMSPEC") + " /c ...
as in the previous section. - Ted Young

Shell Error Numbers:

The manual has an example that says:

If Shell(File1.FileName, 3 - Index) < 32 Then ...

The "32" is there because "Shell" always returns a value and any value equal
to or greater than 32 would indicate an error in trying to Shell.

VB App Cannot Be Windows Shell:

Like the title above says...

VBRUN100 Must Be on A:?:

See "Control Box Exit Bug", above.

Wallpaper Changing:

Declare Sub SetDeskWallPaper Lib "User" (byval FileName$)
Call SetDeskWallPaper(f$) 'where f$ is the name of a BMP file

will change the wallpaper in Windows, but it will not repaint the screen
(ie: make the new wallpaper visible) automatically. To accomplish that, you
must use the following:

Declare Sub ForceRedraw Lib "User" Alias "InvalidateRect"
(ByVal hWnd As Integer, \
lpRect As Long, >-all this on the line above in your app.
ByVal bErase As Integer) /

Call ForceRedraw(0, 0&, 0)

This still does not reset the Windows palette to match the Wallpaper's
palette, so if the Wallpaper's palette is different from the current Windows
palette, the colors will not look right. Also, this does not change WIN.INI, so
the next time you start Windows, the old wallpaper will reappear.

Why VB Apps Don't Cascade/Tile:

Forms in VB apps are actually pop-up forms. The VB parent form is invisible.
This is why functions such as Cascade/Tile in Task Manager will not affect VB

WIN.INI Warning:

If you use WriteProfileString while WIN.INI is also an open file in an app,
the data being written will end up in a lost chain. - Dennis Harrington

You can get rid of the lost chain by doing a CHKDSK /F, but don't forget that
if you do a CHKDSK /F in a Windows 3 DOS window, you will corrupt your hard
disk, so exit Windows first.

CtlName Access Via CDK:

You cannot reliably access the CtlName property via a VBGetControlProperty
function call of the CDK during Run Time. Use the "Tag" property instead.

Cursor (text), Position:

You can get the cursor position in a text box as follows:

CursPos = Text1.SelStart

Data Entry Masking:

General - Declarations:
Dim Txt as String
Text1_GotFocus ():
Text1.Text = "01/23"
Txt$ = Text1.Text 'Txt$ = "01/23". This intitialization keeps KeyUp
' from kicking in until a key has been pressed.
Text1_KeyPress: 'Assume cursor is on the "/" and "X" is pressed.
Txt$ = Text1.Text 'Note: Text1.Text is still "01/23" at this point.
If Txt$ <> Text1.Text Then 'now Text1.Text is "01X/23"
Call CursPos(Text1) 'and Txt$ is still "01/23"
End If
CursPos(Ctl as Control)
i = 0
Do While Len(Txt$) > i
i = i + 1
If Mid$(Txt$, i, 1) <> mid$(Ctl.Text, i, 1) Then
pos = i
Exit Do
End If
if pos = 3 then 'Example of preventing an unwanted change:
Ctl.Text = Txt$ 'Reset Text1.Text and
Ctl.SelStart = pos - 1 'put the cursor back where it was.
end if

Data Entry Routine:

.FRM Code:
Sub Form_Load ()
InitStdControl AcctNo1 'Initialize standard edit controls
InitStdControl AcctNo2
.. etc
End Sub

Sub AcctNo1_KeyPress (KeyAscii As Integer)
StdEdit AcctNo1, KeyAscii 'Use standard editing procedure
End Sub

Module (.BAS) Code:
DefInt A-Z
Declare Function GetFocus% Lib "USER" ()
Declare Function SendMessage& Lib "USER" (ByVal hWnd%, ByVal wMsg%
ByVal wParm%, ByVal lParm&)
Const WM_USER = &H400 'Define the message number for
Const EM_LIMITTEXT = WM_USER + 21 ' limiting the length of an edit
' Control
Sub InitStdControl (Ctrl As Control)
Ctrl.Width = 500 'Make field some standard length
Ctrl.SetFocus 'Allow maximum of 14 characters
Ok& = SendMessage(GetFocus(), EM_LIMITTEXT, 14, 0)
.. more setup
End Sub

Sub StdEdit (Ctrl As Control, KeyAscii%)
If KeyAscii >= 48 And KeyAscii <= 57 Then
Ctrl.SelLength = 1 'This produces overstrike mode
KeyAscii = 0 'Ignore non-numeric keys
End if
End Sub

Data Entry Text Limit:

Declare Function GetFocus% Lib "USER" ()
Declare Function SendMessage& Lib "USER" (ByVal hWnd%, ByVal wMsg%,
ByVal wParm%, Byval lParm&)
Const WM_USER = &H400

In your Form_Load procedure:
MaxLen% = (Whatever number you want)
txhWnd% = GetFocus()
Ok& = SendMessage(txhWnd%, EM_LIMITTEXT, MaxLen%, 0)

Fast Loading:

For fastest loading of a file into a text box, use the following:

OPEN File2Load$ For Binary as #1
If LOF(1) > 32768 Then
MsgBox "File is too big."
Buffer$ = Space$(LOF(1))
Get#1, 1, Buffer$
TextBox.Text = Buffer$
End If
Close 1

This routine involves only 1 disk read and two string operations. The old
WHILE...WEND loop required a disk read and two string operations for *EVERY*
line in the file. -Jack Presley



Concatenating text in a Text Box can cause screen flicker. Instead use
SelStart and SelLength - no flicker.

Flashing/Highlighting Text:

Sub FlashColor ()
If FlashState = True Then
Label1.BackColor = RGB(255, 0, 0)
Label1.ForeColor = RGB(255, 255, 255)
Label1.BackColor = RGB(255, 255, 255)
Label1.ForeColor = RGB(0, 0, 0)
End If
If FlashState = True Then
FlashState = False
FlashState = True
End If
End Sub

You can use the .SelLength and .SelStart to highlight and unhighlight a
section of the text.
Example of highlighting characters 11 - 15 in a Text box:
Text1.SelStart = 11
Text1.SelLength = 5

Increasing Line Limit:

On page 320 of the VB Programmer's Guide, it says the maximum length of a file
that can be edited with Text Editor is approx. 32k bytes because of the
"default" maximum number of characters you can assign to a VB multiline text box

However, using SendMessage with EM_Limittext and &HFFFF I've gotten about 63k
into one. (Robert Eineigl, Microsoft Product Support) [See WINAPI for syntax.]

Line Number, Finding:

The following code from Don Funk shows how to determine the current line
number in a text box from the cursor position (also see "Mouse Click
Detecting" following this tip):

CharPos& = fGetSel()
LineNumber& = fLineFromChar(CharPos&)

Function fLineFromChar& (CharPos&)
'* This function will return the line number of the line that
'* contains the character whose location(index) specified in the
'* third argument of SendMessage. If the third argument is -1,
'* then the number of the line that contains the first character
'* of the selected text is returned. Line numbers start at zero.
Const EM_LINEFROMCHAR = &H400 + 25
Pos% = SendMessage(GetFocus(), EM_LINEFROMCHAR, CharPos&, 0&)
aLineFromChar.Caption = "Current Line = " + Str$(Pos%)
fLineFromChar = Pos%
End Function

Function fGetLine$ (LineNumber As Long)
'* This function returns a line of text specified by LineNumber
'* from the edit control. The first line starts at zero.

Const EM_GETLINE = &H400 + 20

Buffer$ = Space$(MAX_CHAR_PER_LINE)
Pos% = SendMessage(GetFocus(), EM_GETLINE, LineNumber, Buffer$)
fGetLine$ = Buffer$
End Function

Function fGetSel& ()
'* This function returns the starting/ending position of the
'* current selected text. This is the current location of the
'* cursor if start is equal to ending.

'* LOWORD-start position of selected text
'* HIWORD-first no selected text

Const EM_GETSEL = &H400 + 0

location& = SendMessage(GetFocus(), EM_GETSEL, 0&, 0&)
ending% = location& \ 2 ^ 16
starting% = location& Xor high%
aGetSel.Caption = "Caret Location = " + Str$(starting%)
fGetSel = location&
End Function

Number of Lines:

The following routine from Don Funk returns the number of lines in a Text box:

Declare Function GetFocus% Lib "user" ()
Declare Function SendMessage% Lib "user" (ByVal hWnd%, ByVal wMsg%,
ByVal wParam%,
ByVal lParam As Any)
Function fGetLineCount& ()
Const EM_GETLINECOUNT = &H400 + 10
Pos% = SendMessage(GetFocus(), EM_GETLINECOUNT, 0&, 0&)
fGetLineCount = Pos%
End Function

Read-Only Text Box:

In a text box control, if you set Enabled = FALSE and MultiLine = TRUE, the
contents of the text box control are not greyed and the mousepointer stays an
arrow when over this text box control. Essentially, it's a read-only text box

This way I could make the text box control perform like a normal text box
control when I want the user to be able to enter info, while other times it
acts like a label when I don't want the user to be able to enter info.
(Rick J Andrews)

DoEvents () in a Timer:

If you put a DoEvents () in a Timer routine, you allow the Timer to kick in
again while you may still be in the Timer routine. To avoid this, use a flag to
indicate if you are already in the Timer routine and if so, to exit the Sub.

Using the Timer:

If you use a Timer with a small interval (eg: 1) and another application is
active, Windows may not be able to respond at the specified duration and
execution of the related code will be postponed until Windows can get to it.

What is Knowledge Base?:

Fm: Don Funk (SL) 76701,155

I work for Microsoft within Product Support. We receive a variety of questions
over the phone from many customers daily. We have at our disposal a data base
with a query engine that we can search on key words for a solution.

You have access to this data base also by typing "GO MSKB" [at a CompuServe
prompt]. This database not only includes "How to questions" but also includes
bug reports, press releases, and general questions and documentations about the

There are only about 100 articles for VB now but it is growing. The Quick
Basic includes many many many more articles since it has been around for a
long time. This Knowledge Base is the source of articles not only for Basic
but for all the products at Microsoft.

VB Book Reviews:

Rick Heil:

"Visual Basic Programming" by Douglas Hergert, Bantam:
Entry-level info for beginning programmers. Good review of fundamentals like

I/O techniques, data structures, file access, and all VB controls including
their properties & methods.

"Visual Basic" by Steve Holzner, Brady Books:
It assumes a moderate understanding of basic, so if you're totally new to
basic this probably shouldn't be your first book on the subject. The latter
half of the book goes deeper into topics like graphics. Good detail on
advanced data handling, sorting (with algorithms), error handling & DDE.

"Microsoft Windows Multimedia Programmer's Reference", available from
Waldenbooks (ISBN 1-55615-389-9).

VB Magazine Articles:

From Keith Pleas:

The following is a list of VB-related articles that have appeared to date:
1. Mixed-Language Windows Programming: Fortran as a DLL, Visual Basic for the
front end, Dr. Dobb's Journal, October 1991. John Norwood
2. Create Real Windows Applications in a Graphical Environment Using Microsoft
Visual Basic, Microsoft Systems Journal, July 1991. Laura Euler, Eric
Maffei, Adam Rauch

3. Upstream: Visual Basic, PC Techniques, June/July 1991. George Seaton.
4. They laughed when I sat at the keyboard, Computer Language, July 1991.
J.D. Hildebrand.
5. Sprint Through Windows Development with Visual Basic, Byte, August 1991.
Owen Linderholm.
6. Two Ways to Program BASIC for Windows, Byte, October 1991. Owen Linderholm.

How to Get the Files:

The following files are available on CompuServe on MSBASIC, DL's 1 and 5. The
files are also available on disk from PsL. The two sources are not identical
because PsL has screened out some duplication, old version, non-working files,
etc, and may have files that the authors have not uploaded to CIS.

If you are looking for one particular file and belong to CompuServe, the
cheapest and fastest way to get it is from MSBASIC's DL's. From any CIS prompt,
enter GO MSBASIC. From MSBASIC's Forum prompt, enter DL1 or DL5 and browse for
the file name with the BRO command. For example, to find CtlWhWnd, you could
enter BRO CTL*.* LIB:ALL to search all the DL's on MSBASIC.

The MSBASIC sysops have also posted an index file of all the programs on all
of MSBASIC's DL's. That list has the advantage of being more up to date than
the list that follows, but it offers less information, the VB and non-VB files
are mixed together, and the files are not categorized in any way. Still, it
can make it easier to find some of the files listed below.

To get a disk full of routines from PsL, use the disk number indicated. Note
that all categories with the same disk number are on the same disk. See the
"Background" in this file for ordering information. Disks are $5 each.

Please note that as new files are added to these disks by category each month,
disks may have to be split up to make room. If adding a disk for a specific
file, you must specify that when ordering so that we can verify that it is
still on the disk. If you specify multiple files and some are still on the disk,
we will send the requested disk.

The format for this section is to show the program name followed by the name
of the file on the PsL disk, the author/publisher of the file, and the
shareware registration fee, if any. Registration fees are meant to be sent
directly to the author to become licensed to use a program beyond a reasonable
evaluation period. Do not send those amounts to PsL. PsL charges a flat $5
disk fee.

DLL's, Utilities:
Disk #3490

User Friendly, Inc.
is a DLL function that returns the hWnd for any control. This makes it easier to
use API calls that modify controls.

is a DLL that allows you to play sound files through a PC's speaker. A sample
SND file and sample VB code are included. Since digitized sound files from
various sources (eg: Mac, Sound Blaster, Amiga) are all very similar, virtually
all of them can be played with DSound.

Obeda, Ed
is a DLL and related VB code that prevents users from opening more than one copy
of your application or of programs such as Calculator.

GetWindowList 1.1 (GETWLST)
Steinwart, Todd
is a DLL that lets you get a list of handles for active, visible windows. Sample
usage code is included.

Zuck, Jonathan
is designed to provide hotkey support to Visual Basic, which is not directly
supported in VB. You can define multiple hot keys in your application and the
DLL can be used by multiple applications simultaneously. An executable demo with
several examples is included.

Faggart, B.E.
is a DLL for I/O Port control. Sample VB source code is included.

LZSSLib 1.0
Eschalon Development Inc.
is a file compression/decompression DLL for Windows programmers. It uses the
LZSS algorithm. This should work with any language that supports DLL calling,
such as Turbo Pascal, C/C++, Actor, Visual Basic, Realizer, and ObjectVision.

White, Bill
is a sound driver (DLL) with VB source code to illustrate its use. TPW source
code for the DLL is included. (This DLL can be used with other Windows

Stsbar 2.0
Staffin, Ed
is a DLL for a status bar. This is a bar that is used to display program
parameter/function status, mouse coordinated, column/row numbers -- or any other
text you want to assign to it.

Donahue, Ray
lets you create nice looking toolbars and toolboxes with icons for each
selection. (Not on this disk. Ask for disk #8807.)

VBCards 1.01P
Sands, Richard R.
is a card-deck DLL for writers of card games for Windows. The author hopes to
create a public domain standard DLL so that each programmer does not have to
crowd your hard disk with a new card-deck DLL.

VBCards contains 52 cards, a Joker, seven card back designs, two indicator
cards, some support routines, and a short sample program. Although the sample
code is in Visual Basic, the DLL should be usable with other languages.

Accordian Solitaire (ACCRDVB)
Sands, Richard R.
is the VB source code for a solitaire card game. The uncompiled Help is also
included. Requires VBCards, which is also on this disk.

Zuck, Jonathan
is a DLL that adds pointer support to VB. LP2Str$ is a string function that
copies data, pointed to by a so-called "Long Pointer" into a VB proprietary
string variable. Str2LP is a subroutine that will copy data from one address to
another. With this function you can create the CV? and BLOAD commands.

Zuck, Jonathan
is an example for TPW programmers on how to create a string function in a DLL.
TP source code included.

Database, Data Entry, Sorting:
Disk #4062

Milligan, Keith
is a set of data entry control routines for VB: date entry, price/quantity,
currency, two and four decimal places, string to a specified length, uppercase
text input, and others.

Paradox Engine Demo (PDOXED)
Jaster, John
contains a DLL and source code for a simple demonstration of the Paradox engine.

Nech, Jim
provides a way to use the Paradox Engine to create Paradox tables in Visual
Basic. Source Code is included, and VBRUN is required.

is a set of forms and code for a dBase engine. A lot of coding has gone into it.
The sample application is about 85% complete; the engine is completely done.

VB-Paradox Engine Interface (VBPXENG)
Dooley, Sharon F.
is a set of declarations that create an interface to the Paradox Engine. It will
translate between VB and Paradox data types.

Ford, Nelson
is the code and a sample application for sorting both a List box and an array.
Testing shows that it takes 8 times longer to sort a List box.

is a DLL to allow using Btrieve in VB applications.

XQLM Declarations (XQLMVB)
are the VB declarations for working with Novell XQL.

NOTE: VXBASE, following, is not on the disk with the above. VXBASE comes on
two disks.

Comsoft Inc.
#7614/3946 [2 disks]
contains a DLL and sample files for creating dBASE compatible applications.
Features include up to eight active browse windows, large indexing buffer, memo
functions, multi-user options, and much more.

DataWorks 1.25 is on the second disk. It is an xBase power user's tool to be
used by developers building applications with VXBASE for Visual Basic.

DOS Access/Functions:
Disk #3557

DateTime (DATETIM)
shows how to get a file's date/time stamp and file size directly from within
Visual Basic without resorting to a shell to DOS DIR command.

Wykes, Harry JF
shows how to create a Windows style file-open box using List boxes rather than
the VB DriveBox and DirBox objects.

is a DLL that allows you to get free disk space, file information, and change
file attributes. Sample code is included.

Font Utilities:
Disk #3557

EnumFonts (ENUMFNT)
Telelink Systems
is a DLL which allows you to access the Windows API service EnumFonts. A demo is
provided which shows how to find which typefaces and fonts are supported by the
default printer. Source code is included.

FontView 2.0 (FONTVW)
Snider, Charles
is code for an application to find, display, and print Windows fonts.

Disk #3557

Lamson, George
is the source code and executable for a program to dial the phone.

ComDemo 1.0
illustrates the use of several COMM API functions. Includes source code and an
executable file.

Link'n Load (LINKLOD)
Krumsee, Art
is VB code that shows how to establish a network link, run a specified
application, and when done, unload the link. Network managers with VB only have
to enter the application name into the code and compile it for this to work.

NetPrint (NETPRNT)
Krumsee, Art
is VB source that allows network users to link and unlink printers from within
Windows. It includes declarations for 3 undocumented Windows API functions which
should make this application network independent.

McGuinness, Charles
is source code for a simple VT100 terminal emulator.

Disk #3557

HPEnv 2.0
Himowitz, Michael J.
is VB source for an application that prints envelopes in landscape mode without
having to change printer setup. This is also on a Windows applications disk.

Schwartz, Corey
is a DLL and supporting code to add to your applications to allow the user to
select printers, as well as to adjust printer setup and configuration. All C and
VB source code is included.

Mattila, Jussi
is a set of printer selection procedures for VB.

Psetup revC
Kitsos, Costas
is a DLL that lets you invoke the Windows standard Printer Setup Dialog. Sample
source code is included.

Scott, Dennis
is source code for an app that lets you print ASCII text files to an HPLJ
printer in booklet format.

Obeda, Ed
shows how to calculate the default printer's lines per page under the declared
FONTNAME and FONTSIZE. It will also a allow the user to ABORT the print job,
thereby flushing the printer buffer.

VB Printer Control (VBPRINT)
Harrington, Dennis
demonstrates how to put a laser printer into Landscape mode and how to select a
paper bin without going through the Windows print manager.

Disk #3704

Jones, Tim
is a tutorial on writing and using INI profile files. Source code examples are

Mosher, Sue
displays the hex keycode for any key pressed. This is useful for any Windows
programmer, but the included source code is VB.

is a text files that tells how to handle slow loading programs to keep the user
from messing things up.

ResCheck 1.0 (RESCHEK)
Houck, Dan
is source for checking and displaying GDI and User Heap information in an icon.

SySwitch 2.03 (SYSWTCH)
Bonner, Paul
is VB source for a utility for changing Windows configurations.

VBMem 1.1
Snider, Charles K.
is source for a VB app that displays Windows system information: the operating
mode, presence of a math chip, and free memory. Compiled and run minimized, the
display will constantly update the free memory number.

is a text file listing all the Windows 3 API declarations for VB. With these
declarations in your VB code, you can perform many functions not otherwise
available in VB.

User Friendly, Inc.
solves the problem of needing an exact match when using the AppActivate command.
This can also be used to determine if your application is already running.
Source is included.

Time/Date Applications:
Disk #3704

Calendar Program (CAL-PRO)
Meadows, Al
is the source code and executable for a calendar program.

DateTime (DAT-TIM)
Michna, Hans-Georg
is VB source to display the date and time in a variety of international or
custom formats.

SmallCal (SMALCAL)
Meadows, Al
is source for a sample application which illustrates how to generate a calendar
for a user-specified month.

VB Clock 1.0 (VBCLOCK)
Gagliano, Jim
is VB source for an app to display the date and time in a very small window.

Disk #3788

Campbell, George
is a set of routines to scale, print, and display bitmaps. A demo and all source
code are included.

Burglar & Animate (BURGLAR)
Campbell, George
contains source for two demonstrations of simple animation using icons.

Martin, Wendell
is a routine that works like the graphic Get and Put statements in DOS Basic.
Example code is included.

IconDemo (ICONDEM)
Young, Ted M.
is demo code to show how to draw graphics on a VB app's icon.

Newstart 1.04 (NEWSTRT)
Elkins, Jeff
is VB source for an app that allows you to change the Windows start-up graphic
from the MS logo. This is also on a Windows application disk.

PolyGone (POLYGO)
Mack, Jim
is the source for an interesting graphics display. It also illustrates the use
of graphics when iconized.

Programmer's Workshop 1.0 (PROGWRK)
Bushnaq, Firas
is a template that shows all available screen resolutions on your monitor to
make is easy for you to size your forms and dialogue boxes. It has a ruler scale
which shows twips, inches, points, picas, and pixels and can be used for
conversions among those units of measure.

is an outstanding demonstration of using the BitBlt and StretchBlt API
functions. The demo code displays a grid of tiles which you can flip over to
show a graphic or which you can X out with a zooming red X. The code also shows
how to store a series of images in a single picture file and use the individual

Egger, Fredi
shows how to print text at any angle in VB apps to screen or printer.

User Friendly, Inc.
is a DLL that allows you to create Windows screen savers (like After Dark) in
Visual Basic.

is an outstanding demonstration of using the BitBlt and StretchBlt API
functions. The demo code displays a grid of tiles which you can flip over to
show a graphic or which you can X out with a zooming red X. The code also shows
how to store a series of images in a single picture file and use the individual

demonstrates how to create a mask from an image and a screen object using

shows how to add graphic bitmaps and check mark substitutes to a menu.

demonstrates an easy way to scroll/pan across a bitmap in a Picture Box.

Melanson, Leo
is the source for an animation sequence in which Waldo walks across the form.

is a set of graphics icons in WMF format for use in your VB programs.

Custom Controls:
Disk #3880

ClipSib 1.0
Funk, Keith
allows you to have overlapping controls, which VB does not normally support. VB
code for an excellent demo of the routine is included. This code contains many
other useful routines as well.

Mattila, Jussi
tells how to create a Meter box from two Picture boxes.

Graphic 1.1
Cramp, Stephen
is a 256-color custom control. It also includes support for OS/2 BMP files. This
version can only be used within VB. Registration gets a runtime module.

is a routine for entering data into a Grid control and to scroll the Grid with
Cntl-Arrow or the mouse.

Funk, Keith
shows how to use a Picture control to simulate dragging of a Text control (which
normally cannot be dragged).

MsgBox Editor (MSGBOX)
Buhrer, Richard
makes it easy to set up MsgBoxes. Run this utility, click on the elements you
want, add the text, and it generates the source code for you on the Clipboard
for you to paste into your program.

GrafStat 1.0 (GRAF-ST)
Object Productivity, Inc.
is a custom control for displaying the status of a process. Two styles are
offered: thermometer and needle.

Grid Routines (GRIDRTN)
Ford, Nelson ASP
is a set of routines for use with the Grid custom control. Routines include
cursor key scrolling of the Grid, direct input to the Grid, add a row, delete a
row, save to and load from disk, clear the Grid and more.

is a single control that lets you display data in columns and rows. This was
originally given to beta testers by Microsoft, but not included in VB. Jonathan
Zuck has made enhancements to it.

HugeGrid (HUGEGRD)
Ford, Nelson ASP
is sample code illustrating the integration of a Huge Array with a Grid control.
Features include sorting, searching, adding, deleting and sorted inserting of
records into the Huge Array, as well as paging and scrolling through data with
the Grid.

Funk, Keith
allows you to make picture controls look like VB forms. It demonstrates how to
use the Win3 API functions to add a caption, sizing border and a minimize box
and system menu to a picture control.

PropView (PROPVW)
is a Custom Control that can display the properties of the current form and
allows printing of the control properties.

RandGrid (RANDGRD)
Ford, Nelson ASP
is sample code illustrating the integration of a random access file and a Grid

Kitsos, Costas
is commented VB source code to create a Spin Button Control. No DLL/VBX files
are required.

is a VBX that lets you create a meter type display with horizontal or vertical

General Tools/Reference/Info:
Disk #3698

is VB code that illustrates a potential problem with adding fixed length strings
to a List Box.

is a Help file that cross-references the API calls listed in WINAPI.TXT,
included on this disk. To use this file, while in VB, press F1 for Help, then
Alt-F and O to open the file.

App Shell 1.0 (APPSHEL)
Presley, Jim
is VB source code for a sample application that can be used as a starting point
for file-oriented applications. The app includes forms for file open/save,
printing, printer selection and setup (including a custom DLL with C source) and
print canceling and a main form that ties everything together with pull-down
menus and related code.

Young, Ted
is code for an "application" that simply illustrates the use of all the VB
Picture Box functions.

Click & Double-Click (CLK-DBL)
Ford, Nelson ASP
is code that shows how to trap both the click and double-click events for a

Color Calculator (COLCALC)
Evers, Dave
displays color number for color combo selected on sliding bars. VB source code
is included.

Ford, Nelson ASP

is a Module that will draw shadowed frames around controls to create the
illusion of their being recessed. Also included is a routine for underlining a
control such as a Text box.

MicroHelp, Inc.
eliminates unused DLL declarations from the Global module of your programs,
resulting in a smaller EXE.

shows how to use drag and drop, how to link to the Windows calculator, notepad
and winhelp, and how to use the clipboard.

demonstrates the use of VB's implicit refresh versus an explicit refresh of a
Text Box.

Kitsos, Costas
is a utility which can be used to determine the IDs of VB Controls during
design-time. With the ID's known, you can use API calls during run-time without
having to use the clumsy SetFocus method or to employ an extra DLL for the

Disk #3880

BaseCnv (BASECN)
Lyle Lensen
is a routine for doing number base conversions.

Mack, Jim
is a routine to round Currency variables at a given number of decimal places.
ASM and OBJ files and sample code included.

is a routine to force Currency variable amounts to 0, 1, 2, or 3 decimal digits
of accuracy.

shows how to do DDE in a generic fashion. Lets you test which DDE commands will
work with specific applications. Prompts for appname, pathname, topic, item,
execute string, window mode and attempts to conduct the DDE link. Source code

Young, Ted M.
is code that shows how to get/change properties of menu items.

FOpen 1.1
Kitsos, Costas
is a set of forms, VB code, and API calls to create the standard Windows type of
"Open File" dialog box. A demo is included.

is a sample application that shows how VB can be used as a control and
monitoring software.

Mosher, Sue
is code for dragging a line in a List box to another line with the mouse or
cursor keys.

MultiPik (MULTIPK)
Kitsos, Costas
shows how to create a multiple-selection List box with API calls. This is a
frequently requested feature for VB. Source and demo included.

Computing, Marquis
is a set of routines to emulate binary coded decimal and IEEE string/numeric
conversion functions (MKI$, etc.) found in QB and PDS but not in VB.

is the source code for the sample routines in the VB Programmer's Guide.

Vance, Rob
is VB code (API calls) that makes it easy to store program data in WIN.INI.

Text Style (TXTYLE)
Irwin, Gregg
shows how to change the style of Text boxes to allow only uppercase or only
lowercase letters, to limit the number of characters, and to create a password
entry box.

Kitsos, Costas
shows how to load/save an array of up to 64k in a single operation.

contains an MPU-401 MIDI card demo and play-only source routines.

is source code to show how to access an embedded chart in Excel and link it to a
basic picture control.

Eibl, Gunter
contains the source code for an ISAM-emulation, using the rules of Basic PDS
7.1. The emulation needs the Btrieve DLL. An an example is included to see how
to use Btrieve from Visual Basic.

Disk #4062

is source that shows how to use VB to create and edit a form-based document in
Microsoft Word for Windows.

shows how to use DDE to get and manipulate data from Word for Windows.

Turpin, Jerry
shows how to use the MultiMedia Extensions to play MID, WAV, and MMM files.

is an upgrade of the Query VB program in MS's VB SQL Server DLL kit (required to
run this file). It improves the handling of returned rows.

is source code for a program to find and run installation programs on program

PsL's March 1991 Visual Basic Disk:

(The files on this disk will also be added to the disks above by category.
This task is normally completed by the 15th of each month.)

LAN Services; $10
lets you create beautiful 3-D effects. More than 20 different routines are
included to draw 3-D frames, shadowed boxes, borders, picture buttons, etched
and shadowed text, and more. Interestingly, this is all done with VB code - no
DLL's to keep track of. All source code is included.

BarCode 3 of 9 1.1 (BAR3-9S)
VB: Printing
Eclipse Software Programming; $15
is VB source for printing 3-of-9 barcodes.

DropDown (DROPDWN)
VB: Controls
shows how to simulate a Combo box with a Text box and a List box.

Filer 1.1 (FILR-GC)
VB: Sample Apps
George Campbell; $0
is VB source for an app that will search a hard disk for and display files, as
well as the directory tree. BMP/ICO files are displayed graphically. Printing
routines are included.

VB: System
shows how to look for a window based on Caption and/or ClassName.

VB: Calendar
Meadows/Graves; $0
is source for making a calendar using the Grid custom control.

VB: DLL's and Utilities
MicroHelp, Inc.; $0

Novell-VB (NOVL-VB)
VB: Networking
L.J. Johnson; $0
contains an example program with source code that allows you to attach to and
de-attach from multiple servers. You can also set drive mappings on your primary
server. Many of the basic Novell API's are demonstrated.

VB: Utility
Gregg Irwin; $0
provides you with a reminder, as well as the ability, to save a project before
running it. Forgetting to save before running is a sure way to lose lots of work
over time. While Irwin designed this as a stand-alone, it seems like it would be
more effective to make it the start-up form in your program during the design
and testing period.

contains an MPU-401 MIDI card demo and play-only source routines. This update
adds dynamic midi channel and velocity change as well as the ability to abort

VBShell 1.0
VB: System
Paul Smith; $0
is designed to get around the fact that a VB app cannot be used as a Windows
shell. VBShell can be loaded as a shell; then it loads your VB app and unloads
itself, leaving the VB app as the functional shell.

VB-Tips March 1991
VB: Routines, Tips
Compiled by Nelson Ford; $0
This file.

Quick and Dirty Help 1.0 (QDHELP)
Help Utility
Phil Allen; $20
lets you use any ASCII editor to produce RTF files for the creation of Windows
3.0 Help Files. With QDHelp, you can easily create help files with links,
popups, bitmaps, search keys, and browse sequences. The MS Help Compiler is

 December 22, 2017  Add comments

Leave a Reply