home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Supreme Volume 6 #1
/
swsii.zip
/
swsii
/
201
/
BCC101.ZIP
/
BCC101.NWS
< prev
next >
Wrap
Text File
|
1993-04-20
|
98KB
|
2,343 lines
Volume 1, Number 1 April 20, 1993
**************************************************
* *
* The BASIC Code Cache *
* *
* International BASIC Electronic *
* Newsletter *
* *
* Dedicated to promoting BASIC around *
* the world *
* *
**************************************************
The BASIC Code Cache is an electronic newsletter published by
Communication Techniques. The BASIC Code Cache may be freely
distributed provided NO CHARGE is charged for distribution. The BASIC
Code Cache is copyrighted in full by Communication Techniques and
republication of any material contained in The BASIC Code Cache is the
prohibited without the consent of Communication Techniques or the
author of the material. The BASIC Code Cache must be distributed whole
and unmodified.
You can write The BASIC Code Cache at:
The BASIC Code Cache
P.O. Box 507
Sandy Hook, CT 06482
Copyright (c) 1993 by Communication Techniques.
The BASIC Code Cache Page i
Volume 1, Number 1 April 20, 1993
----------------------------------------------------------------------
T A B L E O F C O N T E N T S
1. From The Editor
Welcome to The BASIC Code Cache .............................. 1
Subscribing to The BASIC Code Cache .......................... 3
2. COMMON SHARED
A Hexidecimal Primer by Earl Montgomery ...................... 4
Learning Assembler by Brian Mclaughlin ....................... 6
3. Quick BASIC/PDS
Detecting if Windows is Running by John Riherd ............... 7
A Fast Windowed Print Library by Brian Mclaughlin ............ 9
Power Menus, Prompts and Messages By Larry Stone ............. 12
A Quick Method of Changing Colors by Lee Herron .............. 15
4. PowerBASIC
PowerBASIC 3.0 Adds Power to BASIC by Bruce Tonkin ........... 17
Getting Netware Connection Information by Lewis Balentine .... 20
5. Visual Basic
SpyWorks-VB from Desaware .................................... 27
Tom Hanlin Checks in with PBClone for VB ..................... 29
6. Access Basic
Creating Custom Wizards with Access Basic .................... 31
7. The Bug List
Buglist for Microsoft Visual Basic ........................... 36
The BASIC Code Cache Page ii
Volume 1, Number 1 April 20, 1993
----------------------------------------------------------------------
F r o m T h e E d i t o r
----------------------------------------------------------------------
Welcome to The BASIC Code Cache
Welcome to the premiere issue of The BASIC Code Cache. For those of
you familiar with The QBNews, you can think of this as version 2.0 of
that. When The QBNews first started out, Microsoft QuickBASIC and PDS
were the king of the hill. However, during the three years that The
QBNews was published, the state of BASIC programming has changed
radically.
Microsoft Visual Basic for Windows landed on the scene a short while
ago and took the Windows development market by storm. Hey, any BASIC
product that can get C programmers to use it must be pretty special.
Also more recently, QuickBASIC has gotten some pretty serious
competition with the release of PowerBASIC 3.0 by Spectra Publishing.
Because of these radical changes in the marketplace, The QBNews has
become The BASIC Code Cache. What this means to previous readers of
The QBNews is that we will now cover all aspects of BASIC programming
in the PC world instead of strictly QuickBASIC.
I'm sure some of you are saying "Great, YAWR (Yet Another Windows
Rag)". I can assure you that will not be the case. DOS is still very
important out there, and with the arrival of PowerBASIC 3.0, there is
some new excitement out there too. However, just because you program
in a different environment, I hope you don't quickly dismiss the
articles and code that don't pertain to you. After all, this is BASIC
and you can probably learn something new regardless of what
environment you program for.
For those of you who are new to The BASIC Code Cache, here comes the
part where I plead for your support to sustain this newsletter. The
BASIC Code Cache is made up strictly of contributions from its
readership. Over the course of The QBNews, many programmers have
contributed a ton of programming knowledge and has made The QBNews an
encyclopedia of information on programming in QuickBASIC. Many times
if you have a question on how to do something in QuickBASIC, you can
find the answer in The QBnews. Now that we have expanded the scope of
BASIC programming in The BASIC Code Cache, I'm sure there is a wealth
of programming knowledge out there that can be shared. There are three
ways to support The BASIC Code Cache.
1. Submit well commented code - Part of the problem I had with
sustaining The QBNews was not a lack of code, but rather a requirement
I had about including an article. No more with The BASIC Code Cache.
If you have some interesting code you would like to contribute, but
are worried about your writing skills, we will gladly except well
commented code. For your troubles you will receive the disk based
version of The BASIC Code Cache your code appears in.
2. Submit an article - One of the biggest requests I got was from
beginners who wanted more information tailored for beginners. This was
The BASIC Code Cache Page 1
Volume 1, Number 1 April 20, 1993
always hard to come by because advanced users may not know what stuff
beginners are interested in learning. What may be common knowledge to
an advanced user may be the answer to a problem for a beginner. So, if
you are a beginner and you learn something new that you didn't know
about, write it up and share it with other beginners. After all, if it
wasn't common knowledge to you, it probably isn't common knowledge to
everybody else either. You will also receive the disk based version of
The BASIC Code Cache for your troubles.
3. Subscribe to The BASIC Code Cache - The BASIC Code Cache offers
disk subscriptions for those who are interested. By subscribing, you
also get some bonus goodies not available in the electronic version.
For instance, the disk based edition of this issue includes a working
demo version of PowerBASIC 3.0, a demo of SpyWorks VB, and a full
version of Tom Hanlin's entry into the VB shareware market PBCWIN. The
cost of downloading these from Compuserve or long distance will more
than cover the cost of the subscription, and you will also be
supporting the continuation of The BASIC Code Cache to boot. Detailed
info on subscribing follows this section.
If the rest of the BASIC community supports The BASIC Code Cache like
QuickBASIC programmers supported The QBNews, we can look forward to
many years of outstanding contributions of BASIC knowledge being
spread around, making everybody better BASIC programmers. I look
forward to your comments, contributions, and criticisms and can be
reached at the addresses below.
US Mail: The BASIC Code Cache
P.O. Box 507
Sandy Hook, CT 06482
Compuserve: 76510,1725
Fidonet: 1:141/777
Prodigy: HSRW18A
Internet: dave.cleary@f777.n141.z1.fidonet.org
The BASIC Code Cache Page 2
Volume 1, Number 1 April 20, 1993
Subscribing to The BASIC Code Cache
Show you support of The BASIC Code Cache by subscribing. The disk
based version of The BASIC Code Cache contains extra goodies not
available in the electronic version. It will come to you on two 360k
floppy disks or one 720k floppy disk. Extras include demos of
commercial software reviewed, the latest shareware for BASIC plus much
more. It costs only $24 for a one year subscription.
Name ____________________________________
Company _________________________________
Address _________________________________
City ____________ State _____ Zip _______
___ 360k ___ 720k
Orders outside of the United States please add $10 shipping and
handling.
SPECIAL OFFER FOR THE QBNEWS!!!!
For a limited time, you can receive the entire three disk collection
of The QBNews for just $10. That is twelve issues in all of the best
BASIC has to offer. Almost every topic of programming with QuickBASIC
is touched upon and is an invaluable reference for any programmer.
The BASIC Code Cache Page 3
Volume 1, Number 1 April 20, 1993
----------------------------------------------------------------------
C O M M O N S H A R E D
----------------------------------------------------------------------
A Hexidecimal Primer by Earl Montgomery
While refreshing my memory on Hexadecimal and Binary notations
(and the use of the logical operators <AND> <OR>, I found that
it helped to take notes. This is a compilation of those notes.
Some phrasing is entirely my own. So if you find any mistakes
(other than grammar, sentence structure, or spelling! <smile>)
please inform me so I can correct my document file. This
document should prove useful to the new programmers.
A little info on Hexadecimal notation.
Hexadecimal is a numbering system based on 16 elements.
Digits are numbered 0 through F as follows:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
Representing the digits 0 through 15.
Hexadecimal system uses powers of 16. For example:
&H19 (hexadecimal) represents 25 decimal. Let's see why.
Starting from the right the power is 1. Therefore 9 X 1 = 9
Moving left the next is power is 16. Therefore 1 X 16 = 16
And 9 + 16 = 25.
Let's try another. This time &HFF (hexadecimal)
Again starting from the right (F=15) 15 X 1= 15
Moving left 15 X 16 = 240.
And 240 + 15 = 255
So far we have looked at single byte hex values. Let's look at
a 16 bit hex value. Remember 8 bits to the byte? Now we look at
two adjoining bytes, or sixteen bits.
&H1902 (hexadecimal) represents 6402 decimal.
Again starting from the right 2 X 1= 2
Moving left 0 X 16 = 0
Moving left again <16 X 16 =256> 9 X 256 = 2304
Moving left again <16 X 256 = 4096> 1 X 4096 = 4096
And 2 + 0 + 2304 + 4096 = 6402
In basic hex values are preceeded by &H. For example &HFF or &H1902.
In basic the decimal value of a hexadecimal number can be determined
by the command PRINT &H(hexadecimal number): Example PRINT &HFF.
The printed result in this case would be 255.
Decimal to hex is accomplished by PRINT HEX$(255)
The printed result in this case would be &HFF.
Now let's look at the individual bits within a byte.
Remember there are 8 bits to the byte and reading from Right
The BASIC Code Cache Page 4
Volume 1, Number 1 April 20, 1993
to Left they increase in power by the power of 2.
Binary notation reflects set bits with a 1. Non set bits with a 0.
Let's look at one.
1 0 0 0 1 0 0 1 (This byte represents 137)
As with hex we start at the right.
1 X 1= 1
Moving left 2 X 0 = 0
Moving left again 4 X 0 = 0
Moving left again 8 X 1 = 8
Moving left again 16 X 0 = 0
Moving left again 32 X 0 = 0
Moving left again 64 X 0 = 0
Moving left again 128 X 1 = 128
And 1 + 0 + 0 + 8 + 0 + 0 + 0 + 128 = 137
Logical Operators <AND> <OR>
When you AND two numbers the results are in a new third number.
For example:
a = 137
b = 105
c = a AND b
1 0 0 0 1 0 0 1 This represents A <137>
0 1 1 0 1 0 0 1 This represents B <105>
0 0 0 0 1 0 0 1 This represents C <9>
What occurs when you AND two numbers is that the computer compares
the bits within the first number against the bits in the second
number. If the bits are the same then they will remain the same in
the third number. If they are not the same then the equivalent
bits will be 0 in the third number.
Logical OR
What occurs when you OR two numbers is that the computer compares
the bits within the first number against the bits in the second number.
If the bits are the same they will remain the same in the third number.
If they are not the same then the bits will be 1 in the third number.
For example:
A=122
B=15
C=A OR B
0 1 1 1 1 0 1 0 This represents A <122>
0 0 0 0 1 1 1 1 This represents B <15>
0 1 1 1 1 1 1 1 This represents C <127>
This quick little reference document composed by Earl Montgomery
I hope it proves to be of some use to you.
The BASIC Code Cache Page 5
Volume 1, Number 1 April 20, 1993
Learning Assembler by Brian Mclaughlin
My first suggestion if you want to learn to write ASM routines for
BASIC is to get some good reference material. The best tutorial
available can be found in the last 100 pages of "BASIC Techniques and
Utilities" by Ethan Winer. It's aimed directly at those who know BASIC
already and want to learn assembly language. The book costs $30, and
even if you never get the hang of assembler, you'll get your money's
worth from the rest of the book.
Next, get a good reference on DOS/BIOS interrupts. I have heard
excellent things about the interrupt list put out by Ralf Brown, and
available on many BBSes. I use QUE's "DOS Programmer's Reference" and
can also recommend it.
Don't forget the manual/quick reference guide that comes with your
assembler. When I get error messages, this is where I turn.
Last, once you have a bit of success and are eager for more I suggest
you register for one of the shareware libraries that offers source
code to registered users. Having some code that works(!) which you can
look at is a great way to learn, IMHO. Until then, you can look at
XPRINT.ASM. :->
If you get discouraged, take heart; a year ago I couldn't write a
five-line procedure in ASM without an error message!
The BASIC Code Cache Page 6
Volume 1, Number 1 April 20, 1993
----------------------------------------------------------------------
Q u i c k B A S I C / P D S
----------------------------------------------------------------------
Detecting if Windows is Running by John Riherd
Sometimes you just need to know if your program is running in a DOS
box under Windows. This code will not only tell you this, but will
also tell you what version and what mode Windows is operating in.
CHECKWIN.BAS can be found in the file QBCODE.ZIP.
'*********************************************************************
'* CHEKWIN.BAS *
'* *
'* This program will determine whether Windows is running and, if *
'* so, its operating mode. *
'* QuickBASIC must be loaded, as follows: QB /L QB *
'* Copyright 1993 by John Riherd *
'*********************************************************************
DEFINT A-Z
DECLARE FUNCTION ChekWindows% (Ver1 AS INTEGER, Ver2 AS INTEGER)
'$INCLUDE: 'Qb.bi' 'Include file for interrupt calls
CONST MULTIPLEX = &H2F 'multiplex interrupt number
CONST NOWIN = &H0 'Return for no Windows
CONST WIN386 = &H1 'Return for Windows-386 V2.?
CONST REAL = &H81 'Return for Windows in real mode
CONST STANDARD = &H82 'Return for Windows in standard mode
CONST ENHANCED = &H83 'Return fo Windows in enhanced mode
DIM WellIsIt AS INTEGER
DIM Ver1 AS INTEGER 'Primary version number
DIM Ver2 AS INTEGER 'Secondary version number
WellIsIt = ChekWindows%(Ver1, Ver2) 'Do the test and return values
SELECT CASE WellIsIt
CASE NOWIN
PRINT "Windows is not running"
CASE WIN386
PRINT "Windows-386 V 2.? is running"
CASE REAL
PRINT "Windows is running in real mode"
CASE STANDARD
PRINT "Windows is running in standard mode"
CASE ENHANCED
PRINT "Windows Vers. "; LTRIM$(STR$(Ver1)); ".";
PRINT LTRIM$(STR$(Ver2)); " is running in enhanced mode"
END SELECT
FUNCTION ChekWindows% (Ver1 AS INTEGER, Ver2 AS INTEGER)
DIM Regs AS RegTypeX 'Processor registers for interrupt call
The BASIC Code Cache Page 7
Volume 1, Number 1 April 20, 1993
DIM VBuffer AS INTEGER 'To store version number
Ver1 = 0 'Initialize version numbers
Ver2 = 0
Regs.ax = &H1600 'Function number
CALL INTERRUPTX(MULTIPLEX, Regs, Regs)
VBuffer = Regs.ax 'Set regs.AX
SELECT CASE VBuffer MOD 256 'Get the low byte of AX
CASE &H1, &HFF 'Windows-386 running
Ver1 = 2 'Primary version
Ver2 = 0 'Secondary version is unavailable
ChekWindows% = WIN386
CASE &H0, &H80
Regs.ax = &H4680 'Check for real or standard mode
CALL INTERRUPTX(MULTIPLEX, Regs, Regs)
IF (Regs.ax MOD 256) = &H80 THEN 'Check if Windows is running
ChekWindows% = NOWIN 'No
ELSE 'Check for real or standard mode
Regs.ax = &H1605 'Set registers and function
Regs.bx = &H0
Regs.si = &H0
Regs.cx = &H0
Regs.es = &H0
Regs.ds = &H0
Regs.dx = &H1
CALL INTERRUPTX(MULTIPLEX, Regs, Regs)
IF Regs.cx = &H0 THEN 'Check for real mode
ChekWindows% = REAL
ELSE 'Windows in standard mode
ChekWindows% = STANDARD
END IF
END IF
CASE ELSE
Ver1 = VBuffer AND &HF 'Get the low byte for primary version
Ver2 = VBuffer \ 256 'Get the high byte for second. version
ChekWindows% = ENHANCED
END SELECT
END FUNCTION
The BASIC Code Cache Page 8
Volume 1, Number 1 April 20, 1993
A Fast Windowed Print Library by Brian Mclaughlin
Here is some assembly language code I've been toying with for a while
-- a set of fast-printing routines for QuickBASIC 4.x (and P.D.Q.). I
wrote these because at the time, P.D.Q. did not support VIEW PRINT,
and by design, outputs all text through DOS instead of printing them
directly to video memory.
My answer was to write a group of assembler routines that are (almost)
as easy to use as regular QB. These routines do direct-to-screen
writing, update the cursor, scroll automatically, allow you to set or
change the default colors, and let you set a VIEW PRINT window with
parameters for width as well as height. They require almost exactly
the same parameters as their QB counterparts (they almost have the
same names, even!) and in many programs they will result in a smaller
EXE than if you used QB's display commands. Admittedly, they will
make for a bigger EXE than straight P.D.Q., but the price is fairly
small and the extra convenience is tremendous.
Here's a list of the routines:
DECLARE SUB XInit (IgnoreSnow%)
DECLARE SUB XPrint (PrintThis$, NumberOfScrolls%)
DECLARE SUB XCls ()
DECLARE SUB XColor (FG%, BG%)
DECLARE SUB XLocate (Row%, Col%)
DECLARE SUB XCursorOn (BlockOrLine%) 'non-zero value for block cursor
DECLARE SUB XCursorOff ()
DECLARE SUB XViewPort (TopRow%, LeftCol%, BotRow%, RightCol%)
DECLARE FUNCTION XCsrLin% ()
DECLARE FUNCTION XPos% ()
These routines do have some limitations and drawbacks. They ONLY work
with near strings. Far strings will choke'em. Plus, you can't do the
equivalent of PRINT Num%, instead you must use the equivalent of PRINT
STR$(Num%). Because STR$() pulls the entire floating-point emulation
library into your EXE this can be considered a genuine drawback.
(Fortunately, there are ways to get around this one.)
There is no support for 43/50 line mode. You can only use a 25x80
screen size. Page zero is the only display page supported. You may
change the shape of the cursor, but your only choices are a block or
an underline or turning it off altogether. There are other quirks. I
have tried to identify every quirk, feature and point of interest
about how the routines work in the comment header for the code.
The internal variables used by XPrint use 14 bytes in DGROUP. The
routines all assemble into a single OBJ file, so calling even one of
them will link ALL of them into your EXE file. However, if you can
assemble the source code, you can easily remove any unneeded routines
from the source and reassemble only the routines you want or need.
If you need capabilities not included in XPrint, use straight QB, or
better yet, add them to XPrint!
The BASIC Code Cache Page 9
Volume 1, Number 1 April 20, 1993
I have tried to design XPrint to allow the most painless possible
conversion from normal BASIC syntax. Here are some samples:
In QB: PRINT X$ 'print, scroll once
In XP: XPRINT X$, 1 'same
In QB: PRINT X$; 'print, don't scroll
In XP: XPRINT X$, 0 'same
In QB: PRINT: PRINT: PRINT 'scroll 3 times, no printing
In XP: XPRINT "", 3 'same
In QB: LOCATE , B% 'old row, new column
In XP: XLOCATE XCSRLIN%, B% 'same
In QB: LOCATE , , 0 'cursor off
In XP: XCURSOROFF 'same
I know very well that I can't instruct the gurus here, but not
everyone is a guru. (Look at me!) And I would like to share around a
bit of what I've learned with other folks like me, who haven't been at
this so very long.
I did one thing in XPrint that was new to me. XPrint has multiple
procedures in one object module, some of which are NEAR for internal
use and some of which are FAR, to be called from BASIC. In the past
when I wrote something like this I dropped the BASIC option from the
.MODEL MEDIUM directive, to avoid making all procedures PUBLIC. That
stopped the possibility of accidentally CALLing a NEAR procedure from
BASIC, but it made for more work.
Writing XPrint I stumbled onto a way to be lazy and use the BASIC
directive with a clear conscience, even when mixing NEAR and FAR
procedures. I just used a leading "$" character in the names of all
the procedures I declared NEAR. Because such names are illegal in
BASIC, I figure this should pretty much kill the problem of
accidentally referencing a NEAR procedure.
I did another thing new to me. On 386s and above the MUL and DIV were
speeded up considerably, but I work and play on an XT clone with an
8088, so I always try to shift bits instead. In XPrint I finally got
around to using multiple shifts and adds to achieve what can't be had
by shifts alone. In $SetVideoOffset, I needed to multiply by 160.
First I shift-multiplied by 128, then by 32, and added. Imagine my
childish delight!
I almost blush to point this out, but this same technique can be used
as a fast way to multiply by ten (X*8 + X*2). I hope this tip helps
someone who hadn't heard it yet.
The last thing I'll mention today isn't exactly a matter of code, so
much as a matter of design. In the XInit procedure I added a way to
specify that snow-suppression be ignored on CGAs. It is my fervent
The BASIC Code Cache Page 10
Volume 1, Number 1 April 20, 1993
wish that this feature will be tied in to a command line option for
end-users of the programs you write. I strongly believe in giving
end-users a chance to escape from default behavior! We programmers
don't always know best!
The assembly language source code for XPrint and XPRINT.OBJ are
contained in the file XPRINT.ZIP. Please consult the comments section
at the head of it. When I was writing XPrint I dug out the FastPrint
routine by Christy Gemmel in the QBNews. and studied its design. I
would never have learned assembly language for QB without similar
help. Perhaps, this will slightly repay those who have helped me.
Lastly, feel free to contact me on the BAS_NET ASM for QB echo about
any bugs or weird behaviors you find in XPrint. I ain't poifect,
y'know. In the next issue, we will modify XPrint to work with far
strings!
The BASIC Code Cache Page 11
Volume 1, Number 1 April 20, 1993
Power Menus, Prompts and Messages By Larry Stone
While building a set of routines for the students of my 'C' course
(forgive me, I enjoy programming in 'C' as much as I enjoy QB), I
developed a fairly efficient routine for word-wrapping text within a
boxed frame. Well, I was so impressed with the speed and versatility
of my creation that I immediately coded it for QB.
The routine, BoxDialog(), has six arguments in its parameter list:
BoxDialog Top%, BoxWidth, Border$, Msg$, MsgClr%, Shadow%
"Top" is an integer defining the top row for the boxed display.
"BoxWidth", another integer, defines the width of the displayed box.
"Msg$" is the string of text to word-wrap inside the display box.
"MsgClr" is the color attribute for the text and box frame and,
"Shadow" is true or false for using a shadow around the box frame.
"Border$" is a string of eight characters defining the upper-left
corner, top-side, upper-right corner, left-side, right-side, lower-
left corner, bottom-side and lower-right corner of the box,
respectively.
BoxDialog() automatically frames, word-wraps and centers your text
using the corner and side characters as defined in Border$. Further,
BoxDialog() knows if your monitor is in a 40 column or 80 column
display mode and centers your message accordingly.
Also, BoxDialog() can be instructed to automatically shadow the boxed
message. When Shadow is true, BoxDialog() makes calls to DoShadow().
DoShadow() PEEKs into the appropriate video map at the row, col
location for the color attribute at that location. Then DoShadow()
calculates the attribute needed for a shadow effect at that location.
Finally, DoShadow() sets that new attribute into the video map.
BoxDialog() does not use BASIC's PRINT statement. Instead, it makes
calls to WriteStr(). WriteStr() uses four arguments in its parameter
list: A$, the string to write; row and col, integer location for the
displayed text; and, attr, the color attribute to use.
Note that BoxDialog's MsgClr and WriteStr's attr arguments are color
attributes as used by DOS. In BASIC, for an intense white foreground
and a red background, you would use the command, "COLOR 15, 4". DOS
uses the attribute 79. Because you may prefer to think of color in
the BASIC scheme of things, DIALOG.BAS includes the small function,
ClrAttr%. You can use this function like any other function:
attribute% = ClrAttr%(foreground%, background%)
ClrAttr%() is extremely fast and efficient and does not use any float-
ing point functions (in fact, none of the DIALOG.BAS routines use the
floating point library). ClrAttr() has one line of code:
The BASIC Code Cache Page 12
Volume 1, Number 1 April 20, 1993
ClrAttr% = (fg AND 15) OR (16 * bg - (128 * (fg > 15)))
The code above does not perform any multiplication. The instructions
16 * bg and 128 * (fg...) use even multiples of 2. As, consequence,
the BASIC compiler compiles this statement as a speedy shift left 4
and shift left 7 instruction. The instruction (fg > 15) is a Boolean
test that evaluates as either -1 (true) or 0 (false).
The heart of all of these routines is VidInfo(). VidInfo uses QB's
INTERRUPT routine. The AX register is set to &HF00 and the interrupt
used is &H10. This is a query to extract the current video mode from
the ROM BIOS. Armed with this information, VidInfo() determines your
systems current video map address via a SELECT CASE Vmode. Next, this
routine PEEKs into low memory to find out how many rows and columns
you system is currently set for.
You might want to add two more pieces of information to the CASEs -
the memory required to save a screen and whether the particular screen
is interleaved or not. In any case, VidInfo() passes four pieces of
information via a COMMON SHARED block named, /VInfo/. This means that
whenever you make a call to VidInfo(), Vmode%, VidSeg%, MonRows% and
MonCols% are immediately known to all of the routines in DIALOG.BAS,
as well as, any other modules that '#INCLUDE: 'DIALOG.BI'.
BoxDialog(), DoShadow() and WriteStr() will automatically initialize
with a call to VidInfo() if you haven't already made this call. This
is because these routines need to know, at minimum, the number or
columns used by your monitor. WriteStr(), for example, has the code:
offset = MonCols * 2 * (row - 1) + col * 2 - 2
Speaking of WriteStr(), if you are using QBX or compiling with BC7
using far strings then this routine won't work as written. You need
to use SSEG in lieu of VARSEG. Also, although the routine will not
be fast within the QB environment, compiled, it's lightning. In fact,
compiled and run on an older CGA system will guarantee snow. This is
because WriteStr() does not pause for the monitor's retrace.
I could ramble on and on about every nuance of the code but the code
is rather well commented and speaks for itself. So, the best thing to
say is compile it into an exe file and run it - the test driver should
give you plenty of ideas on how to use it.
To compile -> BC dialog /o;
BC testdlog /o;
To link -> LINK /e testdlog + dialog, , nul, qb;
Now type, "testdlog" to see the action.
Two things you could do to enhance BoxDialog(). One would be an
additional argument to force a upper-left column location. This would
allow you to use BoxDialog() for pull-down menus (at present, you can
us it for menus but the menu is always centered on the screen).
The BASIC Code Cache Page 13
Volume 1, Number 1 April 20, 1993
The other thing you could do to enhance BoxDialog() is the inclusion
of another argument to allow the text to have a different color than
the box frame.
======================================================================
Larry Stone is the author of ORCA Pacific Software's file manager and
archive handler, "Professional SERVICES 4.0". He has produced several
shareware products including the "LS-1.00 Plus Scientific Calculator",
initially released with The QBNews volume 1, issue 4. Larry is also
a consulting programmer and an adjunct professor teaching beginning
QuickBASIC and 'C'. He is also a frequent contributor to The QBNews.
He can be contacted in care of this newsletter or in care of The ORCA
Pacific BBS 1(503)888-6050.
======================================================================
The BASIC Code Cache Page 14
Volume 1, Number 1 April 20, 1993
A Quick Method of Changing Colors by Lee Herron
I had to color code about 40 pages of text for an app that needed key
words highlighted, this idea I got from a BBS software. This code
uses high order ASCII codes to add color information to the text you
are printing. ZPRINT.BAS is contained in QBCODE.ZIP. Enjoy.
'*********************************************************************
'* FileName: ZPrint by Lee Herron @ 1:3612/130 fido *
'* Copyright 1993 by Lee Herron *
'* ----------------------------------------------------------------- *
'* Description: For use in coding color into large text displays, *
'* much quicker method of changing colors than using color ?,? *
'* everytime. I hope you find it of some help. Hint: Don't use zPrint*
'* on every line. *
'* Origin: NightFlight BBS Pensacola,FL 16.8HST/DS (1:3612/130) *
'*********************************************************************
DEFINT A-Z
DECLARE SUB zPrint (z$)
' ASCII TABLE:
'128 Ç black '136 ê hblack
'129 ü blue '137 ë hblue
'130 é green '138 è hgreen
'131 â cyan '139 ï hcyan
'132 ä red '140 î hred
'133 à magenta '141 hmagenta
'134 å brown '142 Ä hbrown
'135 ç white '143 Å hwhite
CLS
LOCATE 11, 6
zPrint "éÇThis is a sample string using èÇ<ÄÇZPrintèÇ>éÇ to "
PRINT "Highlight specific words!" ' see Hint !
SUB zPrint (z$) STATIC
y = CSRLIN: x = POS(0): cflag = 0
FOR z = 1 TO LEN(z$)
a = ASC(MID$(z$, z, 1)): m$ = CHR$(a)
IF a = 145 THEN
PRINT
ELSE
IF a < 128 THEN
PRINT m$;
cflag = 0
ELSE
IF cflag THEN
COLOR , a - 128: cflag = 0
'Set background color because previous character was a color.
ELSE
COLOR a - 128: cflag = 1
'Set foreground color because prev. char was a letter.
The BASIC Code Cache Page 15
Volume 1, Number 1 April 20, 1993
END IF
END IF
END IF
NEXT z
END SUB
The BASIC Code Cache Page 16
Volume 1, Number 1 April 20, 1993
----------------------------------------------------------------------
P o w e r B A S I C
----------------------------------------------------------------------
PowerBASIC 3.0 Adds Power to BASIC by Bruce Tonkin
Product Name: PowerBASIC 3.0
Available from: Spectra Publishing (408) 730-9291
1030 D East Duane Ave
Sunnyvale CA 94086
Price: $149 ($59 for Basic Code Cache readers until 4/30/93)
PowerBASIC represents a clearly-defined and demonstrably better
alternative in the BASIC compiler market. It concedes nothing to
other compilers, and extends the range of BASIC in ways that any other
compiler vendors might envy.
The envy ought to extend to the former leader, Microsoft. That
largest of all software businesses started as a vendor of BASIC, and
for years has prided itself on having the best BASIC available. For
all those years, that pride has been justified, but no more; the new
king is a product called PowerBASIC. To see why this is so, we need
to ask just two questions: What do BASIC programmers want? What makes
PowerBASIC so good?
The answer to both questions is the same: power. Microsoft BASIC has
five numeric data types: integer, long integer, currency, and single
and double precision floating-point. PowerBASIC adds single byte
integer, unsigned word and long integer, 8-byte (quad-word) integer,
BCD fixed and BCD floating-point (instead of just one currency type),
and extended-precision real (80-bit real). While BASIC isn't made
good or bad solely because of the number of numeric data types
supported, more types are a definite help -- and these types have been
on most BASIC programmers' wish lists for years.
Further, the BCD types in PowerBASIC are not limited to a single
number of decimal places; the programmer can specify (at run-time) how
many places are desired. Rather than be limited to four decimal
places for all calculations (as with the currency data type),
PowerBASIC allows you to set BCD to two decimal places for exact (to
the penny) calculations with no need for rounding, or to any number of
places from zero to eighteen for any particular need. The result is
decreased overhead in each program (no need for rounding), and
increased flexibility.
PowerBASIC also implements BCD as scaled integers, rather than the
two-digits-per-byte "natural" method. By using scaled integers,
PowerBASIC's approach gives increased speed -- ordinary BCD math has a
deserved reputation for sluggishness.
PowerBASIC also supports flex strings -- strings with a fixed length,
except that the length can be changed under program control -- and
fundamental changes in the way programs can be created.
The BASIC Code Cache Page 17
Volume 1, Number 1 April 20, 1993
Consider the usual way any program is created. There are several
steps; design, code, test, debug, and retest. In practice, the debug
and retest steps are repeated indefinitely. No one seems to want to
mention the reason for the extended debugging and testing phases,
perhaps because the reason is so fundamental: the number of
expressions and branches in the code. In other words, code complexity
leads directly to coding errors.
From that standpoint, it is clear that the fewer expressions and the
fewer branches, the more likely a program will work correctly the
first time, and the easier it will be to enhance. This is the big
reason for the popularity of toolkits, modular programming, and object
orientation. Sure, there are other reasons for each of those things,
but reduced complexity is the biggest and certainly easy to defend.
So, how does this apply to PowerBASIC? Simple: PowerBASIC adds
functions that would ordinarily be found in a toolkit or written as
needed. PowerBASIC adds types to make it easier and more natural to
express more programming requirements in fewer statements, fewer
lines, and fewer branches. The more power in the language, the better
for the programmer -- and the person using the applications.
Reliability helps everyone!
Some specific examples are in order. Let's start with some of the
array commands and functions.
The Array Sort command allows the programmer to sort any type of array
in ascending or descending order, in whole or in part. The sort can
simultaneously rearrange the elements in another array (a tag array).
It's not so much that the sort command is useful; it's certainly that.
It's even more important that it exists; it's such a generally useful
command that it's hard to imagine going without it once you've used
it. Even a low-level language like C includes a sort command in its
standard library. No Microsoft BASIC has a sort command, however.
Equivalently useful are commands like Array Scan (search any array for
any value), Array Insert (insert a new element in an array), and Array
Delete. Without PowerBASIC, the programmer either must write an
equivalent for each, or buy one. It's not as if these commands are
terribly complex, so there's not a lot of work involved -- but there
is some work, and it is repeated in every program written, and those
programs are thus larger and more likely to contain errors, and slower
than the PowerBASIC equivalent besides.
In every part of PowerBASIC, the same pattern is repeated. Even
mundane matters like compiling are addressed; PowerBASIC supports
conditional compilation (making it easier to maintain separate
versions of the same program in one source module). PowerBASIC also
supports metacommands that eliminates both long and complex command
lines and lengthy and error-prone selections from cascading dialog
boxes. To force the compiler to create a program that will require an
80386 processor, you would insert the metacommand $cpu 80386 at the
start of the program, for example. All executables can be compiled to
stand-alone files that need nothing more than MSDOS to run. The other
The BASIC Code Cache Page 18
Volume 1, Number 1 April 20, 1993
metacommands are also named in easily-remembered ways, making Power
BASIC programs almost entirely self-documenting (at least potentially)!
Other notable advances include an in-line assembler with a full
knowledge of the environment (use MOV AX,X% to move the value of the
BASIC variable X% to the AX register), TSR creation, string memory (in
any single array or any number of arrays) up to the full amount of
available memory -- not just 64K -- and things like block memory
moves, MAX and MIN functions, bit functions (test, set, shift, and
rotate), pointers, library support for arbitrary OBJ files, and minor
niceties almost everywhere you look.
Consider the INSTR function. In any standard BASIC, INSTR returns the
first matching position of one string within another; INSTR("ABC","C")
will return the result 3. PowerBASIC adds the ANY keyword for even
more power, so the function INSTR("GFDSALK",ANY "QWERTAS") will return
the result 4 (S is found before A). The ANY keyword can also be used
in commands like LTRIM, RTRIM, REPLACE, and others. Communications
capability extends to 115K baud and to other COM ports (to COM4, even
all at once). There is a MAP command which allows you to subdivide a
flex string into separate parts, similar to a FIELD statement; in
effect, you can define a type at run-time! All this isn't just "neat"
-- it's good, solid, and genuinely useful stuff.
If you'd like to write TSR programs, PowerBASIC makes it easy; a
complete TSR can take as little as five lines, and you don't need a
third-party library -- the capability is built into PowerBASIC.
In sum, PowerBASIC was designed from the ground up with a very
different agenda than the other BASIC products. PowerBASIC was
written in response to BASIC users; nearly every feature that appeared
on very many wish lists was included in PowerBASIC. Some products
have hidden agendas -- use them and you're buying a vision of the
future of programming or computing that may or may not match what
you'd like. PowerBASIC is pragmatic; it's what you always wanted in a
computer language, and may be far more than you ever expected to get.
That strikes me as especially significant. After all, don't you want
to encourage a company that actually listens to its customers?
I do. I'm now moving all my BASIC software from the former leader to
the new one, and that's not a step I take lightly. After all, I've
been using products from the other vendor since 1976, and there's a
lot to move. It's nice to discover that my EXE files are smaller and
run faster, besides my source files being an average of 25% smaller!
Bruce W. Tonkin
President - T.N.T. Software
34069 Hainesville Road
Round Lake, IL 60073
EDITOR'S NOTE
Disk subscribers to The BASIC Code Cache will find TRYPB3.ZIP on disk
number 2. This is a fully functional demonastration version of
PowerBASIC 3.0.
The BASIC Code Cache Page 19
Volume 1, Number 1 April 20, 1993
Getting Netware Connection Information by Lewis Balentine
This code provides three routines that return information on a work
station's netware connections. It is an excellent example of what you
can do with some of PowerBASIC's more advanced features. NET001.BAS
can be found in the file PBCODE.ZIP.
The functions are:
DECLARE FUNCTION GetConnectNumber% ()
DECLARE FUNCTION GetConnectInfo% (Con%, Reply AS ConInfoRep)
DECLARE FUNCTION GetNode$ ()
GetConnectNumber% returns the Netware Logical Connection number. This
will tell you if you are logged into a server. GetConnectInfo%
returns information about your connection and GetNode$ returns the
6 byte Physical Node Address.
GetNode$ takes advantage of PowerBASIC's in-line assember. It also
calls the internal PowerBASIC routine GetStrLoc to return address
information on a PB string. Be sure to check out the support routines
that these functions call for more interesting code.
' This FILE: "NET001.BAS" is placed in the public domain by the author
' Lewis Balentine, 19 March 1993. The author specificly denies all
'warranties, exspressed or implied, of fittness or function.
' ->>>> use or abuse as you see fit <<<<-
'---------------------------------------------------------------------
'PGN PAGE: XXX in this program remarks refers to a page in the book:
'"Programers Guide to Netware", Charles G. Rose, (c) 1990 McGraw Hill.
'=====================================================================
$compile exe
$debug map on
$dim array
DECLARE FUNCTION GetStrLoc&(byval AllocHandle%)' so ASM CALL can find it
'---------------------------------------------------------------------
' Constants for use with PB3's REGS function/statement.
%FLAGS = 0
%AX = 1
%BX = 2
%CX = 3
%DX = 4
%SI = 5
%DI = 6
%BP = 7
%DS = 8
%ES = 9
'---------------------------------------------------------------------
type ConInfoReq ' Connection Information Request Buffer
length as word ' (LoHi) Request buffer length (4 - 2 = 2)
The BASIC Code Cache Page 20
Volume 1, Number 1 April 20, 1993
cll as byte ' Set to &H16
con as byte ' logical connection number
end type
type ConInfoRep ' Connection Information Reply Buffer
length as word ' (LoHi) Reply buffer length (64 - 2 = 62)
id as Dword ' (HiLo) Object ID
tipe as word ' (HiLo) Object Type
nam as string * 48 ' Object Name
Yr as Byte ' Log in Year where 0 = 1980
Mo as Byte ' Month (1 to 12)
Dy as Byte ' Day (1 to 31)
Hr as Byte ' Hour (0 to 23)
Mn as Byte ' Minute (0 to 59)
Sc as Byte ' Second (0 to 59)
Wk as Byte ' WeekDay where 0=Sunday
Extra as Byte ' Undefined extra byte
end type
'---------------------------------------------------------------------
' the FourByte, TwoByte types and HiLoWord, HiLoDWord are used in the
' functions SwapBytesW and SwapBytesDW.
Type FourByte
B1 as Byte
B2 as Byte
B3 as Byte
B4 as Byte
end Type
Type TwoByte
B1 as Byte
B2 as Byte
end Type
Union HiLoWord
Wrd as Word
BB as TwoByte
end Union
Union HiLoDWord
DW as Dword
BB as FourByte
end Union
'=====================================================================
' This is the "MAIN" code of the prgram. It prints Netware Connection
' Information about the workstation and logged on user.
'=====================================================================
start:
dim info as ConInfoRep
connect%=GetConnectNumber%
print
The BASIC Code Cache Page 21
Volume 1, Number 1 April 20, 1993
if connect%=0 then
print "This workstation is not logged in to the network."
end
end if
errr%=GetConnectInfo%(connect%,info)
if errr%<>0 then
print "Get Netware Connection Information failed."
end
else
Print "Logical Connection number is (DEC): ";connect%
Print " LogIn Name is: "; trim$(info.nam)
print " Netware Object ID is (HEX): "; DWHex$(SwapBytesDW???(info.id))
print " Netware Object Type is (HEX): ";Right$(DWHex$(SwapBytesW??(info.tipe)),4)
print " WorkStation Node is (HEX): "; getnode$
end if
end
'=============================================================================
' Below are the function used by the program.
'=============================================================================
' function to return Netware Logical Connection number
function GetConnectNumber% ' PGN PAGE: 274
' if 0 then not logged in
local c%
!xor AX,AX ' zero AX
!mov AH,&Hdc ' Netware API call for Connect #
!int &H21 ' Call Netware (returns AX=&Hdc32)
!xor AH,AH ' Mask off the high byte
!mov c%,AX ' AL contains connection number
GetConnectNumber%= c%
end function
'----------------------------------------------------------------------------
' function to return info about the Netware object at a specific connection #
' PGN PAGE: 272
function GetConnectInfo% (con%, reply as coninforep) Local
dim request as ConInfoReq
request.length=2
request.cll=&H16
request.con=con%
reply.length=62
reg %ES, varseg(reply)
reg %DI, varptr(reply)
reg %DS, varseg(request)
reg %SI, varptr(request)
reg %AX, &HE300
call interrupt &H21
reply.Extra = Reg(%AX) and &H00FF ' 0 indicates succesful call
GetConnectInfo%=reply.extra
end function
'----------------------------------------------------------------------------
' funtion to return 6 byte Physical Node Address of the requesting workstation
' PowerBasic does not have a 6 byte integer type so we are going to return
The BASIC Code Cache Page 22
Volume 1, Number 1 April 20, 1993
' a 12 byte string representation of the unsigned Hexidecimal number.
' There is probably a better way to do this, but ....
function GetNode$ ' PGN PAGE: 276
temp$="000000000000" ' 6 bytes requires 12 characters
! push DI ; save Destination Index
!
! mov AX,&Hee00
! int &H21 ; Call Netware
!
' ! mov AX,&h4321 ; for test of conversion
' ! mov BX,&h8765 ; for test of conversion
' ! mov CX,&hCBA9 ; for test of conversion
!
! push CX ; Node returned in CX,BX,AX
! push BX
! push AX
' setup our working string
! mov AX,temp$ ' string handle
! push AX ' now for location and length
! call getstrloc ' return: location=DX:AX, length=CX
! dec CX ' reduce count by one
! add AX, CX ' we need the end of the string
! mov ES, DX ' store our address of string
! mov DI, AX ' in ES:DI
! std ; set direction flag for decriment
! mov CX,&h0003
GetNodeLoop:
! pop BX ; get word from stack
! mov AH,4 ; (4 nibbles)
! call WHex ; translate
! loop GetNodeLoop
! pop DI ; restore resisters
! cld
GetNode$=temp$
end function
'----------------------------------------------------------------------------
' functions to swap High and Low bytes of a Word/DWord
' Netware was origionally only available on a Motorola 6800 server. Motorola
' stores numbers with the most significant byte first. Intel stores numbers
' with the least significant byte first. This function is use to translate
' the old style unsigned wird values. Note that it does not change the input.
function SwapBytesW?? (wrd??) Local
dim temp as HiLoWord
temp.Wrd=wrd??
swap temp.bb.b1 , temp.bb.b2
SwapBytesW??=temp.Wrd
end function
'----------------------------------------------------------------------------
function SwapBytesDW??? (DW???) Local
dim temp as HiLoDWord
The BASIC Code Cache Page 23
Volume 1, Number 1 April 20, 1993
temp.DW=DW???
'print hex$(temp.bb.b1), hex$(temp.bb.b4)
swap temp.bb.b1, temp.bb.b4
swap temp.bb.b2, temp.bb.b3
SwapBytesDW???=temp.DW
end function
'----------------------------------------------------------------------------
' function to return the value of the string representation of Hexidecimal
' number. PB's "&Hxxxx" is limited to compile time and/or two bytes.
' NOTE: This function assumes unsigned strings !
' NOTE: This function ignores the invalid characters!
function DWHexVal???( DW as string) Local
dim temp as string
temp=ucase$(DW)
while len(temp)>0
p= instr("0123456789ABCDEF",left$(temp$,1))
if p>0 then
shift left t???,4
t???=t???+(p-1)
end if
' print p,t???,temp
temp=mid$(temp,2)
wend
DWHexVal???=t???
end function
'----------------------------------------------------------------------------
' function to return a 8 character Hexidecimal string representation of
' a four byte DoubleWord variable. (PB's HEX$ function is limited to 2 Bytes)
function DWHex$ (DW as DWord) Local
temp$="87654321" ' allocate 8 byte character string
' PAGE: 333 of PB3 Programers Guide
' " ... save the SI,DI,BP,DS,SS and SP registers."
' of these we only modify the DI register.
! push DI ; save Destination Index
' setup our working string
! mov AX,temp$ ' string handle
! push AX ' now for location and length
! call getstrloc ' return: location=DX:AX, length=CX
! dec CX ' reduce count by one
! add AX, CX ' we need the end of the string
' setup our DoubleWord Variable
! les di,[bp+6] ' get the address from the stack
! mov BX, es:[di] ' store lo word
! inc di ' move index foward
! inc di ' two bytes
! mov CX, es:[di] ' store hi word
! mov ES, DX ' store our address in of string
! mov DI, AX ' in ES:DI
! std ' set the direction flag
! mov AH,4 ' 1 word = 4 nibbles
! call Whex ' now convert
The BASIC Code Cache Page 24
Volume 1, Number 1 April 20, 1993
! mov BX,CX ' get the hi word
! mov AH,4 ' 1 word = 4 nibbles
! call WHex ' convert it
! pop DI ' restore Destination Index
' PAGE: 333 of PB3 Programers Guide
' " ... all these calls also require you clear ... the processor
' direction flag before returning to caller. ..."
! cld
DWHex$=temp$
end function
'----------------------------------------------------------------------------
' This function trims the "white space" from both ends of string.
' It also removes ANY contol characters (0 to 31).
function trim$(t$)
dim temp as string
for i=1 to len(t$)
tt$=mid$(t$,i,1)
if tt$=>" " then temp=temp+tt$
next i
trim$=ltrim$(rtrim$(temp))
end function
'=============================================================================
' below are misc ASSEMBLY soubroutines called from the inline ASM functions
'=============================================================================
' Most, if not all, of this assembly work could have been done in pure Basic.
' The main reason I did it in assembly was to get some idea of what could
' be done (and how) with the INLINE ASM capability of PB3.
' Having gone to the trouble ..... I am going to use it.
'-----------------------------------------------------------------------------
! ; ASM routine converts the WORD in BX to a Hex String representation
! ; on entry
! ; ES:DI = pointer to end of string (works right to left)
! ; BX = value to be converted
! ; AH = number of nibbles (hex digits) to convert (0 to 4)
! ; direction flag = set (decrement)
! ; on exit
! ; BX = undefined (contents rotated, 4 digits->BXin=BXout)
! ; AH = zero
! ; AL = last Hex Character
! ; AX = undefined (AH =0, AL last Hex digit)
! ; ES:DI = byte in front of hex string
! ; The only other register affected is the flag register.
! ; NOTES:
! ; NO ERROR CHECKING, THIS WILL WRITE OVER ANYTHING (or at least try)!!
! ; THIS ROUTINE USES A NEAR RETURN, MUST CALL FROM SAME CODE SEG !!!!
WHex:
! cmp AH,0 ; if 0 then
! jz WHexExit ; out of here
! mov AL, BL ; get value from BX
! and AL, &h0F ; mask off low nibble
! dec AH ; sub one from our counter
! ror BX, 1 ; rotate the next nibble in BX
! ror BX, 1 ; four rotates are used so that
! ror BX, 1 ; this will work on an 8088
The BASIC Code Cache Page 25
Volume 1, Number 1 April 20, 1993
! ror BX, 1 ; rotate is used to preserve BX
! cmp AL, &H0009 ; if greater than 9 then
! jg WHexAlpha ; jump to alpha character
! add AL, &h30 ; else add offset for numeric
! stosb ; store val in AL at ES:DI, DI=DI-1
! jmp WHex ; do it again Sam ...
WHexAlpha:
! add AL, &h37 ; add offset for alpha character
! stosb ; store val in AL at ES:DI, DI=DI-1
! jmp WHex ; do it again Sam ...
WHexExit:
! retn ; back to whince we came (I hope)
'-----------------------------------------------------------------------------
' more to come, maybe ....
' End of File <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
The BASIC Code Cache Page 26
Volume 1, Number 1 April 20, 1993
----------------------------------------------------------------------
V i s u a l B a s i c
----------------------------------------------------------------------
SpyWorks-VB from Desaware
SpyWorks-VB is an advanced programming tool for Microsoft's new Visual
Basic for Windows version 2.0. It consists of a set of custom
controls, dynamic link libraries and applications that not only
significantly extend the power of the Visual Basic language, but
provide valuable debugging tools as well.
VISUAL BASIC ENHANCEMENTS
On the enhancement side, SpyWorks-VB provides three custom controls.
The SBC.VBX, or generic subclass custom control provides you with
complete access to the underlying windows message stream for the
controls and forms in your application. The messages that you specify
will trigger events in the SBC control. Each message can be processed
before or after the default Visual Basic processing for that message,
or the event can be simply posted so that your program will know that
it occurred. The subclass control can even intercept Visual Basic
messages to forms, controls, and VB2 graphical controls.
The CBK.VBX, or generic callback custom control handles those
situations where you are accessing a Windows API function or third
party DLL function that requires a function address as a parameter.
Generic callback provides a pool of "callback" function addresses
which are mapped into VB events.
The SBCKBD.VBX, or keyboard custom control allows you to detect all
keystrokes before they are sent to your application, or to detect all
keystrokes for the entire system.
The DWSPYDLL.DLL dynamic link library not only provides the underlying
code used by the controls described previously, but also provides a
powerful set of functions for accessing hard to get at Windows API
functions (such as those for the printer drivers). It also provides
access to many of the Visual Basic API functions which normally cannot
be accessed directly.
VISUAL BASIC DEBUGGING TOOLS
SpyWorks-VB provides a set of debugging tools that can shave hours off
of the development process.
SpyParam.exe uses the Windows 3.1 built in parameter error detection
capability to detect and trap errors that occur while accessing API
functions - errors that are often hard to find because they are simple
ignored by Windows.
SpyMem.exe is a complete memory/resource/module and task browser. Not
only does it provide detailed information on various Windows objects,
but you can compare the current state of the system to a saved
The BASIC Code Cache Page 27
Volume 1, Number 1 April 20, 1993
reference - ideal for determining if you have forgotten to free memory
or resources before your program terminates.
SpyMsg.exe is a classing "spy" type program with the ability to record
any or all events to one or more Windows in your system. SpyMsg.exe
adds a couple of new twists however, with its ability to detect not
only Windows and Visual Basic messages, but also Visual Basic events.
You can obtain a complete list of every event that occurs during the
course of your program. Naturally, SpyMsg works with VB2 graphical
controls as well.
SpyWin.exe is a system window browser that lets you examine
information about any or all windows, forms and controls in your
system. This application is provided with complete source code so that
you can learn some of the techniques used.
SpyWorks-VB comes with on-line help, various sample programs, and a 30
day money back guarantee (when you order directly from Desaware - your
dealer may have other policies). All this for only $129.
SpyWorks-VB is a powerful and flexible tool - but in order to use it
effectively you will need to have access to information about the
Windows API such as that included in the Widows Software Development
Kit, The Windows Programmer's Reference from Microsoft, or the Visual
Basic Programmer's Guide to the Windows API from Ziff-Davis press
(written by the author of SpyWorks-VB).
SpyWorks-VB requires Windows 3.1 and Visual Basic 2.0. The runtime
custom controls may be distributed with your compiled application
without royalties. SBC.VBX and CBK.VBX are Windows 3.0 compatible as
well at runtime.
Disk subscribers to The BASIC Code Cache can find a demo of
SpyWorks-VB in the file SPYWRK.ZIP. You can order SpyWorks-VB by
calling Desaware at (408) 377-4770.
The BASIC Code Cache Page 28
Volume 1, Number 1 April 20, 1993
Tom Hanlin Checks in with PBClone for VB
Tom Hanlin, shareware author extraordinaire, checks into the Visual
Basic arena with his first offering, PBClone Windows. PBC is a
collection of 79 routines callable from Visual Basic, or any language
that can call DLLs.
PBC provides a number of low level routines that were left out of
Visual Basic, or done more efficiently. Included are routines that
interrogate the hardware, calculate checksums and crcs, and perform
array operations. Also, routines to PEEK and POKE memory are included.
Some of the routines included are:
Hardware:
Comports Returns the number of com ports installed
GetComAddr Returns address of a comport
GetPrtAddr Returns address of a printer port
GetTick Returns the current system timer tick
PeekI and PeekL Peeks that return an integer or long respectively
PokeI and PokeL Pokes that set an integer or long respectively
Misc:
DateSq and DateUnsq Date compression routines
TimeSq and TimeUnsq Time compression routines
IsAlNum Checks to see if a character is alphanumeric
IsControl Checks to see if a character is a control code
Matrix:
PtrMatD Initializes a double precision array with
increasing values
SetMatC Initializes a currency array to a set value
If you have yet to try accessing a third party DLL from your Visual
Basic programs, this is a good way to learn. If you have the disk
version of The BASIC Code Cache, you can find the full version of
PBCWIN in the file PBCWIN10.ZIP. If you have the electronic version,
you can find this library as well as all of Tom's other libraries on
the following BBSes:
219-696-3415 IN ToolKit (Ken Prevo's) PC-Board net: SourceNet
A programmer and power user board.
1 line, 1.2G disk space, HST Dual Standard modem.
Use the name HANLIN SOFTWARE and password SHAREWARE for access.
Voted 23rd Best BBS in the 1991 Best BBS contest.
301-596-7692 MD Programmer's Corner custom BBS also 301-995-6873
All languages-- your one-stop programming shop.
11 lines, 1.5G disk space, v.32 9600 bps modems (some lines are
2400).
201-334-2555 NJ Hacker Central PC-Board net: ILink
Software and hardware support for serious computer folks. ILink,
Intelec.
The BASIC Code Cache Page 29
Volume 1, Number 1 April 20, 1993
2 lines, 930M disk space, 2400 public / 9600 private (HST Dual).
718-837-3236 NY The Consultant BBS PC-Board v14.5a
Programming, Consulting, Tech, Medical, Politics, and more. ASP
hub.
2 lines, 850M, USR HST on Node 1 (listed above), USR DS (v32bis)
on Node 2.
Member of Association of Shareware Professionals. "NYC's best
FREE BBS".
The BASIC Code Cache Page 30
Volume 1, Number 1 April 20, 1993
----------------------------------------------------------------------
A c c e s s B a s i c
----------------------------------------------------------------------
Creating Custom Wizards with Access Basic
The following three articles are part of the Microsoft Knowledge Base
that can be found on Compuserve. They detail undocumented functions
that are necessary to create Access Wizards. For those not familiar
with Wizards, they are programs that automate the creation of new
forms or reports. You can access the Microsoft Knowledge Base by
typing GO MSDS at any ! prompt. This issue also contains a custom
wizard from Microsoft that helps in creating menus for an Access
application. In future issues of The BASIC Code Cache we will look
into creating our own Wizards.
Using Undocumented CreateForm(),CreateReport() Functions [B_WACCESS]
ID: Q93096 CREATED: 23-NOV-1992 MODIFIED: 15-DEC-1992
Summary:
This article discusses the Microsoft Access undocumented functions
CreateForm() and CreateReport(). If you intend to write your own
FormWizard or ReportWizard, you can use these functions to create and
customize a blank form or report to which you can add controls.
This article assumes that you are familiar with Access Basic and
designing forms and reports.
More Information:
CreateForm()/CreateReport() is roughly equivalent to choosing "New...
Form/New... Report" from the File menu at the top of the screen. When
the function is executed, a new empty form/report appears in an
iconized state.
Both functions return an object value that you can use for further
manipulation, and neither function requires parameters.
To use these functions, first define a form or report object
variable, then assign the variable to the function name. An example of
how to do this is:
Dim MyForm As Form
Set MyForm = CreateForm()
After opening the form/report in design mode after executing the
commands above, you can bind the form to a table or query by modifying
the form's/report's RecordSource property:
MyForm.RecordSource = "Categories"
With the form/report in design mode, you can access and
change any of the other design-time properties of the form/report. You
The BASIC Code Cache Page 31
Volume 1, Number 1 April 20, 1993
can also access and change properties of each of the form's/report's
sections via the Section property. The Section property is actually an
array with each array value being a reference to a form's/report's
section. For forms, the Section property array is broken down as:
Section(0) - Detail Section
Section(1) - Form Header
Section(2) - Form Footer
Section(3) - Page Header
Section(4) - Page Footer
For reports, the section property array is broken down as:
Section(0) - Detail Section
Section(1) - Report Header
Section(2) - Report Footer
Section(3) - Page Header
Section(4) - Page Footer
Section(5) - Group Level 1 Header
Section(6) - Group Level 1 Footer
Section(7) - Group Level 2 Header
...etc.
With this information, you can customize the design of a form/report
section programmatically. The following example creates a new report,
hides the page footer by setting its Visible property to false, sets
the Height property of the detail section, and enables its
KeepTogether property:
Dim MyReport As Report
Set MyReport = CreateReport()
MyReport.Section(4).Visible = False
MyReport.Section(0).Height = 1760
MyReport.Section(0).KeepTogether = True
For more information on Microsoft Access libraries, query on the
following words in the Microsoft Knowledge Base:
"libraries AND debugging AND creating"
For more information about other ReportWizard and FormWizard
functions, query on the following words in the Microsoft Knowledge
Base:
"CreateControl AND CreateReportControl AND CreateForm AND
CreateReport AND 'Section Property' AND DeleteControl AND
DeleteReportControl AND CreateGroupLevel"
Wizards Part II - CreateControl(), CreateReportControl()
ID: Q93095 CREATED: 23-NOV-1992 MODIFIED: 23-NOV-1992
Summary:
The BASIC Code Cache Page 32
Volume 1, Number 1 April 20, 1993
This article discusses the Microsoft Access undocumented functions
CreateControl() and CreateReportControl(). If you intend to write
your own Forms and/or Reports Wizard, you can use these functions to
create controls on a form or report that is available in Design mode.
For more understanding of Microsoft Access Libraries, search
on the Knowledge Base for the following keywords:
libraries AND debugging AND creating
For more information about other Wizard functions, query the Knowledge
Base for:
CreateControl AND CreateReportControl AND CreateForm AND CreateReport
AND 'Section Property' AND DeleteControl AND DeleteReportControl
AND CreateGroupLevel
This article also assumes you are familiar with Access Basic and with
designing forms and reports.
More Information:
CreateControl() and CreateReportControl() can be used to create new
controls for a form/report opened in Design mode. These functions
require the name of the form/report represented as a string value,
and a numeric code that represents the type of control. The list of
control types and their associated numeric codes are shown below:
Label .......... 100
Box ............ 101
Line ........... 102
Picture ........ 103
Button ......... 104
Radio Button ... 105
Check Box ...... 106
Ole Object ..... 108
Text Box ....... 109
The CreateControl() and CreateReportControl() functions return a
control object value, so you must first define a control variable,
then Set the variable to the function name. Note the code example
below which creates a form, then adds a button to the form:
Dim MyForm As Form, MyControl As Control
Set MyForm = CreateForm()
Set MyControl = CreateControl(MyForm.FormName, 104)
After executing the code example above, you can modify the properies
of the new control via the control variable you defined. For example:
MyControl.Width = 2000
MyControl.Caption = "&Sum All Records"
For controls that are frequently associated with fields in a table or
The BASIC Code Cache Page 33
Volume 1, Number 1 April 20, 1993
query, you can modify the ControlSource property to bind the control.
Some controls are created with Height and Width properties set to 0,
so that they are - in effect - not visible. In addition, controls
appear at the uppermost left-hand corner of the form by default.
Because of this, you should make it a habit to adjust the size and
position of the control immediately after creating it. The example
below shows how to create, size, move, and bind a text box.
Set MyControl = CreateControl(MyForm.FormName, 109)
MyControl.Width = 1500
MyControl.Height = 200
MyControl.Top = 440
MyControl.Left = 200
MyControl.ControlSource = "[Category ID]"
In addition to specifying the form name and type of control, you can
optionally specify the Section in which the control should be created.
Note the syntactical structure of CreateControl(), (which is identical
to the syntactical structure of CreateReportControl()):
Function CreateControl (FormName As String,
ControlType As Integer
[,SectionNumber As Integer])
Use the SectionNumber parameter to indicate what section the control
is to be placed into. The section that is associated with the
SectionNumber is the same as the index representing a section in a
Section property array.
Wizards Part III - DeleteControl, DeleteReportControl
ID: Q93094 CREATED: 23-NOV-1992 MODIFIED: 23-NOV-1992
Summary:
This article discusses the Microsoft Access undocumented commands
DeleteControl, DeleteReportControl, and the undocumented function
CreateGroupLevel(). If you intend to write your own Forms and/or
Reports Wizard, you can use these functions to delete controls on a
form or report that is available in Design mode, and create groups on
a Report that is available in Design mode.
For more understanding of Microsoft Access Libraries, search
on the Knowledge Base for the following keywords:
libraries AND debugging AND creating
For more information about other Wizard functions, query the Knowledge
Base for:
CreateControl AND CreateReportControl AND CreateForm AND CreateReport
AND 'Section Property' AND DeleteControl AND DeleteReportControl
AND CreateGroupLevel
The BASIC Code Cache Page 34
Volume 1, Number 1 April 20, 1993
More Information:
DeleteControl and DeleteReportControl can be used to delete a control
that exists on a form/report in design mode. Both of these functions
require a string value that represents the name of the form and a
string value that represents the name of the control.
DeleteControl and DeleteReportControl are commands - not functions.
They do not return a value.
The example below creates a form, then creates a button on the form,
displays a message, then deletes the button on the form:
Dim MyForm As Form, MyControl As Control
Set MyForm = CreateForm()
Set MyControl = CreateControl(MyForm.FormName, 104)
MsgBox "About to Delete " & MyControl.ControlName
DeleteControl MyForm.FormName, MyControl.ControlName
When you delete a control using DeleteControl or DeleteReportControl,
there is no visual feedback that the control is gone if the
form/report is not iconized. The control still appears as if it were
not deleted even though you could not click it into focus. To make
the deleted control disappear in such a case, you would have to
repaint the form design window by minimizing and restoring it.
If you have a report available in design mode, you can create groups
for it programmatically by using the CreateGroupLevel() function. The
syntactical structure of CreateGroupLevel() is shown below:
Function CreateGroupLevel(ReportName$,Expression$,
HasHeader$, HasFooter$)
ReportName is a string expression that indicates the name of the
Report.
Expression is a string expression that represents the field name or
calculated field on which the group will be based.
HasHeader and HasFooter indicate whether the group includes a group
header or group footer, respectively.
When CreateGroupLevel() is executed, it adds a group at the innermost
level. For example, if one group already exists, CreateGroupLevel()
will create a second group within the first group. CreateGroupLevel()
returns a number indicating which level it created, beginning with
zero. In the example just mentioned, CreateGroupLevel() would return
1 since it is the second group, and the first group was Group 0.
The preceeding articles are Copyright 1992 by Microsoft Corp.
The BASIC Code Cache Page 35
Volume 1, Number 1 April 20, 1993
----------------------------------------------------------------------
T h e B u g L i s t
----------------------------------------------------------------------
Buglist for Microsoft Visual Basic
Believe it or not, all software has bugs. Some are called features.
Whatever they are called, I'd like to inform you of them. In every
issue of The BASIC Code Cache, The Bug List will detail some of the
bugs and quirks in our commercial development tools. This issue
details articles on Visual Basic for Windows and Visual Basic for
MSDos bugs that are available in the Microsoft Knowledge Base. These
can be found on Compuserve by typing GO MKB at any ! prompt.
Visual Basic for Windows
------------------------
Q94166: GP/UAE When Closing a DDE Application trom the Task List
Q94167: GP/UAE with Stop Statement in Event Procedure and Deleted Sub
Q94216: GP/UAE After Undoing Edit of an Option Explicit Statement
Q94217: GP/UAE When Assign NULL to VBM_GETPROPERTY of Data Type HLSTD
Q94242: Using Graphics Method on Database Objects May Cause GP/UAE
Q94244: GP/UAE When Large Tag with MultiSelect of 30 or More Controls
Q94290: Setting Add Watch May Cause Your Program to Reset
Q94292: Setting Add Watch May Cause GP Fault or UAE
Q94293: Painting Problems When FontItalic Set to True for Text Box
Q94296: Grid Control Painted Incorrectly When PGUP or PGDN Pressed
Q94351: GP/UAE When New Project Loaded After Large Previous Project
Q94698: No Out of Memory Error Generated with Text Box > 32K
Q94773: Attempting to Refresh Null TableDef Field Causes GP Fault
Q94774: GP Fault When Using 8514 Driver with Long String in Text Box
Q94892: Making .EXE File May Cause GP Fault If Forms Saved as Binary
Q94939: Bad .MAK File Prevents Display of Make EXE File Dialog
Q95197: Stack Fault When 2-Item Combo Box Has Small Width Set in Move
Q95285: Large Sub, Function, or Module Can Cause GP Fault or UAE
Q95290: GP/UAE When Create or Use a Huge Array with Large Elements
Q95428: Error Message: Timeout While Waiting for DDE Response
Q95429: FixedCols Can Cause Paint Problem With Grid Control
Q95430: GP/UAE When Multi-Select Controls w/ No Properties in Common
Q95431: Type Mismatch Error When Use VAL Function on Large Hex Value
Q95498: Problems Calling DoEvents from a Scroll Bar Change Event
Q95499: Stack Fault May Occur If Trapping Divide By Zero
Q95500: GP Fault When Close Form That Contains a Single MCI Control
Q95501: MAPI: GP Fault When Attempt to Download 923 or More Messages
Q95508: Extra Characters in Masked Edit Cause Empty InvalidText Box
Q95509: Text Box/Masked Edit in Select Mode if MsgBox in LostFocus
Q95513: Negative ScaleHeight Resizes Control When Form Saved as ASCII
Q95514: Focus Rectangle Remains When Grid Loses Focus
Q95525: GP Fault When Erase User-Defined Array of Variable Strings
Q95590: Loading Project Gives Error: Custom control 'Graph' not found
Q95829: GP Fault When Making EXE If Declare Is Missing Lib & DLL Name
Q95830: Stack Fault When Move Method Makes Combo Box Width Too Small
Q96097: Resizing MDIForm with UI Does Not Update Height and Width
Visual Basic for DOS
The BASIC Code Cache Page 36
Volume 1, Number 1 April 20, 1993
--------------------
Q96942: ""No currently active form"" with ISAM Statement In Quick
Q80412: ""Internal Error"" for SWAP of TYPE Elements in SUB Using VAL
Q90396: ERDEV$ Returns Incorrect Value Under MS-DOS 5.0
Q90905: Click Still Occurs when Using SetFocus to Prevent Focus Change
Q90961: SETUP.EXE on Bernoulli Hard Disk Is Not Supported
Q92953: Stand-Alone Program Using Overlays and ISAM Hangs on Open
Q93670: Using Run on Program That Has Overlays Causes Incorrect Error
Q93674: Run in Procedure That Dimensions Array Hangs VB Environment
Q94007: Basic Serial Communications Programs May Hang With No Error
Q94160: Text Box Does Not Highlight Text When Word Double-Clicked
Q94260: Pmt# Causes Illegal Function Call Error If No Math Coprocessor
Q94261: Code Remains in VBDOS.EXE After Change in FD.EXE
Q94262: Computer Hangs When Click Form Without Focus Then CTRL+BREAK
Q94289: Mouse Cursor Disappears When Using the CodeView Debugger
Q94663: Blank Screen In Monochrome Mode
Q94664: Chaining with Modal Form or Msg Box in Menu May Hang Computer
Q94692: Assignment in Immediate Window Causes Syntax Check In Editor
Q94693: ALTSETUP.BAT Does Not Create NEW-VARS.BAT
Q94694: C7 Module, Overlay, and Run-time Module Makes Computer Hang
Q94695: Error: Executable code not allowed in module level of a form
Q94730: Corrections for Errors in the Visual Basic for MS-DOS Manuals
Q94777: Arrow Keys Don't Activate Control Box in Form Containing Menus
Q94779: Disk not ready Error Saving File After Change to Invalid Drive
Q94780: ISAMCVT Problems Converting BTRIEVE Files
Q94781: Static Common Array Greater than 65510K May Hang the Computer
Q94782: Clipboard Not Cleared After Restart in Interpreter Environment
Q94786: File Not Found When Directory Name Contains Period
Q94787: Unable to Move or Size Form After Executing Width Statement
Q94788: Changing Visible or Enabled Property Can Cause Extra Change
Q94789: Height/Width Properties of Attached Scroll Bars May Be Wrong
Q94790: Errors When Use CHAIN: Illegal function call or Bad file mode
Q94791: Selecting a Menu Suspends All User Events
Q94792: Making the ENTER Key Act Like a TAB Key in Visual Basic
Q94793: Minimum Width of Text Box is 4 Characters Even Without Border
Q94823: PACKING.TXT for VB for MS-DOS Version 1.0 Professional Edition
Q94824: COMPAT.TXT for Visual Basic for MS-DOS 1.0
Q94831: Undocumented Error: Too many local strings in Sub/Function
Q94832: Value Property May Change When You Click the Scroll Bar Thumb
Q94833: Array Errors When Call Function in Other Module w/ No Declare
Q94834: Scrolling List Box Causes Click Event and Change in Selection
Q94835: No Resize Event When Maximize MDI Child
Q94836: Cannot Distinguish CTRL+DEL and CTRL+BACKSPACE In Key Events
Q94868: Linking with CMNDLG.LIB or SPIN.LIB Can Cause Library Error
Q94897: PACKING.TXT for VB for MS-DOS Version 1.0 Standard Edition
Q94937: Computer Hangs When Load Then Unload Form in Form_Load Event
Q94938: Overflow Error When Use ISAMREPR.EXE to Repair ISAM Database
Q95180: Getting Started with CodeView in Visual Basic for MS-DOS
Q95181: Create Link Overlays Using Parentheses or a .DEF File
Q95291: Obtaining the Module Name and Address of an Error
Q95363: Viewing Basic Variable-Length String Variables in CodeView
Q95432: Cannot Profile Program Compiled in Programming Environment
Q95433: /ES Option Is Not Valid in the Standard Edition
Q95438: OS/2 Version 2.0 System Error SYS3175 from LINK.EXE
The BASIC Code Cache Page 37
Volume 1, Number 1 April 20, 1993
Q95461: Memory Loss with Repeated Display of MsgBox or Modal Form
Q95481: On Gosub or On Goto Causes Error: Variable not declared
Q95863: Wrong Number of Dimensions in Common or Dim Statement
Q95942: HelpSetOptions Sets Visible Property to False on Wrong Button
Q96068: Use && to Display Ampersand Character in MsgBox Text
Q96099: How to Drop Down a Menu on the Startup Form of an Application
Q36576: CTRL+Z Embedded in Source Truncates BC.EXE Compilation
Q40547: BC.EXE Will Not Compile Files Named USER.*
The BASIC Code Cache Page 38