Dec 062017
 
Visual Basic Tips File - Updated 7-1-92.
File VB-TIPS.ZIP from The Programmer’s Corner in
Category BASIC Language
Visual Basic Tips File – Updated 7-1-92.
File Name File Size Zip Size Zip Type
TPCREAD.ME 199 165 deflated
VB-TIPS.TXT 263734 90487 deflated

Download File VB-TIPS.ZIP Here

Contents of the VB-TIPS.TXT file


VB-Tips
July 1, 1992

CONTENTS:

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

I. PROGRAMMING::

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

Calls - Controls & Modules:
Calling Other Form's Controls
| Calling Other Form's Subs/Functions
| Passing Control Arrays
| Passing Variable Arrays
Passing Form & Controls in CALLs
Passing Properties

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

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

Focus:
| Control Must Be Visible
GotFocus Lost Due to MsgBox
| LostFocus Bug
LostFocus Timing

Global.Bas:
DEFINT Is NOT Global
Global Const CRLF = chr$(13) + chr$(10) Not Allowed
Global.bas Renaming
WINAPI - "Out of Memory"

Keyboard, Mouse, Joystick:
| Alt-F4-Exit Trapping
Calling the Same Routines
| Ctrl-Alt-Del Can't be Trapped
} Double-Click before Click, Trapping
INKEY$ Loop Emulation
Joystick & VB
Keyboard/Mouse Shortcuts
Mouse Move Event
Mouse Pointer Style Setting
Right Click - Can't Detect
Using "Enter" in Place of "Tab"

Printer:
Aligning Text of Different Sizes
Calculating Page Length
Changing Paper Bins
EndDoc Makes Temp Files
EndDoc vs NewPage
Font Not Available on Printer
Formfeed Problems
Getting Available Font Sizes
Getting List of Installed Printers
Getting Printer Settings
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
Sending Raw Data to the Printer
"Printer.TextHeight" Does Formfeed
TextWidth Improvement

Screen (Also see FONTS under CONTROLS):
Background for VB
Color Const Declarations
Displaying "ASCII Graphics"
Framing Controls (the NEXT Look)
| "Painting" Not Finishing
| Resolution, Determining
Screen/Window Capture
Screen Scaling/Form Resizing
Screen Scaling Tips
Screen Type
Screen Wipes
Stop Flashing During Updates
Suppress Arrow Cursor
Testing for 256-Color Display
Twips to Pixels
| Windows is Device Independent - NOT!

VB Environment/Editor & Coding Style:
Breakpoints Change Processing
Code Should Go In Modules
.EXE Size / Junk In EXE's
Removing Deleted Control's Code
Saving Your Work
| Splitting Long Lines

Programming, General:
Alias Use in Declares
API vs VB Routines
API Errors
Autoredraw Error Message
Blanking Out Text
Branding Your Apps
Control ID's
Currency Format vs Date Format
DateValue Bug
| DoEvents(), Use Of
Emulating READ-DATA
Error Messages
Error Trapping
Far Pointers
Help Files
Interrupting a Routine
Julian Date Considerations
| Jumping to a Subroutine During Coding
| Memory, Out of
NEVER USE "END"
| Polygon-Drawing API Call
| Private INI Problem
| RND Seeding
Slow Loading of App
Sound Standards
Telephone Dialing
"Too Many Files"
TRUE <> NOT FALSE
Windows 3.1 Quirks

System:
Calculating System Resources
Close Another App From Within VB
Control Box Menu, Adding Items
} Control Box Menu, Removing Items
} Control Box Exit Bug
Control Accessing Via DDE
Controlling Apps with SendKeys
Corrupt VB File Recovery
Detecting Running Applications
Detecting the Start-Up Directory
Exit Windows from VB
Faster Loading of Apps
File Creation Delay
File Handles - Maximum
Finding Excel
Linking to Excel
Get Program's Path & EXE Name
Get System Focus
hWnd for a Control

hWnd for a Control II
.INI Files
Mode - Which You Are In
Multi-Instance App Prevention
Name in Task List
Networks & VB
Norton Desktop vs VB
"Out of Stack Space" Error
| Program Manager Groups, Getting List of
Resource Limits
Restart Windows
SendMessage
Shell-to-DOS Problems
Shell-to-DOS Syntax
Shell Error Numbers
| Task List Exit Can't Be Stopped
Temp Files
} VB App Cannot Be Windows 3.0 Shell
VBRUN100 Must Be on A:?
| Version (of Windows) Detection
Wallpaper Changing
Why VB Apps Don't Cascade/Tile
WIN.INI Warning

II. CONTROLS, FORMS, FONTS::

Controls, General:
"&" in Captions
Aligning Text & Format$ Padding
Aligning Text on a Form
Button Colors
Buttons with Pictures
Loading Custom Controls
Modal Bugs
| Modal Form Switching Bug
MsgBox Limitations
No DblClick in Directory List Box
Scroll Bars - Automatic and Manual
| Toolbar Rearranging

Combo Boxes:
Changing Text
Color
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 Bug
METER1.VBX Display Update
Sound.DLL
DLL's Explained
DLL's Are Slow
ByVal When Passing Strings
TPW Framework for DLL's
| Passing Records
Passing Strings
SLIDER Control Bug
VBPro MDI Child Creation at Runtime

Form & Control Placement, Sizing:
Centering a Form on Screen
Controlling Form Size
Copying/Moving Controls
Covered Form?
Cursor in First Text Box
"Floating" Window (Forcing Window to Top)
Form Size and Granularity
Form Wipes, Fades, Etc.
Grouped Controls
Iconizing All Forms in a Project at Once
Instant Display of Controls
| Minimizing All Forms At Once
Mouse Pointer, Position
Move Disabling
Moving Controls Cleanly
Moving Form Without Title Bar
| Moving Multiple Forms Together
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
| Property Bar Errors
Resizing Forms
| ScaleWidth/Height Purpose
Scrollable Forms
Unloading Control Arrays

Fonts:
Fixed Width Fonts
Large Font Bug
Listing Printer Fonts
| Metafiles in Place of Fonts
TrueType Fonts Don't All Show Up
Using Different Fonts, Colors in a Box
System Font

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

Grid Control:
What is the Grid Control?
| Alternative to a Grid Control
Can't Enter "&"
| Graphics in a Grid Cell
Faster Row Adding/Deleting

Icons:
Changing Icons
} Getting an Icon from an EXE
Icon Caption Doesn't MOVE
Icon - Get Rid Of
Transparent Background

Label Boxes:
} An Alternative to Label Boxes
| Captions, Multi-Line
Label Box Text Background Color

List Boxes:
"AddItem #" Error
| Bitmaps in List Boxes
Capacities
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:
| An Alternative to Picture Boxes
As Buttons
AutoRedraw
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 A Full Picture
Dragging Text Box in Picture
Drawing - Scale
Faster Drawing
Number of Colors Supported
Saving Extracted Icons
Saving Picture Files
ScaleHeight Negative
Unloading a Picture
Zoom In/Out On A Picture

Text Boxes:
255-Character Limit?
CtlName Access Via CDK
Cursor (text), Position
Data Entry Masking
Data Entry Routine
Data Entry Text Limit
Fast Loading
Flicker
Flashing/Highlighting Text
Increasing Line Limit
| Keeping Text Highlighted
Line Number, Finding
Number of Lines
Read-Only Text Box
| Tab Key Trapping

Timer:
DoEvents () in a Timer
| Resolution/Accuracy
Using the Timer

III. OTHER::

Knowledge Base:
What is Knowledge Base?

Visual Basic Books:
VB Book Reviews

VB Downloadable Files:
How to Get the Files
DLL's, Utilities
DOS Access/Functions
Font Utilities
Networking/Communications
System
Time/Date Applications
Graphics/Video
Custom Controls
General Tools/Reference/Info
Miscellaneous
Database, Data Entry, Sorting
Printing
Other
PsL's July 1992 Visual Basic Disk

---------------


Background:

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.
PsL distributes shareware on disks.

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 are presently being posted every month or two. Updates are cumulative,
so if you get an update, there is no point in keeping the old files.

This file would not have been possible without the willingness of others to
share their knowledge. Early updates did not include the name of the
contributor with each tip, but the following were, and continue to be, among
the important contributors:

Jonathan Zuck
Keith Funk
Mark Novisoff
Ted Young
Dennis Harrington
the Microsoft Section Leaders

Sysop of the MSBASIC forum on CompuServe is Jerry Fisher, 76711,22.
---------------


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.

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.

If you cannot find information you need in the CONTENTS, be sure to browse
through the text of the "VB Downloadable Files" section at the end of this
file.

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. Feel free to customize the source for your own
use, such as hard-coding the location of the VB-TIPS.TXT file on your system.

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
backup.

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
Window.

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.
---------------


Disclaimers:

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 on the MSBASIC forum (sec. 5) 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
FWIW = For what it's worth
= "Grin"
= Grinning, ducking and running (usually appended to a teasing remark)
IMO = In my opinion
IMHO = In my *humble* opinion
PITA = Pain in the a(natomy)
PMFBI = Pardon me for butting in
PMJI = Pardon my jumping in
ROFL = Rolling on the floor, laughing
😉 = a smiley face winking (tilt your head to the left and look at it)

There are many more. You can usually figure them out - or just ask.
---------------


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:

Global:
Type SomeType
X As Integer
etc
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:

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
---------------


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 Variables, Clearing Of:

To clear an entire Type in one shot you can dimension a second Type variable
of the same kind, and use LSet to assign it:
Dim Inv As Invoice, Dummy As Invoice
LSet Inv = Dummy
(Ethan Winer)
---------------


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.

Example:
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.
---------------


Calling Other Form's Subs/Functions:

You cannot directly call a sub or function in another form. Form routines are
"local" to that routine. You have to move your routines into a module, which
makes them "global".

One method I use to get around this is to create an invisible textbox or
caption on a form, and then in the textbox_chaged sub for that control, call
the sub. Its a little obtuse, but it works. Simply address the control: sub
text1_change()
Call some_neat_sub end sub

then inside another form, I use:

form1.text1.text="1" REM or whatever the arguments are"

This fires the form1.textbox_changed routine, which in turn calls my sub.

However, there are lots of good reasons to move routines in a module of thier
own (memory, speed etc..) (hank marquis)
---------------


Passing Control Arrays:

You can send an a single element of a control array to a Sub, but not a whole
control array. (Ted Young)

Example:
For i = 0 to 9
Call SomeSub(SomeControl(i))
Next
(Jeff Simms)
---------------


Passing Variable Arrays:

To pass an array to a subroutines, use enpty parentheses:

Call ProcName(Array(), otionalparam1, param2, etc)

Inside the called routine you can use LBound and UBound to know how big the
array is. (Ethan Winer)
---------------


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
parentheses):

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
---------------


File Copying:

1.
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.
close

2.
Here's a routine for file copying using global memory and the new 3.1
functions hread and hwrite which will read/write any size file in one shot as
long as sufficeint global memory is available. You'll want to put error
handling in as needed. I've included the hread & hwrite declarations. You can
get the other function declares and the constants from VBAPI.TXT.

Declare Function hread Lib "kernel" Alias "_hread" (ByVal hFile As Integer,
lpBuffer As Any, ByVal dwBytes As Long) As Long
Declare Function hwrite Lib "kernel" Alias "_hwrite" (ByVal hFile As Integer,
lpBuffer As Any, ByVal dwBytes As Long) As Long

inHndl% = lopen(InFile$, OF_READ) 'open source file for reading
size& = llseek(inHndl%, 0, 2) 'find length of file
msg& = llseek(inHndl%, 0, 0) 'reset file pointer to beginning
memHndl% = GlobalAlloc(GHND, size&) 'allocate needed global memory
memAddr& = GlobalLock(memHndl%) 'get global memory address
inBytes& = hread(inHndl%, ByVal memAddr&, size&) 'read in source file
outHndl% = lOpen(outFile$, OF_CREATE Or OF_WRITE) 'open target file
outBytes& = hwrite(OutHndl%, ByVal memAddr&, size&) 'write out target
ok% = lclose(inHndl%) 'close source file
ok% = lclose(OutHndl%) 'close target file
ok% = GlobalUnlock(memHndl%) 'unlock global memory
ok% = GlobalFree(memHndl%) 'free global memory

(Dennis Harrington)
---------------


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.

Global:
DefInt A-Z
Type OFSTRUCT
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$)
Dim a As OFSTRUCT
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.)
---------------


Btrieve:

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$
Next
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
Next
---------------


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
---------------


Objectrieve:

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
---------------


Control Must Be Visible:

If you get an illegal function call when trying to SetFocus to a control, the
reason is probably that the control/form is not visible. This usually happens
in Form_Load. Just add the line: "Form1.Show". (Mark Novisoff)
---------------


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.
---------------


LostFocus Bug:

If you have specified a lost focus procedure it is possible that
when the focus is lost, your form will re-load itself if the string
being examined is a null string. It took me awhile to find the
problem, but if I commented out my code, sure enough everything
worked fine. I got around it by first specifying in the lost
focus routine that the string I was checking must not be a null
string (if so, exit sub). (Jim Poland)
---------------


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 or if you use SetFocus to
change focus, then control-1's LostFocus gets executed AFTER control-2's Click
event.
---------------


DEFINT Is NOT Global:

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.BAS
Global CRLF$

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


Global.bas Renaming:

VB will not let you load a file and rename it to Global.bas, so you can
cut-and-paste code, but it's easier to use the Save Text to save the desired
Global.bas code and then use Load Text (in the Code menu) to Replace or Merge
it into the new Global.bas. (Jon Wumkes)
---------------


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.
---------------


Alt-F4-Exit Trapping:

To stop the user from quitting your app by pressing Alt-F4 (the shortcut key
for the System box Close), you can put Cancel=-1 in Form_Unload, but that also
keeps you from unloading the form when you really want to. You can get around
this by setting a global flag/variable before unloading the form and checking
the variable in the Unload sub.

Another alternative is to remove Close from the Control box. See "Control Box
Menu, Removing Items" under "Programming - System".
---------------


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()
Commonhandler
End Sub
Sub Text1_KeyPress(....)
If KeyAscii = 13 Then
KeyAscii = 0
CommonHandler
End If
End Sub
---------------


Ctrl-Alt-Del Can't be Trapped:

Ctrl-Alt-Del cannot be trapped in VB.
---------------


Double-Click before Click, Trapping:

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):
(A previous routine given here was crocked. Sorry.)

General_Declarations:
Dim DblClk, Reject

Sub Form_Load()
For i = 1 to 20: List1.Additem Str$(i): Next
End Sub

Sub List1_Click()
If Reject Then Exit Sub Else Reject = True
x! = Timer: Do: y = DoEvents(): Loop While Abs(Timer - x!) < .3
'The line above pauses to allow some time for the 2nd click, if any.
'You can play with this number to get best results. The larger the
'number, the longer the delay when doing a single click. The smaller
'the number, the more likely you'll generate an unwanted single click
'execution when trying to do a double click.
If DblClk Then DblClk = 0: Exit Sub
Print "Single Click"
Reject = 0
End Sub

Sub List1_DblClick()
Print "Double Click"
DblClk = - 1 'it does help to actually *set* this variable
Reject = False
End Sub
---------------


INKEY$ Loop Emulation:

In other Basics, you can say:

WHILE INKEY$ = ""
[do stuff]
WEND

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

GLOBAL.BAS:
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
Const Msg$ = "XXXXXXXXXXXXXXXX"

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

SomeSub ():
While NoKeys ()
[do stuff]
Wend
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
---------------


Joystick & VB:

You cannot read the joystick port with native VB commands. See MHELP in the
program listing at the end of this file for a DLL that lets you emulate INP
and OUT statements that should do the job.
---------------


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 Move Event:

The _MouseMove event is only triggered when the mouse changes position.
However, there's a bug in VB which causes continuous MouseMove events when
you're using debug.print within the MouseMove event. (Ted Young)
---------------


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.)
---------------


Right Click - Can't Detect:

VB provides no direct means of detecting a double-click of a right mouse
button.
---------------


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
---------------


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
Global Const GETSETPAPERBINS = 29

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

Note:
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 Makes Temp Files:

When you do EndDoc, VB makes a NEW temporary file for the new page. So if you
use NewPage to do page ejects, say for printing 1500 labels, then you get 1500
temporary print files!
---------------


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:

If using PostScript, TrueType, ATM or other scalable fonts, then any size is
available.

For raster (fixed-size) fonts, you can use the following:

For i% = 1 To 30
FontSize = i%
If FontSize <> LastFontSize Then
Print FontSize
LastFontSize = FontSize
End If
Next

This will show you the font sizes that windows will *produce* for you. Note:
o this is NOT the list of exact fonts available
o you *could* miss some sizes here, so if you need more
precision then loop with something like For i! = .5 To 30 Step .5
---------------


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.
---------------


Getting Printer Settings:

DefInt A-Z
Type DEVMODE
DeviceName As String * 32
SpecVersion As Integer
DriverVersion As Integer
Size As Integer
DriverExtra As Integer
members As Long
Orientation As Integer
PaperSize As Integer
PaperLength As Integer
PaperWidth As Integer
Scale As Integer
Copies As Integer
DefaultSource As Integer
PrintQuality As Integer
Color As Integer
Duplex As Integer

For Win 3.1:
''''''''''''
Declare Function ResetDC Lib "GDI" (ByVal HDC, lpDevMode As DEVMODE)

Sub Command1_Click ()
Dim OrientBuff As DEVMODE
OrientBuff.Orientation = 2
OrientBuff.members = 1
OrientBuff.SpecVersion = &H300
OrientBuff.DeviceName = "HP LaserJet Series II"
OrientBuff.Size = Len(OrientBuff)
ApiErr = ResetDC(Printer.hdc, OrientBuff)
Debug.Print ApiErr, Printer.hdc
For ncount = 1 To 5
printer.Print String$(30, Chr$(64 + ncount))
Next
Printer.EndDoc
End Sub

For Win 3.0:
''''''''''''
Declare Function escape Lib "gdi" (ByVal HDC As Integer,
ByVal nEscape As Integer, ByVal ncount As Integer,
lpInData As Any, lpoutdata As Any) As Integer

Sub Form_Click ()
Dim RtnCode As Integer
Dim ncount As Integer
Static Orient(0 To 4) As Long
Orient(0) = 2
RtnCode = escape(printer.HDC, 30, 10, Orient(0), 0&)
For ncount = 1 To 5
printer.Print String$(30, Chr$(64 + ncount))
Next
printer.EndDoc
End Sub
(Tony G. Strain)
---------------


Landscape Setting on HPLJ:

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

GLOBAL:
Type ORIENT
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
Campbell)

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 " "
Printer.EndDoc
---------------


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
CompuServe.
---------------


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:
Printer.NewPage
Printer.Enddoc
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
Printer.EndDoc
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%)
END SUB

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
Else
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 MAX_CHAR_PER_LINE = 80
Const EM_GETLINE = &H414
TextBox.SetFocus
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.
Const EM_GETLINECOUNT = &H40A
TextBox.SetFocus
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
settings.

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)
---------------


Sending Raw Data to the Printer:

Using the PASSTHROUGH Escape function, one *should* be able to print raw data
on a printer. There has been some discussion about some Postscript drivers not
supporting the PASSTHROUGH Escape. I don't know. The PASSTHROUGH is kind of
tricky because of the info required to pass in the string to print. This
should work...

'Declaration section
Declare Function Escape Lib "GDI" (ByVal hDC As Integer, ByVal nEscape As_
Integer, ByVal nCount As Integer, lplnData As Any, lpOutData As Any) As_
Integer ' all on one line
Const PASSTHROUGH = 19

String2Pass$ = "This is the string to print."
InData$ = Chr$(len(String2Pass$) Mod 256) + Chr$(Len(String2Pass$) \ 256)
InData$ = InData$ + String2Pass$
Printer.Print " "; ' If you've already printed since last EndDoc, this isn't
' necessary.
Result% = Escape(Printer.hDC, PASSTHROUGH, Len(InData$), InData$, 0&)
Debug.Print Result% ' length of string actually passed to printer.
Printer.EndDoc

The calculation to set the first two bytes of InData$ is simpler than it
looks. The first byte is the low byte of the string length to send. If it's
less than 256 you can just set it to the Chr$(Len(String2Pass$)) + Chr$(0).
The high byte would be used if it's longer than 255. (Jack Presley)
---------------


"Printer.TextHeight" Does Formfeed:

Q: The code "X = Printer.TextHeight("Sample")" causes Print Manager to open,
and after exiting the VB program, my printer does a form feed. All I want to
do is set a variable to the TextHeight, but it ejects a piece of paper. (Paul
Davison)

A1: In the file EndPrn (see program listings at the end of this file) is a
method of cancelling a print job. Use this routine to cancel the blank page.
It takes about half a second. (George Campbell)

A2:
Global:
DefInt A-Z
Const ABORTDOC=2, Const NULL = 0&
Declare Function Escape Lib "GDI" (ByVal hDC, ByVal nEscape, ByVal nCount,
lplnData As Any, lpOutData As Any)
Form:
DefInt A-Z
...
printer.scalewidth = 3000
hite& = printer.TextHeight("I")
wit& = printer.TextWidth("I")
Print "hite& "; hite&
Print "wit& "; wit&
x% = Escape(printer.hDC, ABORTDOC, NULL, NULL, NULL)
Print "ABORT"; x%

The above code worked on PostScript and PCL laser printers. On the PS printer,
ending caused a "Printer Error" in VB, and that error plus "Can't Quit At This
Time" error (which nonetheless allowed me to quit after hitting OK) when
compiled. (Robert Eineigl, MS)
---------------


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):

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);
Next
Print
Next
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
effect.

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
'bottom:
Screen.ActiveControl.Line (C.Left, C.Top + C.Height + Offset)
-(C.Left + C.Width, C.Top + C.Height + Offset)
'right:
Screen.ActiveControl.Line (C.Left + C.Width + Offset, C.Top)
-(C.Left +C.Width +Offset, C.Top +C.Height +Offset)
Screen.ActiveControl.forecolor = &H808080
'top:
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)
'left:
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)
(etc)
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.
---------------


"Painting" Not Finishing:

Changes to forms, etc, may not show up as soon as you might wish if the
program is involved in heavy processing. To get the changes to show up
on the screen right away, use .REFRESH, such as List1.Refresh.
---------------


Resolution, Determining:

The following code returns the dots-per-inch resolution of the display:

Global (all on one line):

Declare Function GetDeviceCaps Lib "GDI" (ByVal hDC As Integer,
ByVal nIndex As Integer) As Integer

Form1_Load:
xDPI% = GetDeviceCaps(Form1.hDC, 88)

This is suffficient for VGA, S-VGA and 8514-A because those resolutions have
"Square" screen metrics. I.E. 96X96 DPI or 120X120 DPI. CGA and EGA, on the
other hand, use metrics which are not square...CGA is 96X48, EGA is 96X72. If
you want to appropriately scale for these resolutions you will need to
determine the yDPI as well...

yDPI% = GetDeviceCaps(Form1.hDC, 90)

(Bob Scherer)
---------------


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
printed.

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.

More:

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)

Also see the ICONWRKS code (comes with VB) for illustrative code.
---------------


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
Type$="CGA"
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
Morris)

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)
---------------


Testing for 256-Color Display:

You need to call GetDeviceCaps to check the RASTERCAPS value and the bit within
it known as RC_PALETTE. In order for a vid card to display 256 or more colors
it needs to support palettes, which is what this bit will reflect. I'll leave
it to you to figure out the API call. (Steve Cramp)
---------------


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
---------------


Windows is Device Independent - NOT!:

A form that you carefully lay out on your monitor can look substantially
different on other systems' video. John Socha has a full article about this in
the July 1992 issue of WinTech magazine, so I don't think the problem can be
fully treated here in VB-Tips.

The tips in this section can help, but the best bet is to test your program
on a wide variety of monitors and card types.
---------------


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.
---------------


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)

(1) Module code is more efficient in terms of memory usage and is
initialized only one time (i.e., when it is first loaded--although
local module variables will be initialized each time the function
is called).

(2) Form code is not unloaded, but is apparently initialized each time
the form associated with it is reloaded (a little paradox there).

(3) Static variables are really static, no matter where they appear.

(4) It is generally better to put non-event functions into modules.

(5) It is efficient to unload forms (actually all that really gets
unloaded is the display component of the form, which is all that
really needs to be unloaded -- more or less).

(6) it is OK to put DLL defines in any module and the effect is the same
as putting them in the global module. (J.D. Evans, Jr.)

Variables Dim-ed at the general declarations level persist but variables just
Dim-ed (as opposed to Static) in a sub are stack variables and thus gone if
the form is unloaded. (Robert Eineigl)
---------------


.EXE Size / Junk In EXE's:

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.
Note that to Load the text back in, you must be viewing the code window
to which you wish to load the code.

Microsoft's Explanation:

VB is holding on to all procedure names, variables/consts, control and form
names; in short everything that potentially would be needed to evaluate
interactive changes in break mode.

No question that some mechanism should be in place to purge this before compile.
However, consider the case where a compile occurs with no save on existing
changes (some would argue that you should be asked or forced to save changes
before compile as PDS does): The temp files that VB uses may contain code
and references that, though now commented out, are part of the threaded
p-code.

Rather than dump this and force rethreading of everything, it was decided to
hold on to all "threads".

The environment stores all this data from every project loaded in a session
so if a larger project was loaded prior to the one you're compiling,
this hangover gets incorporated into the .exe.

I have heard of situations where the old formname (which matched a presently
existing one) caused problems when both forms where running in two different
exe's.

Doing the Save/Load text and starting your final compile from a clean
environment should eliminate this. The formname is one of the critical tags
that the DLL uses to feed itself so in rare circumstances, to apps with
Form1 formnames could interact. All of these issues are known and will be
addressed in the next version.
Robert Eineigl Microsoft Product Support
---------------


Removing Deleted Control's Code:

When you delete a control, the code goes into the General Procedures list.
To get rid of it, highlight the entire block of code, including the Sub and
End Sub lines, and press Delete.
---------------


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.

Another good idea is to save the code to a Text file (under the Code menu) to
protect against your FRM file being trashed.

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.
---------------


Splitting Long Lines:

VB does not have a line continuation character as does QB.
The only way to split a long line of code into multiple lines is with kludges.
Say that you have a long line like:

If (a=b and not (c=d or e=f) and g=h) or (i=j and k=l) then
Call SomeRoutine
End if

With descriptive variable names, the line above would be long, as well as being
difficult to read and follow the logic of. So I would split it up:

If c=d then
'do nothing; this just gets rid of the "not (c=d or e=f)"
elseif e=f then
'do nothing
elseif a=b then
Call SomeRoutine
elseif i=j then
if k=l then
Call SomeRoutine
end if
End If

Note that an "or" in a line can be split into an ElseIf while an "and" must be
split into an indented "If".
---------------


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).
---------------


Autoredraw Error Message:

"Can't create autoredraw image!" means you're out of ram! Virtual (disk)
memory won't do for creating the persistent bitmap (which is what autoredraw
is trying to do). You need to unload some hoggish forms or toggle autoredraw
on and off instead of having it on for several objects that all need
persistent bitmaps created concurrently. (Robert Eineigl)
---------------


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)
---------------


Branding Your Apps:

To brand your application with the user's name, address, whatever, set a
variable in your app to an identifiable string, such as "ZZZZZZZZZZ". During
runtime, you can substitute the users's name for the string by opening your
EXE as Binary, searching for the string, substituting the name, and writing it
to the file:

Open MYPROG$ For Binary As #1
A$ = String$(LOF(1), Chr$(32))
Get #1, , A$
If wDlg = 0 Then
x& = InStr(A$, DummyString$)
UName$ = String$(30, Chr$(90))
Else
x& = InStr(A$, String$(30, Chr$(90)))
End If
Seek #1, x&
Put #1, , UserName$
Close #1

If you want to be able to continually replace the string in this location, then
you will need to leave part of the String the same, say the first 5 'Z''s.
(Troy G. Strain)
---------------


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)
Dim ID_COMMAND1
Dim ID_COMMAND2

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)
---------------


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$(0@,"0.00")
Print Year(Now)
will display
0
1900

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


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:
10/01/1914

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


DoEvents(), Use Of:

DoEvents allows the user to access other programs (or other parts of
the current program) while the current app is otherwise tying up the
CPU in some sort of processing loop.

You should put "x = DoEvents()" in your code anywhere you don't mind
having the current operation interrupted.

There is a risk that if the user clicks a button that causes lengthy
processing and during that time, clicks the same button again,
unwanted results could occur.

To prevent such happenings, you should set a global flag to indicate
that you are already in that Sub, and then check the flag each time
the Sub is entered, doing a Sub Exit if it is set.

Setting MousePointer to an hourglass at the start of long processing
alerts the user and helps prevent unnecessary extra button clicking.
---------------


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
Do
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
shown.
---------------


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.
---------------


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.
---------------


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
files.

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.

4. WordPerfect's RTF format for footnotes is reported not to be compatible
with the Help Compiler.

5. There are several utilities available for generating Help files without
having to learn WFW or other RTF-compatible word processor. They range from
very expensive to inexpensive shareware. (See the program listing at the
end of the file for shareware solutions.)
---------------


Interrupting a Routine:

Question:
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?

Answer:
Use a DoEvents (see your manuals for details):

Example:
While DoEvents and CounterOn
Counter = Counter + 1
Wend

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)
---------------


Jumping to a Subroutine During Coding:

If the cursor is on the name of a sub or function call, you can jump to that
code simply by pressing Shift-F2. Unfortunately, there is not a key for
jumping back to where you were. (Kevin Cook)
---------------


Memory, Out of:

1. An "Out of Memory" error when you know you have plenty of memory often
means that you are using up too much of the systems resources. This results
from using too many controls -- particularly Picture boxes.

To cut back on Label boxes, see "An Alternative to Label Boxes" under
"Label Boxes" in this file.

To cut back on Picture boxes, see "An Alternative to Picture Boxes" under
"Picture Boxes" in this file.

2. You can also get this error if your code gets caught in a recursive loop.
This usually is the result of a routine calling itself or a routine calling
another routine that calls another routine (etc) that calls the first
routine.

To track down the problem, run your program in the VB environment. When you
get the error message, use Alt-R, M to move the Next Statement to Exit Sub
and press F8 to single-step. This will take you to the calling sub. Repeat
the process of jumping to Exit Sub and see where it takes you. Usually you
will see that the program flow keeps going back from Exit Sub to the same
line in the same Sub, then fix that line. If it is a more complex recursion,
the single-step will flow back to some other Sub, but eventually you can
spot where the recursion is coming from.
---------------


NEVER USE "END":

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.
---------------


Polygon-Drawing API Call:

Global Const gCYAN = &HFFFF00

Type PointAPI
X As Integer
Y As Integer
End Type

Declare Function Polygon Lib "GDI" (ByVal hDC As Integer,
lpPoints As PointAPI, ByVal nCount As Integer) As Integer

This code will draw your polygon.
Change the number of points to suit your needs.

TheForm.FillColor = gCYAN
ReDim Points(4) As PointAPI
Points(1).X = 100
Points(1).Y = 100
Points(2).X = 200
Points(2).Y = 100
Points(3).X = 200
Points(3).Y = 200
Points(4).X = 100
Points(4).Y = 200
Result% = Polygon(Theform.hDC, Points(1), 4) .
(Bob Scherer)
---------------


Private INI Problem:

If you get bizarre happenings after reading from an INI file (using API
calls), the most likely reason is that you have not initialized the variable
used to return the data to a sufficient length.

For example, if you want to retrieve data of up to 15 characters from an INI
file, initialize the variable with something like X$ = Space$(15).
(Mark Novisoff)
---------------


RND Seeding:

If you need to seed the random number generator ("RND") to a specific series
of random numbers, such as for being able to duplicate deals in a card game,
then you should be aware that:

Randomize n! 'where "n!" is some specific value

does NOT reseed RND() with n! as the manual implies. (In fact, there is no
apparent use for "Randomize n!". Instead, use:

x = RND(-n!)

(Jim Mack)

This feature is downplayed, to say the least, in the VB manual. Also, the VB
manual is a little deceptive in showing the syntax as being RND(-n#). While
it is true that you can use a double-precision number, the fact is that a
number longer than 7 digits will not generate a UNIQUE series of numbers.
That is, if you increment an 8+ digit number by one, the subsequent series
of numbers may not change from the previous series -- not ideal if you are
generating card deals. (Nelson Ford)
---------------


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)
---------------


Sound Standards:

Now that Windows 3.1 is here, let's all make an effort to observe standards.
Notably the new sounds for MsgBox dialogs:

Call MessageBeep before MsgBox dialogs. Follow the chart below..

MsgBox with no icon = 0
MsgBox with Stop icon = 16
MsgBox with Question mark = 32
MsgBox with Exclamation mark = 48
MsgBox with lower-case i = 64

And use the value above for the parameter to pass to MessageBeep.

The declare for MessageBeep is...

Declare Sub MessageBeep Lib "User" (ByVal wType As Integer)

Doing this will make sure that the proper user-assigned sounds are played.
(Mike Mezaros)
---------------


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:

General:
DEFINT A-Z
Declare Function SetHandleCount Lib "Kernel" (ByVal wnumber)

Form_Load:
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)
---------------


TRUE <> NOT FALSE:

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

(1) 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...".

Example:

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


(2) Here is related quirk:

If X and Y are both -1, then the following statement will print "TRUE":

If X And Y Then Print "TRUE"

This code will also print "TRUE" for some, BUT NOT ALL, positive values of
X and Y, such as 2 and 4. (Nelson Ford)
---------------


Windows 3.1 Quirks:

1. A bug in Win31 and/or VB causes your program not to show up when you go to
make an EXE in the File menu. If you select the form whose icon you want to
use, it will still use it, you just want be able to see it until run-time.

2. The "System" nonproportional 9 point font has been renamed "Fixedsys",
which could cause problems.
---------------


Calculating System Resources:

This is an *UNDOCUMENTED* API call which does the trick:

Declare Function GetModuleHandle Lib "Kernel"
(ByVal lpModuleName As String) As Integer
Declare Function GetHeapSpaces Lib "Kernel"
(ByVal hModule As Integer) As Long '*UNDOCUMENTED*
Function CalcFreeResources (file As String) As Long
info& = GetHeapSpaces(GetModuleHandle(file))
CalcFreeResources = (LowWord(info&) / HighWord(info&)) * 100
End Function
Function GetSystemResources ()
gdires = CalcFreeResources("GDI")
userres = CalcFreeResources("USER")
If gdires < userres then
GetSystemResources = gdires
Else
GetSystemResources = userres
End Function
Function LowWord (dlong As Long) As Long
LowWord = dlong And &HFFFF&
End Function
Function HighWord (dlong As Long) As Long
HighWord = (dlong And Not &HFFFF&) / &H10000 And &HFFFF&
End Function
(Hans Peters)
---------------


Close Another App From Within VB:

Declare Function SendMessage Lib "User" (ByVal hWnd As Integer,
ByVal wMsg As Integer,
ByVal wParam As Integer,
lParam As Any) As Long

Global Const WM_CLOSE = &H10

Assuming hWndApp is the handle, then the usage would be:
msg = SendMessage(hWndApp, WM_CLOSE, 0, 0&)

(Keith Pleas)

Another way to close an already running program is to use AppActivate and
SendKeys. AppActivate doesn't return a value, but you can use On Error.

In most cases, you can use the control bar and shut it down. All assuming that
the program closes gracefully. Oh yeah, you'll want to set the wait argument
to -1 in the SendKeys command. (George Campbell)
---------------


Control Box Menu, Adding Items:

The "Control Box" is the box in the upper left corner of a form. Clicking on
it during run-time brings up a "system menu" (so some people call the box a
"SysBox").

You can add items to the Control Box menu, but you can't attach any code to
it, normally (since VB won't process user-defined WM_SYSCOMMAND events).

However, you can INDIRECTLY trigger user-specified events by using the SysBox
CLOSE event (Form_UnLoad) to replace the SysBox Menu with a PopUp Menu of your
own from which the user can select an action.

The beauty of this method is that you can use the VB Menu Design Window to
build your PopUp menu. This simplifies the work as the VB MDW automatically
maps menu events to procedures. The code is as follows:

Form_Load:
'Use API to get handle to Window Menu Bar (created with VB Menu Design)
hWndMenu% = GetMenu(hWnd)
'Use API to get handle to 1st (and only) submenu on Menu Bar
hPopUpMenu% = GetSubMenu(hWndMenu, 0)
'Use API to get name of submenu which is to replace Control Box 'Close'
'(MStg$ is name of submenu as it appears on your menu bar)
nchar% = GetMenuString(hWndMenu, 0, MStg$, Len(MStg$), MF_BYPOSITION)
'Use API to modify Control Box (System) Menu to replace 'Close'
'with your submenu name
hSysMenu% = GetSystemMenu(hWnd, FALSE)
boolean% = ModifyMenu(hSysMenu%, SC_CLOSE, MF_BYCOMMAND, SC_CLOSE, MStg$)
'Use API to Remove Menu Bar from Window (this is not an option!)
If RemoveMenu(hWndMenu, 0, MF_BYPOSITION) Then DrawMenuBar (hWnd)

Form_Unload:
'If UnLoad event is not thru this Window's Control Box Menu then Exit:
If SetActiveWindow (hWnd) <> hWnd Then End
'Otherwise, Replace Control Box Menu with own PopUp Menu
'(PopUpPosition is declared as 'Dim PopUpPosition As POINTAPI')
PopUpPosition.x = Screen.ActiveForm.Left 'Position in Twips
PopUpPosition.y = Screen.ActiveForm.Top
'Convert twip position of form to pixel position
'(TwipsToPixels is Sub of my own design to do this.)
Call TwipsToPixels(PopUpPosition, PopUpPosition)
'Position PopUp Menu where Control Box is
X% = PopUpPosition.x 'Position in Pixels
Y% = PopUpPosition.y + GetSystemMetrics(SM_CYSIZE)
Cancel = TRUE '(Don't forget this! Or your program will exit)
boolean% = TrackPopupMenu(hPopUpMenu, 0, X%, Y%, 0, hWnd, 0)

Since you are replacing the "Close" function of the Control Box, you need to
make "Close" one of the options in your PopUp submenu.

Also, since the Menu Bar will be deleted as a part of this method, your form
will adjust itself vertically to replace the missing menu bar. If you set
AutoSize=True for the form, this is taken care of, otherwise you need to keep
this in mind when fixing the form borders.

You must add the necessary DECLARES for the the API functions and constants
used in the code. They can be found in WINAPI.TXT. This technique can also be
used to create a 'floating' PopUp Menu just about anywhere. (Gary Williamson)
---------------


Control Box Menu, Removing Items:

Defint A-Z
Declare Function RemoveMenu Lib "User" (ByVal hMenu, ByVal nPosition,
ByVal wFlags)
Declare Function GetSystemMenu Lib "User" (ByVal hwnd, ByVal bRevert)
Global Const MF_BYPOSITION = &H400

Form_Load:
HSysMenu = GetSystemMenu(A_Form.Hwnd, 0)
R = RemoveMenu(HSysMenu, 7, MF_BYPOSITION) 'Separator
R = RemoveMenu(HSysMenu, 6, MF_BYPOSITION) 'Close
End Sub
---------------


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.

If there is more than one form in the project and there is no code in that
forms Unload procedure to unload all other forms or END then the DLL stays
loaded and the app is still a task so no resources are freed (other than
those associated with the one unloaded form).

However, if there is code in the unload (to trigger the exit routine or END)
it will be executed by the System menu close and has the same effect as
triggering your exit routine with a button.

If there are loaded forms orphaned, ending the task from the Task Manager
frees the resources.

One of the resources not freed up in some situations is VBRUN100.DLL, which
Windows does not remove from memory. If you run a VB app from drive A: with
VBRUN on A: and the forms are not properly unloaded as described above, then
Windows will continue to look for VBRUN on A: and display an error window if
it is not still there. However, canceling the error window allows the next
invocation of a VB app to look for VBRUN on your hard disk again.
(Robert Eineigl)
---------------


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)
---------------


Controlling Apps with SendKeys:

It is easy to provide buttons for menu items in other apps using the VB
'SendKeys' command. For example, attach the following code to a pushbutton to
cause the Program Manager group windows to be cascaded (using the
Window/Cascade menu option):

Sub Command1_Click ()
AppActivate "Program Manager" ' Gives focus to the program manager
SendKeys "%Wc" ' Sends ALT+W c to progman End Sub
(Danny Dawes)
---------------


Corrupt VB File Recovery:

If you get a UAE and have to reboot in the middle of running an app in VB, you
may get an error message when trying to reload your source code, meaning that
it has become corrupted.

The only option at this point, if you do not have backup, is to check for any
.TMP files left in your Window's TEMP directory and delete them prior to
loading Windows/VB/project again.

A good habit before starting a programming session is to archive (using LHA,
PKZIP, etc) your project files, using the day's date for a name. That way you
do not risk losing everything.

Another good habit is to save your code as Text (under the Code menu) before
ever running the project in the VB environment. Although this will not
safeguard the forms and controls, the code is normally a lot more work.
---------------


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

Example:
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
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"
Else
Label1.Caption = Left$(path, (VbPos% - 1))
End If
(Arthur Edstrom)
---------------


Exit Windows from VB:

1.
Declare Function ExitWindows% Lib "user" (ByVal dwReserved&, ByVal wReturnCode%)
Form_Click:
RetVal% = ExitWindows(0,0)
or:

2.
Declare Sub ExitWindows Lib "User" (ByVal dwReturn&, ByVal wReserved)
Const EW_RESTARTWINDOWS = &H42&
Form_Click:
RetVal% = ExitWindows(EW_RESTARTWINDOWS, 0)
---------------


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
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)

findwindow(classname$,mywinname$)

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.

Global:
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)

Form_Load:
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
MyForm.Show

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
StartUp:
If Err = DDE_NO_APP Then
T% = Shell(CommandLine, Style)
StartApp = "Excel" + LTrim$(Str$(T%))
Hwnd = GetActiveWindow()
Resume
Else
MsgBox "UnKnown Error " + Str$(Err) + " in StartApp", \
MB_ICONSTOP, "Timelog Error"
Status = VBPXExit()
Stop
End If
End Function
---------------


Get Program's Path & EXE Name:

The following routine will get the full path and filename for your program.
(NOTE: This will not work if the user has renamed the EXE file!)

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
---------------


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):

Global.Bas:
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
Else
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
Command1.SetFocus
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, (Ctl.top * &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
WritePriviteProfileString

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

Example:
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


NOTICE:
======
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

example:
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

Const SW_SHOWNORMAL = 1
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

MsgBox "A DDE LINK HAS BEEN ESTABLISHED WITH A SERVER!!!"

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.
---------------


Name in Task List:

The name that appears in the Windows Task List for a VB application is what
you specify under Title in the Make EXE File menu. If you don't specify
anything, it defaults to the MAK file name.
---------------


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:

SET TEMP=C:\
---------------


Norton Desktop vs VB:

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


"Out of Stack Space" Error:

This is usually caused by recursion (a loop where the same Sub/Function gets
called over and over). The easiest way to detect this is to single-step
through your program using the F8 key. Also see "Memory, Out of".

---------------


Program Manager Groups, Getting List of:

The following routine will get a list of program groups and their contents
from Program Manager. This example uses a Text box to grab the data and
transfers it to a List box array, but you could use other ways to store,
manipulate, and display the data.

Text1.LinkTopic = "ProgMan|Progman"
Text1.LinkItem = "ProgMan"
Text1.LinkMode = 2
Text1.LinkRequest
On Error Resume Next
Text1.LinkMode = 0

t$ = Text1.Text + Chr$(13) + " "
x = InStr(t$, Chr$(13))
y = 0
Do While x > 0
List1(0).List(y) = Left$(t$, x - 1)
t$ = Mid$(t$, x + 2)
y = y + 1
x = InStr(t$, Chr$(13))
Loop

For z = 0 To List1(0).ListCount - 1
Text1.LinkTopic = "ProgMan|Progman"
Text1.LinkItem = List1(0).List(z)
Text1.LinkMode = 2
Text1.LinkRequest
On Error Resume Next
Text1.LinkMode = 0
t$ = Text1.Text + Chr$(13) + " "
x = InStr(t$, Chr$(13))
y = 0
Do While x > 0
List1(z + 1).List(y) = Left$(t$, x - 1)
t$ = Mid$(t$, x + 2)
y = y + 1
x = InStr(t$, Chr$(13))
Loop
List1(z + 1).Refresh
Next

This works for Norton Desktop (from what I understand) also.
(Link routines by Jeff Simms. List box routines by Nelson Ford)
---------------


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)
---------------


SendMessage:

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)
Next

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()
Wend
Next
End Sub

IMPORTANT NOTICE:

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()
Next
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:\command.com /c ...
but this might not give you the right location of command.com. Instead, use
x = (Environ$("COMSPEC") + " /c ...
as in the previous section. - Ted Young


Jonathan Zuck has suggested the following method of shelling to DOS:

In your declarations:
DefInt A-Z
Declare Function GetModuleUsage Lib "Kernel" (ByVal hInst)

In your event/procedure code:
Go$ = "c:\batfile.bat"
hInst = Shell(Go$, 2)
While DoEvents() And GetModuleUsage(hInst) > 0
Wend

George Campbell points out that DoEvents() returns a value equal to the
number of windows open and that if you have none opened or iconized,
then the above routine will not work because DoEvents will return 0.
---------------


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.
---------------


Task List Exit Can't Be Stopped:

Even with "Cancel=-1" in your Form_Unload sub, you cannot stop (in VB) someone
from closing your app via the Windows Task Manager.
---------------



Temp Files:

If you are using large graphical objects such as bitmaps or many forms
with many controls, then as you run and edit (even add a control or two), VB
makes new temp files for each form to track the latest changes (for saving).

If you use a Ram disk for Temp file space, a large project could easily
exhaust the space. So either increase the size of the ram disk, or save more
often, or go to a disk based temp directory. (Robert Eineigl, MS)
---------------


VB App Cannot Be Windows 3.0 Shell:

You cannot create a Program Manger replacement with a VB application in
Windows 3.0, but you CAN in Windows 3.1.
---------------


VBRUN100 Must Be on A:?:

See "Control Box Exit Bug", above.
---------------


Version (of Windows) Detection:

DefInt A-Z
Declare Function GetVersion Lib "Kernel" () As Long

In your code...

Ver = GetVersion() '-- Make the API call
WinVer = Ver Mod &H10000 '-- Extract the Windows version number
DosVer = Ver \ &H10000 '-- Extract the DOS version number
'-- Extract the Major and Minor
' revisions and build the version
' strings.

WinVersion = Format$(WinVer Mod &H100) + "." + Format$(WinVer \ &H100)
DosVersion = Format$(DosVer \ &H100) + "." + Format$(DosVer Mod &H100)
(Gregg Irwin)

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".
---------------

Wallpaper Changing:

Routine 1 uses an undocumented feature of Windows 3.0 to change wallpaper.
This does not change WIN.INI nor the Windows Palette.

Routine 2 uses a feature of Windows 3.1 to change the wallpaper, as well
as changing WIN.INI. This feature also changes the Windows Palette. This
method does not work with Windows 3.0, so you may need to use both versions.
Try method 1 first and "on error", goto to method 2.

1. For Windows 3.0 Only:
=======================
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)


2. For Windows 3.1 only:
=======================
Declare Sub SystemParametersInfo Lib "User"
(ByVal wAction%, ByVal wParam%, lParam As Any, ByVal fWinIni%)

Const SPI_SETDESKWALLPAPER = 20

Const SPIF_UPDATEINIFILE = 1 'update Win.ini Const
SPIF_SENDWININICHANGE = 2 'update Win.ini and tell everyone

SystemParametersInfo SPI_SETDESKWALLPAPER, 0, ByVal BMPFile$,_
SPIF_UPDATEINIFILE
(Jonathan Zuck)
---------------


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
apps.
---------------


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.
---------------


"&" 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:

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

2.
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)
Example:
Right$( Space$(10) + Format$(1.2, "#######.00"), 10)
results in " 1.20"


3.
A method of aligning proportional text is with TextWidth:

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


Aligning Text on a Form:

"SetTextAlign" is a superior way to center text. It not only affects the
output alignment of the API "Textout" call, but the standard VB "Print"
statement reacts to it as well.

Use the following to print a centered line of text on your form:

Global:
Global Const TA_NOUPDATECP = 0
Global Const TA_UPDATECP = 1
Global Const TA_LEFT = 0
Global Const TA_RIGHT = 2
Global Const TA_CENTER = 6
Global Const TA_TOP = 0
Global Const TA_BOTTOM = 8
Global Const TA_BASELINE = 24
Declare Function SetTextAlign Lib "GDI" (ByVal hDC As Integer,
ByVal wFlags As Integer) As Integer
Form_Click:
Lbl$ = "Centered title"
CurrentX = TheForm.ScaleWidth/2
CurrentY = 100 'or whatever...
Result% = SetTextAlign(TheForm.hDC, TA_CENTER + TA_BOTTOM)
Theform.Print Lbl$
'centers output; use other constants for other alignments
(Bob Scherer)
---------------


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.
---------------


Loading Custom Controls:

To save time loading all the custom controls each time you start a project,
have a "master" app with all the VBX's in it. Load it and save it immediately
to a new project name.
---------------


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
PrintForm
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)
---------------


Modal Form Switching Bug:

A couple of people have reported that if you have one modal form call up
another modal form (which then exits back to the original modal form) and
this call-return procedure is repeated several times, you will get an "out
stack space" error message.

The fix is to set focus to a 3rd form's control from either of the two
modal forms so that the two modal forms to not switch focus back and forth.

Set a global variable called gDirection. In the event which calls the 2nd
modal form (like ModalFrm1.cmdModalFrm2_Click), set this variable to "2" and do
a "MainForm.cmdOK.Setfocus" AFTER your "Unload ModalFrm1" command. In the
Gotfocus event of the Mainform.cmdOK, test for the value of gDirection and
respond with either a "ModalFrm2.Show 1" or a "ModalFrm1.Show 1". You can
either unload the 1st modal form when switching between the two, or you can
hide it for faster showing the second time around. If you opt for the hide
method, be sure to explicitly Unload both modal forms in the Mainform.Unload
procedure.

If you do not have that 3rd visible underlying form, then you're out of luck.
(Bob Scherer)
---------------


MsgBox Limitations:

You cannot position a MsgBox at will, nor change its colors, etc. As an
alternative, consider creating your own MsgBox with a small form.
---------------


No DblClick in Directory List Box:

The documentation for the DblClick event suggests that the directory list box
gets this event. This is a documentation error.
---------------


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.
---------------


Toolbar Rearranging:

You can't change the shape/size of the toolbar, but you can force VB to do so!
Move the toolbar down on the screen so that the last row or two of the controls
is below the visible portion. Then exit and restart VB. VB will reconfigure the
toolar for you! (Mark Novisoff)
---------------


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.
---------------


Color:

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


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
list.

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
Click.
---------------


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
LB_FINDSTRING.
---------------


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)

where
Const NULL = 0&
Const WM_SYSCOMMAND = &H112
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 Bug:

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:
Meter1.Refresh
---------------


Sound.DLL:

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
OpenSound;
SetVoiceAccent(1,100,255,S_NORMAL,0);
SetVoiceQueueSize(1,1000);
SetVoiceNote(1,Note,nLength,Cdots);
StartSound;
WaitSoundState(S_QueueEmpty);
CloseSound;
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:

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

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


Passing Records:

Type MyType
A As Integer
B As Long
End Type
Dim Foo(10) As MyType
X = SomeDLLFunction(Foo(1))

The DLL will receive a pointer to element 1 of the array. Because
user-defined types are stored one after the other, the DLL needs to know the
length of each record, which lets it calculate the address of the subsequent
records. (Mark Novisoff)
---------------


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.

Global:
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)
---------------


VBPro MDI Child Creation at Runtime:

The MDI children control arrays can be created at run time. Put the seed on
the form and set the index to 0 and the visible property to false, the
controlname to z and maximize a form.

For i% = 0 To 7000 Step 3000
For j% = 0 To 3800 Step 1900
If i% <> 0 Or j% <> 0 Then
Load z(k%)
z(k%).caption = Str$(k%)
z(k%).left = i%
z(k%).top = j%
z(k%).visible = -1
z(k%).active = -1
k% = k% + 1
End If
Next j%
Next i%
(Robert Eineigl)
---------------


Centering a Form on Screen:

To center a form on screen:

Move (Screen.Width - Width) \ 2, (Screen.Height - Height) \ 2
(Ted Young)
---------------


Controlling Form Size:

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

General_Declarations:
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
Else
OldWidth = Width
End If
If Height < MinHeight Or Height > MaxHeight Then
Height = OldHeight
Else
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-Insert.
---------------


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. Routine 1 (courtesy of Ted Young) only works with
Windows 3.0. Routine 2 (from Mike Mezaros) only works with Windows 3.1.

1. For Windows 3.0 only:
=======================
Global.Bas:
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

2. "Stay on Top" for Win31 only:
===============================
Declare Function 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) As Integer
wFlags = &H2 Or &H1 Or &H40 Or &H10

On_Top% = SetWindowPos(Form1.hWnd, -1, 0, 0, 0, 0, wFlags)
Normal% = SetWindowPos(Form1.hWnd, -2, 0, 0, 0, 0, wFlags)
(Mike Mezaros)
---------------


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.
---------------


Form Wipes, Fades, Etc.:

You cannot "fade out" a form with screen colors, since VB is limited to 16
colors, even in SuperVGA, but you can do wipes and fades. Example:

s = 25 'number of steps to use in the wipe
hx = Form1.Height / s 'size of horizontal steps
wx = Form1.Width / s 'size of vertical steps
For i = 1 To s - 1
Move Left + wx \ 2, Top + hx \ 2, Width - wx, Height - hx
Next
Unload Form1
(Also see Screen Wipes under "Screen".)
---------------


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 make 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 clicked.

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).

When you minimize all the forms, an icon will appear for each one. If you only
want one icon to appear, use the syntax "FormName.Visible = 0" to hide the
others.
---------------


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.
(Dennis L. Harrington)
---------------


Minimizing All Forms At Once:

If you have one main form and several subsidiary ones, you may want to be able
to minimize all the forms at once when the main form is minimized.

The easiest way is to set up a Global flag for keeping track of which forms
are loaded:

Global:
Global Form2Loaded
Global Form3Loaded, etc.

'in each form:
Sub Form2_Load:
Form2Loaded = -1

Sub Form2_UnLoad
Form2Loaded = 0

Sub MainForm_Resize:
If WindowState = 1 then 'if main form minimized
If Form2Loaded then Form2.Visible = 0
If Form3Loaded then Form3.Visible = 0
etc
Else 'main form maximized
If Form2Loaded then Form2.Visible = -1
etc
end if
---------------


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
---------------


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.

Set a flag in the Form_MouseDown event (eg AllowFormMove=TRUE) and store the
mouse position (eg MStartX=X MStartY=Y). Then in the mouse move event: If
AllowFormToMove=TRUE then
Form.Left=Form.Left+(X-MStartX)
Form.Top=Form.Top+(Y-MStartY)
End If

In the Form_MouseUp event reset the flag ( AllowFormToMove=FALSE ).
(Ian Taylor)
---------------


Moving Multiple Forms Together:

If you have a related set of forms and would like to move them all if the user
drag-moves any one of them, it is easy enough to do by setting .Top and .Left
for each form back to their original relative values when you detect a form
has been moved. The problem is detecting when a form has been moved. Here is
a method (suggested by Jonathan Zuck):

Start your program from a Sub_Main and do the checking in there:

Sub_Main:
Load Form1 (and Form2 and Form3, etc)
Do
x = DoEvents()
If Form1.Top <> Form2.Top (etc.) then MoveForms
Loop
End Sub

Sub MoveForms:
(Either use flags to determine which of the three forms has moved,
or check to see which 2 forms are in the same place, which means
the other form is the one that moved, then move the other forms.)
End Sub

The effect is that "Load Form1" starts your program running, but any time
the program is awaiting user action, program flow will be in the Do-Loop
in which the control locations are constantly being checked. The DoEvents
in the loop allows other things to be done in Windows and in your app.
---------------


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 directly, although the MS Professional
Toolkit and other 3rd-party add-on's support it.

One alternative to buying add-on's is to make as many duplicate forms as
you think you might need.

Another alternative is to 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_HWNDNEXT = 2
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)
Do
Style = GetWindowLong(hWndChild, GWL_STYLE)
Style = Style Or WS_CLIPSIBLINGS
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

Form1.Show
Call SetClipSiblingBit(Form1.hWnd)
'get hWnd for the controls:
Grid1.SetFocus
G = GetFocus()
Text1.SetFocus
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.

Q:
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?

A:
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.
---------------


Property Bar Errors:

The height read-out in the gray area on the right side doesn't always
agree with the Height property. To see this problem in action, try this:
1. Create a text box.
2. Set the Height property to 1385 using the Settings box. The height
read-out will now read 1385.
3. Click on the form to unselect the text box.
4. Click on the text box to select it. Now the height read-out says
1335 (it should say 1385).
(John Socha)
---------------


Resizing Forms:

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


ScaleWidth/Height Purpose:

The .Width and .Height properties will give you the external width of the form.
The .ScaleWidth and .ScaleHeight properties give you the *internal* dimensions
of the form. Keep in mind that the .ScaleWidth and .ScaleHeight values are
given in terms of the current .ScaleMode which is usually TWIPS. The good
thing is if you change the .ScaleMode to something else, it won't matter
because the placement of the controls is based on the Form's .ScaleMode. So,
if you want to keep your Picture box at the full area of the form, then
you'd do the following in your Form_Resize event:

Picture1.Move 0, 0, Form.ScaleWidth, Form.ScaleHeight

(Ted Young)
---------------


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:

Q:
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?

A:
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.

---------------


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.
---------------


Metafiles in Place of Fonts:

If you would like to put your name or your company name in an "about" box,
but you don't want to use a VB label (because you can't guarantee the font you
want to use will work on other people's machines), and you don't want to use a
bitmap, or you want to use "curved" lettering but don't have a fancy graphics
program...

Well, if you have Microsoft Word for Windows 2.0, you can use its "Word Art"
and "Microsoft Draw" mini-apps to create metafiles. You can use Word-Art to
create your company/organization name or the program name with fancy features
like curved letters, shadows, "button" styles, etc; and then copy it to the
clipboard and paste it directly into a VB form or picture control at design time.

Best of all, metafiles can be sized from very small to very large without
distortion. (Raymond W. Six)
---------------

TrueType Fonts Don't All Show Up:

Only some of the sizes will be shown in the combo box. But you can click in
the edit part of the combo box and type your own point size. (Mike Mezaros)
---------------


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.
---------------


Calling Form_Paint:

You can call Form_Paint from anywhere within the same form as the _Paint
method. The only thing you have to know, which isn't documented, is that you
have to use the word "Form" and *not* the actual name of your form. I use
Form_Paint all the time. (John Socha)
---------------


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
reason.
---------------


Detecting a Form's Getting Focus:

The following will detect when a form gets focus:

Sub Main
hMyTask = GetCurrentTask()
hOldTask = hMyTask
Form1.Show
Do
Do
Ok = DoEvents()
hTask = GetWindowTask (GetActiveWindow)
Loop Until hTask <> hOldTask
If hTask = hMyTask then
Form1_GotFocus
Else
Form1_LostFocus
End If
hOldTask = hTask
Loop
End
(J.Zuck)
---------------


Flashing a Form:

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

Command1_Click
Randomize
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
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).

Form1_Load:
TitleForm.Show
Do
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.
---------------


Form.GotFocus:

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.

It is reported that during a Windows Termination shutdown of your app, if your
Form_Unload tries to unload other forms, you will get a UAE.

A work-around is to put a timer on all forms that stay loaded other than my
main form. The event for that timer is Form_Unload for the form it is on and
the design time interval is 0 (timer off). The Form_Unload for the main form
can change the timer interval on those forms and unload them without the UAE
while exiting. (Barry Simon)
The file RANDGR.EXE on DL6 shows how to manually scroll data through a Grid.
---------------


What is the Grid Control?:

The Grid Control is like a List Box divided into columns. It is not part of
VB, but is in the Professional Toolkit and other 3rd-party add-on's.
---------------


Alternative to a Grid Control:

For anything other than a very simple grid-type display, it makes more sense
to buy a third-party grid control. The one in VBTools is superior to the one
in the MS Professional Toolkit, but the PTK is cheaper.

For very simple grid-type displays, here is an example of how to make your
own:

Sub Form_Paint ()
Line (100, 100)-(2500, 2500), , B
For XPos = 100 To 2100 Step 400
Line (XPos, 100)-(XPos, 2500)
Next
For YPos = 100 To 2100 Step 400
Line (100, YPos)-(2500, YPos)
Next
End Sub

Sub Command1_Click ()
i$ = InputBox$("Enter X and Y coordinates separated by a space", "")
XCoord% = Val(Left$(i$, 1))
YCoord% = Val(Right$(i$, 1))
XCoord% = 100 + ((XCoord% - 1) * 400)
YCoord% = 100 + ((YCoord% - 1) * 400)
Line (XCoord%, YCoord%)-Step(400, 400), RGB(0, 0, 0), BF
End Sub
(Ian Taylor)
---------------


Can't Enter "&":

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


Graphics in a Grid Cell:

You cannot draw directly to a cell in the PTK's Grid control. Instead, draw
to an invisible Picture box then assign the image to the the Grid cell:

Picture1.Autoredraw = -1
' draw stuff on Picture1
Grid1.Picture = Picture1.Image

The speed issue can be addressed by building the graphs one by one, and
transferring to a piclip control via an ordinary control. Then load the grid
in a loop from the piclip. You redraw the next set while the user sees the
old. THe following loads in 4 seconds:

grid1.rows = 6:grid1.cols = 8
For i = 1 To 6
grid1.rowheight(i - 1) = 1000
Next i
For i = 1 To 8
grid1.colwidth(i - 1) = 1000
Next i
grid1.width = 8.4 * grid1.colwidth(1):grid1.height = 6.4 * grid1.rowheight(1)
picture1.Move form1.width - picture1.width, form1.height - picture1.height,
grid1.colwidth(1), grid1.rowheight(1)
picture1.autoredraw = -1 'THIS IS IMPORTANT. The picture property of
graph1.drawmode = 3 'graph does not contain the bitmap so
picture1.picture = graph1.picture 'use VB and autoredraw to create and
picture1.picture = picture1.image 'then assign to the picture property
For i = 1 To 5
For j = 1 To 7
grid1.row = i
grid1.col = j
grid1.picture = picture1.picture
Next j
Next i
(Robert Eineigl)
---------------


Faster Row Adding/Deleting:

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.
(Jim Dolson)
---------------


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).
---------------


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.

Global.Bas:
Declare Function GetWindowWord Lib "User" (ByVal hWnd As Integer, ByVal
nIndex As Integer) As Integer
Declare Function LoadIcon Lib "User" (ByVal hInstance As Integer, ByVal
lpIconName As Any) As Integer
Declare Function DrawIcon Lib "User" (ByVal hDC As Integer, ByVal X As
Integer, ByVal Y As Integer, ByVal hIcon As Integer) As Integer
Declare Function FindWindow Lib "User" (ByVal lpClassName As Any, ByVal
lpWindowName As Any) As Integer
' Window field offsets for GetWindowLong() and GetWindowWord()
Global Const GWW_HINSTANCE = (-6)

Form1_Declare:
Dim hInstance, handle, HICON As Integer

Form1_Click:
'handle = FindWindow("TheAPP'sClass", "TheApp'sWindowname")
handle = FindWindow("Progman", "Program Manager")
hInstance = GetWindowWord(handle, GWW_HINSTANCE)
Do
HICON = LoadIcon(hInstance, n&)
n& = n& + 1
Loop Until HICON <> 0

Picture1.Autoredraw = -1
If DrawIcon(picture1.hDC, 10, 10, HICON) Then Print "OK"
Picture1.Refresh

Also note that DrawIcon is also in User not GDI.
(Robert Eineigl, MS)
---------------


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)
---------------


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 may be colored in. If you try to do animation with an icon, you will find
that although the background on the icon may be transparent, the Picture
control is not, so you see a block moving across the screen.

To make the background invisible, use IconWorks' "screen inverse" setting and
paint the background. (Marshal Bostwick)
---------------


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)

You can print 3-D or shadowed text on a form as follows:

Picture1.ForeColor = &HC0C0C0 'light gray shadow
Picture1.CurrentX = 70 'vary depth to taste
Picture1.CurrentY = 70
Picture1.Print "This is shadowed or 3D text."
Picture1.ForeColor = &H0& 'black
Picture1.CurrentX = 50 'vary location to taste
Picture1.CurrentY = 50
Picture1.Print "This is shadowed or 3D text."
---------------


Captions, Multi-Line:

You can make a label caption multi-line simply by inserting a CHR$(13):
Label1.Caption = "This is Line 1" + Chr$(13) + "This is Line 2"

This can only be done with code during runtime, not design-time.
During design time, you can force text down to the next line by
inserting enough spaces, but this may not stay the way you want it
on different types of monitors.

This only works with Label boxes, not Button captions; however, you
can easily emulate Button controls with Picture boxes and put pretty
much whatever you want on them. (Nelson Ford)
---------------


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.
---------------


Bitmaps in List Boxes:

To display a bitmap in a list box:

1) Load a bitmap into a picture control, and set it's AutoRedraw to True
2) Set the Left property to a negative number so it resides off the screen.
3) Put the following code in the Form_Paint event sub:


Success = BitBlt (List1.hDC, DestX, DestY, XPixels, YPixels, Picture1.hDC,
0, 0, SRCCOPY)

Where:
List1 is the name of the list box control.
DestX is the X coordinate inside the List box.
DestY is the Y coordinate inside the List box (both X and Y in Pixels)
XPixels is the width (in pixels) of the bitmap
YPixels is the height (in pixels) of the bitmap
Picture1 is the name of the picture control holding the bitmap
0 and 0 are the X/Y coordinates within the picture (0,0)=top/left
SRCCOPY is a windows constant &HCC0020 which tells BitBlt to copy the
bits verbatim.

Here is the syntax for declaring BitBlt in VB:

Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X As I
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
(Carl Franklin)
---------------


Capacities:

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"
Next

It will bomb out at i = 5440
---------------


Clear a List Box:

The following routine will quickly clear a list box:

Global:
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
Const LB_RESETCONTENT = WM_USER + 5

Sub ClearListBox (Ctrl As Control)
hWndOld% = GetFocus()
Ctrl.SetFocus
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
following:

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:

List1.SetFocus
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
box:

Object: (general) Proc: FindItem
Sub FindItem (Lst as Control, a$)
U = Lst.ListCount
L = 0
Do
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
Else
U = I - 1
End If
Loop
(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.

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 ()
Form1.Show
List1.SetFocus
ListHwnd = Getfocus()
End Sub
(Don Funk, MS)

Note that LB_SelectString will NOT get the index of a string specification
if it is more than the first word. (eg: finds "John" but not "John Smith".)
(Robert Werning)
---------------


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
LB_GETITEMDATA message.

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:

Q:
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.

A:
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:

Edit
Cut
Paste
Copy

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

Command1_GotFocus
Command1Focus = -1
---------------


Right Justify "Help":

In Windows 3.0 menu bars, "Help" is right-justified. In past VB-Tips, we had
some complex ways to right-justify your own Help menu heading, but it turns
out that you can do it with one line of code added to the Load procedure:

Help.Caption = Chr$(8) + Help.Caption

This came from a suggestion by Michael Partsch. His suggestion included
setting up all menu headings as an array, but a quick test showed that the
array is not necessary.

It should be noted that the preferred design in Windows 3.1 is NOT to right
justify Help.
---------------


Screen Flicker on Selection:

Q:
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!

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


An Alternative to Picture Boxes:

Picture boxes eat up a lot of system resources. An alternative is to put the
picture directly on the Form rather than using a Picture box. The code is
fairly simple and straightforward, as shown in the example below which is
used to perform animation on the About form in one of my apps:

'put in your Declares:
DefInt A-Z
Declare Function BitBlt Lib "GDI" (ByVal hDestDc, ByVal x, ByVal y,
ByVal nWidth, ByVal nHeight, ByVal hSrcDC, ByVal XSrc, ByVal YSrc,
ByVal dwRop As Long) 'all on one line, of course
Global Const SrcCopy& = &HCC0020

'animation sub:
Fsm = Form1.ScaleMode
Form1.ScaleMode = 3
Picture1.ScaleMode = 3
Picture1.AutoRedraw = -1
SrcCopy& = &HCC0020
For x = Form1.scalewidth To (Form1.scaleleft - Picture1.scalewidth) Step -1
ret = BitBlt(Form1.hdc, x, y, Picture1.Width, Picture1.Height,
Picture1.hdc, 0, 0, SrcCopy&)
Next
Form1.ScaleMode = Fsm

The above simply moves the picture from the right of the form to the left.
"x" and "y" are the locations on the form. Picture1 is located on out of
sight on the form. Multi-cell animation can be done by using multiple
pictures. To place a fixed image on a Form, just eliminate the loop, of
course. (Nelson Ford)

You can also conserve resources by combining pictures into one Picture1
box and BitBlt-ing the desired form to the Form or another Picture box.
The following code by Jeff Simms shows how to use a combined set of 9
pictures as a tool bar from which the user can select by clicking the
mouse:

Picture1 (autoredraw false) stores a bitmap with all the tool icons (9).
Picture2 (autoredraw true), twice the height of the first one, stores a bitmap
with all the Up tool icons on the top half of the bitmap and all the Down tool
icons on the bottom half.

After setting the scalemode for both boxes to pixels, I used the following code
to transfer the DOWN tool icon to Picture1:

Sub Picture1_MouseDown (Button As Integer, Shift As Integer, X As Single,
Y As Single)

N = Int(X / (Picture1.ScaleWidth / 9) + 1) 'icon selected
Z = Picture1.ScaleWidth / 9 'width of icon
L = Z * (N - 1) + N 'X for BitBlt - 1 pixel adjustment per icon
I = BitBlt(Picture1.hdc, L, 0, Z, Picture1.ScaleHeight, Picture2.hdc, =>L,
Picture2.ScaleHeight / 2 + 1, SRCCOPY) '1 pixel adjustment for YSrc

End Sub

You can also use BitBlt with DSTINVERT and just one picture box

You can easily combine graphics into a single Picture box by first laying
them out next to each other on a form using separate Picture boxes, then
use PrintScreen to capture them and Paintbrush to clip and save them.
---------------


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
problems.
---------------


AutoRedraw:

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 ()
ClipBoard.Clear
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:
Global.bas:
(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 A Full Picture:

Q: How can I drag a picture control whole, instead of just the outline, and
still have all the drag-and-drop functions?

A: Sorry, you can't have both. See the program Accordian Solitaire in the file
listing for an example of dragging and dropping.
---------------


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
Source.SetFocus
Child% = GetFocus()
Picture1.SetFocus
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
Source.SetFocus
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 ()
Text1.Drag DRAG_START
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 bitblt 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

The following is some actual code for the above:

1. Picture.AutoRedraw = 0
2. do the .Line statements to draw the stuff
3. Now copy the "screen" image to the AutoRedraw "persistant" image:

hScreenDC = Picture.hDC ' get the device context handle of the "screen"
Picture.AutoRedraw = -1 ' set it to true, so the hDC points to .Image
PrevScaleMode = Picture.ScaleMode
Picture.ScaleMode = 3 ' pixels
r%=BitBlt(Picture.hDC,0,0,Picture.ScaleWidth,Picture.ScaleHeight,
hScreenDC,0,0,SRCCOPY)
Picture.ScaleMode = PrevScaleMode

Make sure to type the "r%=BitBlt..." all on one line, and don't forget the
declaration for it (and the const SRCCOPY) in your Global code module. You can
get those declarations from the WINAPI.TXT (stored as WINAPI.ZIP) if you don't
already have it. Also, if you're using a custom ScaleMode (by setting the
ScaleTop/Left/etc. yourself), then you'll have to somehow convert the
measurements to Pixels yourself because BitBlt _requires_ the parameters to be
in Pixels. (Ted Young)

Other than the above, the only real way to spead things up is to limit the
number of pens that are used (a pen includes the color of the line and the
width) and use API calls to draw the lines. VB does all this dynamically so
it's slower. (J. Zuck)

If you set AutoRedraw = -1, Visual Basic transfers the image to the screen
only during idle periods, so if you're drawing several thousand points, this
can seem quite slow. I have a program that draws 3000 lines in about 3 seconds
on my computer, which seems like a lifetime.

But you can make this "feel" faster with a simple trick. If you call the
Refresh method periodically, you can provide feedback while you're drawing
without slowing down the program much. For example, my program that draws 3000
lines looks something like this:

Dim i As Integer

pic.CurrentX = points(1).x
pic.CurrentY = points(1).y

For i = 2 To numPoints
pic.Line -(points(1).x, points(2).y)
If i Mod 500 = 0 Then
pic.Refresh
End If
Next i

This will feel faster because your picture will be redrawn every 500 points,
or about every 1/2 second (on my 25 MHz 386).

By the way, Polyline is about 5 times faster, but you must work with Pixel

coordinates.

More notes:

1. Calling Refresh rather than DoEvents tells Visual Basic just to transfer
your new image to the screen. It won't process any other events, which can be
good or bad. The good part is that Refresh may be faster. The bad part is that
you can't have a cancel button to stop long drawing operations.

2. Here's how you can set the redraw so it happens after a certain amount of
time has ellapsed (without using a timer object):

Dim t As Single ' Timer returns a Single

t = Timer ' Get current clock ticks
For i = 1 To numPoints ' Draw all the lines
pic.Line ... ' Draw a line
If (Timer - t) > 0.5 Then ' Has 1/2 second passed?
pic.Refresh ' Copy new image to screen
t = Timer ' New starting time
End If
Next i

This example updates your image every half second. As you can see, it's almost
as simple as using a Timer object, but you don't have to call DoEvents.
(John Socha)
---------------


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 Extracted Icons:

Q: I have used the ExtractIcon function from shell.dll (WIN31) to extract
icons from *.EXE and *.DLL's. The icons are displayed in a Picture control. I
can't save the resulting picture. It seems that VB believes the picture
control has a "none" property, perhaps due to the fact the the API calls
filled the control.

A: Put "PicName.Autoredraw = TRUE" and do a "PicName.Picture = PicName.Image"
after you load the icon.
---------------


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
statement.

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
r%=BitBlt(Picture1.hDC,0,0,Picture1.ScaleWidth,Picture1.ScaleHeight,
hScreenDC,0,0,SRCCOPY)
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
---------------


Unloading a Picture:

To unload a picture from a Picture Box:

Picture1.Picture = LoadPicture ("")
(Jonathan Zuck)
---------------


Zoom In/Out On A Picture:

DefInt A-Z
Declare Function StretchBlt% Lib "GDI" (ByVal hDC, ByVal X, ByVal Y,
ByVal nWidth, ByVal nHght, ByVal hSrcDC, ByVal XSrc, ByVal YSrc,
ByVal nSrcWidth, ByVal nSrcHeight, ByVal dwRop&)
Const PIXEL = 3
Const NULL = 0&
Const SRCCOPY = &HCC0020
picture1.ScaleMode = PIXEL
picture1.autoredraw = 0
hDestDC = picture1.hDC
X = 0: Y = 0
nWidth = picture1.scalewidth
nHeight = picture1.scaleheight
hSrcDC = picture1.hDC
XSrc = 0: YSrc = 0
dwRop& = SRCCOPY 'stretches an icon to full size
apierror = StretchBlt(picture1.hDC, 0, 0, picture1.scalewidth,
picture1.scaleheight, picture1.hDC, 0, 0, 32, 32, SRCCOPY)
(Robert Eineigl)
---------------


255-Character Limit?:

Q: I seem to bump up against a 255 byte limit when a textbox on a VB server is
hot-linked to a textbox on a VB client. Am I missing something?

A: Change the Multiline property to True for the text box. Then you can get
more than 255 bytes passed. (Art Baron)
---------------


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.
Text1_KeyUp
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
Loop
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 ()
SomeForm.Show
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
Else
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
Const EM_LIMITTEXT = WM_USER + 21

In your Form_Load procedure:
Form1.Show
MaxLen% = (Whatever number you want)
Text1.SetFocus
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."
Else
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

---------------


Flicker:

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

An alternative is to concatenate the text in a string variable and then
assign the string to the Text box when done. (Mark Novisoff)
---------------


Flashing/Highlighting Text:

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


2.
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
control.

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


Keeping Text Highlighted:

Text in a Text box can be highlighted with SelStart and SelLength or with
the mouse by the user, but it becomes un-highlighted if the Text box loses
focus.

So if you want to display an "are you sure" box before a user deletes
highlighted text, for example, you have to use a round-about method:

1. Make a small form for asking for the confirmation.
2. Use the "floating form" routine in VB-Tips to make the form stay
on top of the original form.
3. Bring the focus back to the original form and Text box and highlight
the selected text again.
4. Set a flag so that no other processing is done until the confirmation
has been done.
5. Wait for the user to click on the "Ok" or "No" (or whatever) button
on the floating form.
6. To the user's eye, it appears that the confirmation box has popped up
while the text has remained highlighted. The user can't tell that
the floating form does not have the focus.
(Nelson Ford)
---------------


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
Text1.SetFocus
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 MAX_CHAR_PER_LINE = 80
Const EM_GETLINE = &H400 + 20

Text1.SetFocus
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

Text1.SetFocus
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
Text1.SetFocus
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
control.

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)
---------------


Tab Key Trapping:

VB intercepts Tab and doesn't pass it to either the KeyPress or KeyDown events.
But if you set the TabStop property on all controls on the form to FALSE, then
suddenly the Tab key is passed through like a normal character. (Brett Foster)
---------------


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.
---------------


Resolution/Accuracy:

The TIMER function provides a "tick" resolution (approx. 1/18th of a second),
but actual mileage may vary due to the way Windows works; ie: if you invoke
the TIMER function, you'll get an accurate reading of the ticks, but if you
use a Timer *control*, you most likely won't get a hit exactly every
TimerInterval milliseconds... (Mark Novisoff)
---------------


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
products.

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:

"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. (Rick Heil)

"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.
(Rick Heil)

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

Other recommended books:

Waite's "Visual Basic How-To", Waite Group Press, ISBN 1-878739-09-3.
for intermediate to advanced

Visual Basic Workshop, MS Press, ISBN 1-55615-386-4. "for getting started"
---------------


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.

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 ordering 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:
PsL Disk #3490

256PICBX (256PBX)
$0
is a DLL with source that displays 256 color bitmaps in a VB Picture box
control. Source for a sample app is included.

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

DSound
$0
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.

Findapp
Obeda, Ed
$0
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
$0
is a DLL that lets you get a list of handles for active, visible windows. Sample
usage code is included.

HotKey.DLL (HKEYVB)
Zuck, Jonathan
$0
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.

HUGEARR.DLL
Schmidt, Stephen
$0
is a DLL which contains functions for creation, maintenance, and deletion of
arrays larger than 64K, with more than 32k elements per dimension.
It also lets you copy contiguous elements from a Huge Array into a specified
part of a VB array and vice-versa. C source for the DLL is included.

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

LZSSLib 1.0
Eschalon Development Inc.
$25
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.

MHelp
MicroHelp, Inc.
$0
is a DLL with VB versions of PEEK, POKE, INP, OUT, VARPTR, VARSEG, VARSEGPTR,
SADD, SSEG and SSEGADD.

SounDLL
White, Bill
$0
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
languages.)

Space & Time (SPCTIME)
Barrett, Paul James
$0
is a DLL that reports free disk space and will copy the date and/or time to the
the Clipboard. C source code for the DLL is included, but no source code to
illustrate calling the DLL from VB, so you will have to study the C code.

Stsbar 2.0
Staffin, Ed
$0-$10
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.

Toolwnd.DLL (TOOLWND)
Donahue, Ray
$35
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.
$0
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.
$0
is the VB source code for a solitaire card game. The uncompiled Help is also
included. Requires VBCards, which is also on this disk.

VBPoint
Zuck, Jonathan
$0
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.

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


DOS Access/Functions:
PsL Disk #3557

DateTime (DATETIM)
$0
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.

Dirscan.BAS
Rose, Joel
$0
contains source code for a routine that scans a system's drives for a particular
file or files.

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

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


Font Utilities:
PsL Disk #3557


EnumFonts (ENUMFNT)
Telelink Systems
$8
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.

Fontro
Young, Ted M.
$0
contains VB source code to create a rotatable font that can print sideways.

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


Networking/Communications:
PsL Disk #3557


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

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

Link'n Load (LINKLOD)
Krumsee, Art
$0
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
$0
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.

Novell-VB (NOVL-VB)
Johnson, L.J.
$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.

VBMate
$0
contains a library of routines and sample code for interfacing VB with EXTRA!
for Windows EHLLAPI.DLL. This gives VB the ability to interact with a 3720
mainframe terminal session.

VBTerm
McGuinness, Charles
$0
is source code for a simple VT100 terminal emulator.
---------------


System:
PsL Disk #3704


Fndwnd
$0
shows how to look for a window based on Caption and/or ClassName.

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

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

LoadTime
$0
is a text file that tells how to handle slow loading programs to keep the user
from messing things up.

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

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

VBMem 1.1 (VBMEM-S)
Snider, Charles K.
$0
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.

VBShell 1.0
Smith, Paul
$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.

WinAPI
Microsoft
$0
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.

WndFind
User Friendly, Inc.
$0
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:
PsL Disk #3704


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

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

GridCal
Meadows/Graves
$0
is source for making a calendar using the Grid custom control.

SmallCal (SMALCAL)
Meadows, Al
$0
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
$0
is VB source for an app to display the date and time in a very small window.

VB Popup Calendar (VBPCAL)
Schueler, Don
$0
is source for a small pop-up calendar. It is intended for popping up in date
fields and returning the value to the field.
---------------


Graphics/Video:
PsL Disk #3788


3D4VB
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.

BMPKit
Campbell, George
$0
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.

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

Icon Browser (ICONBRW)
$0
shows how to more easily extract icons from apps in Windows 3.1.

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

MetaLib 1.0
Sygenex
$0
contains a set of routines that will allow you to create, draw, and save SDK
(vector graphics) metafiles.

Newstart 1.04 (NEWSTRT)
Elkins, Jeff
$0
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
$0
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
$10
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.

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

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

TurboDXF (TURBODX)
Dorosh, David
$50
is a DLL that allows you to create standard DXF graphics files with up to 25
entities per drawing. It works with TP, VB, OV, Excel, C. (Not on this disk.
Ask for disk #8842.)

VBFlip
$0
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
images.

VBMask
$0
demonstrates how to create a mask from an image and a screen object using
BitBlt.

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

VBScroll (VBSCROL)
$0
demonstrates an easy way to scroll/pan across a bitmap in a Picture Box.

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

WMFPix
$0
is a set of graphics icons in WMF format for use in your VB programs.
---------------


Custom Controls:
PsL Disk #3880


CBList 1.0.
Waldmeyer, Manfred
$58
allows you to create checkboxes or radiobuttons as a single custom control for
VB. It supports 2D and 3D display, several standard box types, and bitmaps/icons
picture property. This version works only in design-time.

ClipSib 1.0
Funk, Keith
$0
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.

DropDown (DROPDWN)
$0
shows how to simulate a Combo box with a Text box and a List box.

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

Graphic 1.1
Cramp, Stephen
$15
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.

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

Huge-MC
Marquis Computing
$0
is the VB Source code to a huge array manager for Windows 3.1. You can create
huge arrays of up to 16MB on 386 and 1 MB on 286.

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

MsgBox Editor (MSGBOX)
Buhrer, Richard
$0
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.
$22-$32
is a custom control for displaying the status of a process. Two styles are
offered: thermometer and needle.

Grid Routines (GRIDRTN)
Ford, Nelson ASP
$0
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.

Grid-VB
$0
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
$0
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.

Picnic
Funk, Keith
$0
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.

Prntlbl
$0
shows how to print captions on a Form using API calls instead of Label controls.
The code looks enormously complicated to us, considering that you can do the
same thing with a few straight VB commands, but maybe we are missing something.

PropView (PROPVW)
Microsoft
$0
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
$0
is sample code illustrating the integration of a random access file and a Grid
control.

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

ToolBar
Murphy, Stephen
$0
walks you through creating a toolbar using a picture box for the bar and BMP
files for the buttons. Sample source code and BMP files are included.

VBMeter
$0
is a VBX that lets you create a meter type display with horizontal or vertical
bars.
---------------


General Tools/Reference/Info:
PsL Disk #3968


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

APIFunc
Solutions by Smith
$15
is an API function database in Cardfile format. (Not on this Disk. Ask for
disk #8841.)

APIXref
$0
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
$0
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.

BitMap
Young, Ted
$0
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
$0
is code that shows how to trap both the click and double-click events for a
control.

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

Drag'n'Drop (DRAGDRP)
Bonner, Paul
$0
shows how to add support for the Windows 3.1 File Manager drag-and-drop facility
to a VB program.

Focus U-Turn (FOCUTRN)
$0
demonstrates techniques for the use of Focus, and what to expect when you use
it, particularly as it applies to attempts to verify data on exit from a control
via LostFocus.

FrameVB
Ford, Nelson ASP
$0
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.

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

Hyper (HYPR-VB)
Martin, Wendell
$0
shows how to add hypertext to your VB programs.

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

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

RTFGen 1.0
Baldwin, Dave
$0
converts plain ASCII files into Rich Text Format for use with the Windows Help
Compiler. This is much easier than learning Word, or its equivalent. (Not on
this disk. Ask for disk #8842.)

Setup1
Johnson, L.J.
$0
is an enhanced version of the Setup program that comes with the PTK. (Not on
this disk. Ask for disk #8880.)

VBAPI 0.2
Pruitt, Steve
$0
contains text of the Microsoft WINAPI.TXT file in the Windows .HLP format. It
includes additions and corrections as of March 9, 1992, and contains
definitions of functions, constants, and data structure for use within Visual
Basic.

VB Assistant (VB-ASST)
English, Donald R.
$0
displays a menu that lets you save and run the application you are working on,
invoke an icon editor, or quickly gain access to all help and text files which
relate to Visual Basic. Source code and executable are included.

VBE
Digital Solutions
$15
adds two useful text options to VB. It allows you to save selected text to a
file and to print selected text. These options will appear under the File Menu
in VB.

VBFindID (VBFNDID)
Kitsos, Costas
$0
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
purpose.
---------------


Miscellaneous:
PsL Disk #3969


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

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

Coerce
$0
is a routine to force Currency variable amounts to 0, 1, 2, or 3 decimal digits
of accuracy.

DDE Utility (DDEUTIL)
$0
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
included.

DragForm (DRAGFRM)
Simms, Jeff
$0
demonstrates how to drag a form without a title bar and dragging a textbox using
similar code.

FixMenu
Young, Ted M.
$0
is code that shows how to get/change properties of menu items.

FOpen 1.1
Kitsos, Costas
$0
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.

GPIB
$0
is a sample application that shows how VB can be used as a control and
monitoring software.

LstDrag
Mosher, Sue
$0
is code for dragging a line in a List box to another line with the mouse or
cursor keys.

MultiPik (MULTIPK)
Kitsos, Costas
$0
shows how to create a multiple-selection List box with API calls. This is a
frequently requested feature for VB. Source and demo included.

NCalc
Smaby, Marcus
$0
demonstrates the implementation of a common keyboard handler.

Numbers
Computing, Marquis
$0
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.

PGuide
$0
is the source code for the sample routines in the VB Programmer's Guide.

Profile.BAS
Vance, Rob
is VB code (API calls) that makes it easy to store program data in WIN.INI.

SaveMe
Irwin, Gregg
$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 and take it out before compiling.

Search (SRCH-VB)
$0
contains source code and a sample application for quickly searching a list box.

Shuffler (SHUFLER)
Stine, Brian D.
$0
is VB source code for a utility to shuffle (randomize) sound files assigned to
system events. Requires Win31 and the Professional Tool Kit for VB.

Text Style (TXTYLE)
Irwin, Gregg
$0
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.

VBArray
Kitsos, Costas
shows how to load/save an array of up to 64k in a single operation.

VBMPU Demo (VBMPU)
$0
contains an MPU-401 MIDI card demo and play-only source routines. Dynamic MIDI
channel and velocity change are supported.

VBXlDem
$0
is source code to show how to access an embedded chart in Excel and link it to a
basic picture control.

VB-ISAM
Eibl, Gunter
$0
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.

VqStrings (VQSTRNG)
$0
allows huge variable and fixed length strings for Visual Basic. VStrings
delivers fast access and return error codes compatible with Visual Basic's 'On
Error GoTo'.


#4062 Miscellaneous, continued

ComServ
Buck, Alan
$0
is the source code for a VB application for keeping track of Zip files. Studying
other apps is a good way to learn.

GPMVB1
$0
is source that shows how to use VB to create and edit a form-based document in
Microsoft Word for Windows.

GMPVB2
$0
shows how to use DDE to get and manipulate data from Word for Windows.

MMPVB
Turpin, Jerry
$0
shows how to use the MultiMedia Extensions to play MID, WAV, and MMM files.

VBInst 1.00
Kallonen, Jari
$0
is the source code for a VB app to install a program from a floppy to a
specified subdirectory. It can also create a Program Manager group.

Winstall.Frm
$0
is source code for a program to find and run installation programs on program
disks.
---------------


Database, Data Entry, Sorting:
PsL Disk #4062


BtrTest
Meyer, David
$0
is source code for a Btrieve for Windows sample program.

CallBT
Kubelka, Bob
$0
contains a function which simplifies using Btrieve with Visual Basic. Requires
Btrieve for Windows.

EditDem
Milligan, Keith
$0
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.

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

SQL Query (SQLQRY)
$0
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.

SQLGrid
Hyland, Bob
$0
shows how to use a GRID control (on disk #3880) with the VBQSL SQL Server (from
Microsoft).

VBDB
$0
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.
$0
is a set of declarations that create an interface to the Paradox Engine. It will
translate between VB and Paradox data types.

VBSort
Ford, Nelson
$0
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.

WinBtrv
Novell
$0
is a DLL to allow using Btrieve in VB applications.

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


Printing:
#4226


Code.Print 1.6a (CODEPRN)
Caladonia Systems, Inc.
$35
lets you print your VB source code with a wide range of options, including
titles, headers, footers, project name, font control, user-defined pagination,
margins and line spacing, the ability to print entire applications or single
modules or routines; to sort source code by subroutine; to serialize backups of
source code files and to print an index of functions and subroutines.

Endprn
$0
is source code for a small utility for cancelling a print job.

HPEnv 2.0 (HPENV-S)
Himowitz, Michael J.
$0
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.

HPESC
O'Rourke, Peter
$0
explains how to send ESCape sequences from Visual Basic to an HPIII printer.

PrSetup
Schwartz, Corey
$0
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.

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

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

PSOut
Cramp, Stephen
$0
illustrates a method of printing bitmap files up to 32k in size. It has been
tested on PostScript and HP LaserJet printers only.

PWrap
Wagner, Richard
$0
is a routine for sending text to the printer with word wrap at word breaks, as
delimited by spaces.

VBBook (VBBOOKS)
Scott, Dennis
$0
is source code for an app that lets you print ASCII text files to an HPLJ
printer in booklet format.

VBPDemo
Obeda, Ed
$0
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
$0
demonstrates how to put a laser printer into Landscape mode and how to select a
paper bin without going through the Windows print manager.
---------------


Other:

(The programs in this section are large enough to be on disks by themselves.
Their disks numbers are indicated below. The dollar amounts shown are for
registering these shareware programs with their authors. PsL charges $5/disk.)


BasicBasic
Davidsaver, Mark
$15
#4204
lets you compile BASIC programs for DOS or Windows. Not all "standard" BASIC
commands are supported. This is a very easy way to develop small programs
without regard for which environment they will run under. Several sample files
are included for compiling, and ASCII source code is accepted.


Quick and Dirty Help
Allen, Phil
$20
#4142
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
required. (Also see WI-01: #3477, Xantippe, which can create RTF files very
easily.)


vxBase
Comsoft Inc.
$50
#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.
---------------


PsL's July 1992 Visual Basic Disk:

This disk contains new programs being added to PsL's Visual Basic section.

Btcreate (BTCREAT)
VB: Database/Data
J. Highsmith; $0
is sample source for the use of Btrieve create in VB. Requires
the Btrieve for Windows DLL.

CopyFix
VB: System
Dan Smith; $0
explains how to fix a bug in the CopyFile function in the Setup
Kit of the PTK.

ExWait
VB: System
$0
demonstates how to use the Windows GetModuleUsage function to
cause VB to wait until a SHELL command has finished.

IcoXtrc
VB: Icons
$0
is a routine for extracting icons from executables and DLLs.

Ini_Mod1 (INIMOD)
VB: INI's
Forrest Berry; $0
is a VB module to create, read and update private
INI files. You only need to add it to your project and call it.
Sample code is inlcuded.

INotepad 1.0 (INOTPAD)
Text Editors
Gordon Smith; $0
is a Windows Notepad look-alike with many improvements, including
font selection, word wrap, macros, and the ability to strip carriage
returns and linefeeds from text. VBRUN is required.

MenuOn
VB: Menu Bar
$0
shows how to disable and then enable the entire Menu Bar of a Form
quickly and without flicker.

Modalex
VB: DLLs
$0
is a DLL that makes a VB app appear modal to the calling app.

Modified Setup1 (MODIFVB)
VB: Custom Controls
L.J. Johnson; $0
adds new features to the PTK Setup program. It provides options
to do a Full Installation or Update Files, and allows users to install
in either a new program group or an existing group. Other features
include many bargraph improvements, 3-D controls, automatic disk space
calculation, and much more.

Pict_t
VB: Printing
George F. Santamarina; $0
demonstrates how to print bitmaps using Windows 3.1 Common Dialog
routines.

Print Clipboard (PRINTCB)
VB: Printing
Clifford P. Barnett; $0
contains source code and an executable for printing the contents
of the Windows Clipboard. Text and graphic dumps are both supported.

Prnset (PRNSTVB)
VB: DLL
$0
is a DLL that allows changes to the default printer settings.

Shades
VB: Graphics
Brian Stine; $0
show how to give your forms a "brushed steel" background like
the Borland custom controls.

VBClean
VB: Tools
David Hite; $0
is source for an automatic save/load procedure needed to shrink
VB executables.

VBClock 1.1 (VBCLK)
VB: Date/Time
Sarah Holland; $0
contains source code and an executable for a Win31 utility that
shows the date, time, and the percentage of free system resources
remaining. Options include background/foreground color selection,
font selection, alarms, and more.

VBMacro
VB: Tools
George Campbell; $0
is a group of macros for the Windows macro recorder that can
save you a lot of keystrokes when writing code in VB.

WinkDemo (WNKDEMO)
VB: Graphics
George Campbell; $0
is source that illustrates how to provide an animated screen
while your VB application is loading.







 December 6, 2017  Add comments

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)