An introduction to Visual Cadd API programming with PB/DLL
By Bob Benson

This the second in a series of articles dedicated to exploring the Visual 
Cadd API in the easiest possible way with PowerBASIC PB/DLL.

What are Equates (Constants) in PB/DLL?

PB/DLL programs process two distinct classes of data: constants and variables.  A variable is allowed to change it's value as a program runs.  A constant's value is fixed at compile time and cannot change during program execution (hence, it remains constant).  PB/DLL supports three types of constants: string literals, numeric literals, and equates.
 
A string literal is simply a group of characters surrounded by double quotes.  For example:

"This is a string"
"Visual Cadd is a great 2D cadd program!"

A numeric literal represent numeric values.  They consist primarily of the digits 0 through 9 and a decimal point.  Negative values need a leading minus sign (-); a plus sign (+) is optional for positive values.

PB/DLL equates allow you to refer to constant values by name.  Be aware that equates have global scope; that is, they are visible throughout your program.  Unlike variables, you can us an equate on the left side of an assignment statement exactly once, and only a literal value (not a variable expression) may be assigned to it.  You can use an expression as long as each part of the expression is a numeric literal.  Numeric equates in PB/DLL are preceded by the percent (%) sign.  For example, the following are all legal equates:

%X = 1
%Y = 1 + 1
%Z = %X + %Y

PB/DLL also allows you to refer to string constants by name.  Like numeric equates, string equates are global in scope and can be seen throughout your program.  String equates are designated by using a leading dollar-sign ($) with the equate name:

$username = "Bob"
$Best2Dcadd = "Visual Cadd"


PB/DLL includes a number of pre-defined string equates that you can use.  These were omitted from the help file, so I will include them here:

Equate       Value          Meaning
---------------------------------------------------------
$NUL         CHR$(0)        NUL
$BEL         CHR$(7)        Bell
$BS          CHR$(8)        Back Space
$TAB         CHR$(9)        Horizontal Tab
$LF          CHR$(10)       Line Feed
$VT          CHR$(11)       Vertical Tab
$FF          CHR$(12)       Form Feed
$CR          CHR$(13)       Carriage Return
$CRLF        CHR$(13,10)    Carriage Return & Line Feed
$EOF         CHR$(26)       End of File
$ESC         CHR$(27)       Escape
$DQ          CHR$(34)       Quotation mark

You can use equates to reduce the incidence of "magic numbers" in your programs.  Magic numbers are mysterious values that mean something to you when you first write a program, but not when you come back to it six months later.  One of the best advantages of using equates, is that these definitions are made in only one place, rather than spread out throughout our program code.  Part of the Visual Cadd API documentation includes the definitions of a large number of equates (constants) that we can use throughout our programs.  For example:
 
%UNKNOWN            = 0
%LINE2D             = 1
%ARC2D              = 2
%CIRCLE2D           = 3
%ELLIPSE2D          = 4
%BEZIER2D           = 5
%Point2D            = 6
%SYMBOL2D           = 7
%TEXT2D             = 8
%DIMLINEAR2D        = 9
%DIMANGULAR2D       = 10
%DIMRADIAL2D        = 11
%DIMDIAMETER2D      = 12
%FILL2D             = 13

shows the "magic numbers" used for some of entity types in a Visual Cadd drawing.  Instead of having to remember that a circle is a type 3 entity, we can use the equate %CIRCLE2D  anywhere in our program to represent the desired entity code.  These equates (predefined constants) and many more can be found in the VCType32.inc file, which is part of the PB/DLL version of the Visual Cadd 3.0.1 API documentation.

What Variable Types are supported by PB/DLL?

Variables represent numeric or string values.  Unlike constants, the value of a variable can change during program execution.  Variable names must begin with a letter and can contain any number of letters, digits and underscores.  In PB/DLL, case is not significant:  ENTITYTYPE, entitytype and EntityType are all the same.  This is typical of the Basic language, but is in direct contrast to C/C++ where variable names are case sensitive and three separate variables would be recognized in this case.

PB/DLL supports fifteen variable types: dynamic string, fixed-length string, ASCIIZ string, integer, long integer, quad integer, byte, word, double word, single-, double-, and extended-precision floating point, two currency types, and pointers.

Visual Basic supports the Variant data type, which PB/DLL does not.  PB/DLL supports the following data types, which Visual Basic does not: word, double word, quad integer, extended currency, ASCIIZ string, unions and pointers.  The advantage of PB/DLL here is the support of word, double word and ASCIIZ strings.  These data types are comparable to those of C/C++ and are used by the Windows API as well as the Visual Cadd API.  ASCIIZ strings (null terminated) can be used directly in our programs without any extra code or conversions to worry about.

Variable Naming Conventions and Type Identifiers in PB/DLL

PB/DLL allows an optional type identifier to be appended to the variable name.  Although a variable name can be predefined as a specific data type, it is often more beneficial to use a naming convention that indicates the specific data type as well.  In PB/DLL the following indicators can be used: string ($), integer (%), long integer (&), quad integer (&&), byte (?), word (??), double word (???), single precision (!), double precision (#), extended precision (##), currency (@), and extended currency (@@).

Some programmers prefer to use these identifiers to explicitly "type" their variable names.  An example of this would be the addition of an ampersand (&) to the end of a variable name to indicate that a variable such as Something&  is a PB/DLL long integer.  Other programmers, particularly those who program for 32-bit Windows, often use a convention called "Hungarian notation" where something is added to the beginning of the variable name.  The Hungarian notation version of Something& would be  lSomething, with the lower-case L prefix standing for long.  (Hungarian notations vary.  For example, some use i for integer, others use n.)  Examples of this notation can be found within the Visual Cadd API help files and documentation.  

For maximum readability by both groups of people, others including a company that I have beta-tested graphics add-ons for PB/DLL with, recommend using both prefixes and suffixes, so every variable you see will look like lSomething&.  The following prefixes and suffixes are used in their documentation and code examples:

iSomething%     INTEGER  16-bit signed
lSomething&     LONG     32-bit signed 
qSomething&&    QUAD     64-bit signed
bSomething?     BYTE      8-bit unsigned
wSomething??    WORD     16-bit unsigned
dwSomething???  DWORD    32-bit unsigned
sSomething$     STRING   dynamic string
lpzSomething    ASCIIZ   null-terminated string
spSomething!    SINGLE   precision floating point 32-bit
dpSomething#    DOUBLE   precision floating point 64-bit
epSomething##   EXTended precision floating point 80-bit
  
The best approach is to establish a style that best suits your own purposes, but try to be consistent.  That way, when you come back to your program some time later, your code will remain understandable and problems (bugs) that come up will be easier to find and correct.  The vast majority of API programming errors are mostly related to improper procedure declarations and using incorrect variable types.  This is more important under Windows because all numeric variables (8-bit, 16-bit, 32-bit) are passed as 32 bits, and errors can often be subtle if they show up at all.

The Visual Cadd API procedure declarations include a variable name with an indication of it's type.  This information is used internally to specify the proper types and passing order only.  The variable names used in these declarations have nothing to do with the variable names that you use in your programs. You are free to use any variable names that may be more meaningful for your own purposes.      

One common variable that appears in  most of the Visual Cadd API  routines (both functions and procedures) is the iError value. This value represents the success or failure of the function. Some calls to set properties will only return an iError value since no information is needed on return. An iError value of 0 is true or succeed, while all other values other than 0 is failed or false.  This value can be checked during the development of your application but is often ignored in final versions and many code examples. 




Note that the previous example DLL actually had three procedures exported.  The HideFill and ShowFill procedures turned fill display on or off by passing the appropriate value of 0 or 1, rather than performing a toggle on the current setting.  Equates could be used here to make the source code more meaningful.  Also note that even though we do nothing with the value returned as iError%, we still have to include it as a place-holder when making the calls to the Visual Cadd API. 

%ON  = 1
%OFF = 0

'----------------------------------------------------------
SUB MyHideFill ALIAS "HideFill" () EXPORT
'----------------------------------------------------------
CALL VCSetFillDisplay (iEerror%, %OFF)
END SUB

'----------------------------------------------------------
SUB MyShowFill ALIAS "ShowFill" () EXPORT
'----------------------------------------------------------
CALL VCSetFillDisplay (iError%, %ON)
END SUB

In PB/DLL, as long as the procedure being called is a procedure within your own program or an external procedure (Visual Cadd API) declared with a DECLARE statement, you can omit the CALL keyword.  If you do, you must also omit the parentheses surrounding the parameters to be passed.  For example the following line would be equivalent:

VCSetFillDisplay iError%, %ON

At first glance, this may appear to be a simpler approach, but I would suggest using the previous format.  Functions (procedures that return a value) require that the parameter list be within parentheses as show in this example: 

iFillDisplay% = VCGetFillDisplay (iError%)
IF iFillDisplay% = %OFF THEN
   CALL VCSetFillDisplay (iError%, %ON)
ELSE
   CALL VCSetFillDisplay (iError%, %OFF)
END IF

I prefer to use the CALL keyword, especially as an indicator that an external library procedure is being called.  This also gives a more consistent format to the parameter list,  as it is specified within parentheses in both cases.  It has also been reported that not using parentheses can cause problems when calling the Visual Cadd API from Visual Basic. 

Expanding  our sample DLL to include more Visual Cadd toggles

We can easily add additional Visual Cadd API calls and procedures to access more of the toggles that we may want to use without using the dialog boxes in the Visual Cadd UI.  In this example, we will add ON, OFF, and TOGGLE capabilities for the display of hatches, line widths, line types, standard points, reference points, and construction points.  As you will see, the format is virtually the same as the fill display used in the previous example.  This is typical of many of the calls to the Visual Cadd API.  Once a few basic formats are learned, finding the proper call within the 1350+ available becomes the larger task, especially when dealing with all of the settings that are available within the Visual Cadd environment.

As with the previous example, we will need to define our custom command by adding a line to the cmdext.def file:

GcDH,DH,,,,DllName;Sample02.dll;DllFunName;ToggleHatch;
DllRun;Regen;   

Although shown on two lines here, make sure that you add everything on one single line in the cmdext.def file.  If you want the drawing to redraw after the hatch is toggled, add  Regen; to the end of the line as shown.  If you will be using the new command while another operation is being performed, replace DllRun with DllRunNested.

PB/DLL allows a number of compiler option switches, called metastatements.  A metastatement is preceded by a number sign (# - also known as a pound-sign) to differentiate it from an ordinary statement; it is not placed inside a remark.  There can be only one metastatement per program line, though some metastatements accept several arguments.  I have included several that I generally use with appropriate remarks in the following sample source code.

The #DIM ALL metastatement requires that you declare all variables before they are used in a program.  This option makes PB/DLL behave more like C/C++ which requires that all variables be declared before the can be used.  Although this requires a bit more work, as even simple variables must be listed in a DIM statement, it will protect you from subtle errors like misspelling a variable name.  In this example, we will declare iError% as a global variable to satisfy this requirement.

Note that we have increased the amount of code by seven times, but the size of our DLL has only increased by 44% to 6656 bytes.  This would indicate that the total `overhead' for our PowerBASIC DLL is a little over 4k. 

'============================================================
'  SAMPLE02.BAS by Bob Benson
'  using PowerBASIC DLL Compiler
'===========================================================
#REGISTER NONE ' disables automatic assignment of REGISTER
               ' variables.  You can still use the REGISTER
               ' statement to explicitly define REGISTER
               ' variables in your code.  It has been noted
               ' that using REGISTER variables may not be
               ' compatable with certain C/C++ procedures.

#COMPILE DLL "Sample02.dll" 'specify desired name and path

#DIM ALL       ' Using #DIM ALL requires you to declare all
               ' variables and arrays before they are used
               ' in a program - same as OPTION EXPLICIT

#DEBUG ERROR ON  ' use error checking during development
                 ' turn off when finished, as this will make
                 ' your code smaller and faster.

'--- Declare Visual Cadd API calls here ---------------------
DECLARE FUNCTION VCGetFillDisplay LIB "VCMAIN32.DLL" _
          ALIAS "VCGetFillDisplay" (iError AS INTEGER) _
          AS INTEGER

DECLARE FUNCTION VCGetHatchDisplay LIB "VCMAIN32.DLL" _
          ALIAS "VCGetHatchDisplay" (iError AS INTEGER) _
          AS INTEGER

DECLARE FUNCTION VCGetLineWidthDisplay LIB "VCMAIN32.DLL" _
          ALIAS "VCGetLineWidthDisplay" (iError AS INTEGER) _
          AS INTEGER

DECLARE FUNCTION VCGetLineTypeDisplay LIB "VCMAIN32.DLL" _
          ALIAS "VCGetLineTypeDisplay" (iError AS INTEGER) _
          AS INTEGER

DECLARE FUNCTION VCGetPointDisplay LIB "VCMAIN32.DLL" _
          ALIAS "VCGetPointDisplay" (iError AS INTEGER) _
          AS INTEGER

DECLARE FUNCTION VCGetConstPt LIB "VCMAIN32.DLL" _
          ALIAS "VCGetConstPt" (iError AS INTEGER) _
          AS INTEGER

DECLARE FUNCTION VCGetHandlePt LIB "VCMAIN32.DLL" _
          ALIAS "VCGetHandlePt" (iError AS INTEGER) _
          AS INTEGER

DECLARE SUB VCSetFillDisplay LIB "VCMAIN32.DLL" _
     ALIAS "VCSetFillDisplay" (iError AS INTEGER, _
     BYVAL tf AS INTEGER)
DECLARE SUB VCSetHatchDisplay LIB "VCMAIN32.DLL" _
     ALIAS "VCSetHatchDisplay" (iError AS INTEGER, _
     BYVAL tf AS INTEGER)

DECLARE SUB VCSetLineWidthDisplay LIB "VCMAIN32.DLL" _
     ALIAS "VCSetLineWidthDisplay" (iError AS INTEGER, _
     BYVAL tf AS INTEGER)

DECLARE SUB VCSetLineTypeDisplay LIB "VCMAIN32.DLL" _
     ALIAS "VCSetLineTypeDisplay" (iError AS INTEGER, _
     BYVAL tf AS INTEGER)

DECLARE SUB VCSetPointDisplay LIB "VCMAIN32.DLL" _
     ALIAS "VCSetPointDisplay" (iError AS INTEGER, _
     BYVAL tf AS INTEGER)

DECLARE SUB VCSetConstPt LIB "VCMAIN32.DLL" _
     ALIAS "VCSetConstPt" (iError AS INTEGER, _
     BYVAL tf AS INTEGER)

DECLARE SUB VCSetHandlePt LIB "VCMAIN32.DLL" _
     ALIAS "VCSetHandlePt" (iError AS INTEGER, _
     BYVAL tf AS INTEGER)

'--- Define Equates and Variable usage here -----------------
%ON  = 1
%OFF = 0

GLOBAL iError AS INTEGER
'------------------------------------------------------------
SUB MyHideFill ALIAS "HideFill" () EXPORT
'------------------------------------------------------------
CALL VCSetFillDisplay (iError%, %OFF)
END SUB

'------------------------------------------------------------
SUB MyShowFill ALIAS "ShowFill" () EXPORT
'------------------------------------------------------------
CALL VCSetFillDisplay (iError%, %ON)
END SUB

'------------------------------------------------------------
SUB MyToggleFill ALIAS "ToggleFill" () EXPORT
'------------------------------------------------------------
CALL VCSetFillDisplay (iError%, 1-VCGetFillDisplay(iError%))
END SUB

'------------------------------------------------------------
SUB MyHideHatch ALIAS "HideHatch" () EXPORT
'------------------------------------------------------------
CALL VCSetHatchDisplay (iError%, %OFF)
END SUB


'------------------------------------------------------------
SUB MyShowHatch ALIAS "ShowHatch" () EXPORT
'------------------------------------------------------------
CALL VCSetHatchDisplay (iError%, %ON)
END SUB

'------------------------------------------------------------
SUB MyToggleHatch ALIAS "ToggleHatch" () EXPORT
'------------------------------------------------------------
CALL VCSetHatchDisplay (iError%, _
     1 - VCGetHatchDisplay(iError%))
END SUB

'------------------------------------------------------------
SUB MyHideLineWidth ALIAS "HideLineWidth" () EXPORT
'------------------------------------------------------------
CALL VCSetLineWidthDisplay (iError%, %OFF)
END SUB

'------------------------------------------------------------
SUB MyShowLineWidth ALIAS "ShowLineWidth" () EXPORT
'------------------------------------------------------------
CALL VCSetLineWidthDisplay (iError%, %ON)
END SUB

'------------------------------------------------------------
SUB MyToggleLineWidth ALIAS "ToggleLineWidth" () EXPORT
'------------------------------------------------------------
CALL VCSetLineWidthDisplay (iError%, _
     1 - VCGetLineWidthDisplay(iError%))
END SUB

'------------------------------------------------------------
SUB MyHideLineType ALIAS "HideLineType" () EXPORT
'------------------------------------------------------------
CALL VCSetLineTypeDisplay (iError%, %OFF)
END SUB

'------------------------------------------------------------
SUB MyShowLineType ALIAS "ShowLineType" () EXPORT
'------------------------------------------------------------
CALL VCSetLineTypeDisplay (iError%, %ON)
END SUB

'------------------------------------------------------------
SUB MyToggleLineType ALIAS "ToggleLineType" () EXPORT
'------------------------------------------------------------
CALL VCSetLineTypeDisplay (iError%, _
     1 - VCGetLineTypeDisplay(iError%))
END SUB




'------------------------------------------------------------
SUB MyHideConstPts ALIAS "HideConstPts" () EXPORT
'------------------------------------------------------------
CALL VCSetConstPt (iError%, %OFF)
END SUB

'------------------------------------------------------------
SUB MyShowConstPts ALIAS "ShowConstPts" () EXPORT
'------------------------------------------------------------
CALL VCSetConstPt (iError%, %ON)
END SUB

'------------------------------------------------------------
SUB MyToggleConstPts ALIAS "ToggleConstPts" () EXPORT
'------------------------------------------------------------
CALL VCSetConstPt (iError%, 1 - VCGetConstPt(iError%))
END SUB

'------------------------------------------------------------
SUB MyHideRefPts ALIAS "HideRefPts" () EXPORT
'------------------------------------------------------------
CALL VCSetHandlePt (iError%, %OFF)
END SUB

'------------------------------------------------------------
SUB MyShowRefPts ALIAS "ShowRefPts" () EXPORT
'------------------------------------------------------------
CALL VCSetHandlePt (iError%, %ON)
END SUB

'------------------------------------------------------------
SUB MyToggleRefPts ALIAS "ToggleRefPts" () EXPORT
'------------------------------------------------------------
CALL VCSetHandlePt (iError%, 1 - VCGetHandlePt(iError%))
END SUB

'------------------------------------------------------------
SUB MyHideStdPts ALIAS "HideStdPts" () EXPORT
'------------------------------------------------------------
CALL VCSetPointDisplay (iError%, %OFF)
END SUB

'------------------------------------------------------------
SUB MyShowStdPts ALIAS "ShowStdPts" () EXPORT
'------------------------------------------------------------
CALL VCSetPointDisplay (iError%, %ON)
END SUB

'------------------------------------------------------------
SUB MyToggleStdPts ALIAS "ToggleStdPts" () EXPORT
'------------------------------------------------------------
CALL VCSetPointDisplay (iError%,1-VCGetPointDisplay(iError%))
END SUB

